@chatcode/cco-market 1.45.1

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 (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/README.zh.md +143 -0
  4. package/UPDATE-API-V1.md +132 -0
  5. package/client/client.js +11534 -0
  6. package/cordis.patch.yml +5 -0
  7. package/lib/accelerate.js +184 -0
  8. package/lib/agents.js +36 -0
  9. package/lib/backup.js +572 -0
  10. package/lib/catalog-local-match.js +126 -0
  11. package/lib/catalog-npm.js +111 -0
  12. package/lib/changelog.js +242 -0
  13. package/lib/channels.js +64 -0
  14. package/lib/check.js +1067 -0
  15. package/lib/compatibility.js +198 -0
  16. package/lib/diagnostics.js +49 -0
  17. package/lib/discovery-compatibility.js +260 -0
  18. package/lib/dsh-cli.js +949 -0
  19. package/lib/dsh-install.js +108 -0
  20. package/lib/gist.js +352 -0
  21. package/lib/groups.js +97 -0
  22. package/lib/home-paths.js +42 -0
  23. package/lib/hot.js +519 -0
  24. package/lib/http.js +39 -0
  25. package/lib/index.js +97 -0
  26. package/lib/install.js +387 -0
  27. package/lib/log.js +197 -0
  28. package/lib/ndjson.js +154 -0
  29. package/lib/net.js +98 -0
  30. package/lib/order.js +284 -0
  31. package/lib/patch.js +521 -0
  32. package/lib/pnpm-compat.js +490 -0
  33. package/lib/presets.js +278 -0
  34. package/lib/profile.js +911 -0
  35. package/lib/region-probe.js +93 -0
  36. package/lib/regions.js +253 -0
  37. package/lib/registry.js +204 -0
  38. package/lib/restart.js +373 -0
  39. package/lib/routes.js +4372 -0
  40. package/lib/settings.js +109 -0
  41. package/lib/skill-market/api.js +119 -0
  42. package/lib/skill-market/config.js +29 -0
  43. package/lib/skill-market/installer.js +352 -0
  44. package/lib/skill-market/runtime.js +13 -0
  45. package/lib/skill-market/tui.js +119 -0
  46. package/lib/skill-market/types.js +1 -0
  47. package/lib/skill-market/web-routes.js +123 -0
  48. package/lib/snapshot.js +500 -0
  49. package/lib/source-migration.js +54 -0
  50. package/lib/sources.js +596 -0
  51. package/lib/store.js +91 -0
  52. package/lib/themes.js +102 -0
  53. package/lib/trial.js +116 -0
  54. package/lib/types/accelerate.d.ts +85 -0
  55. package/lib/types/agents.d.ts +22 -0
  56. package/lib/types/backup.d.ts +131 -0
  57. package/lib/types/catalog-local-match.d.ts +50 -0
  58. package/lib/types/catalog-npm.d.ts +47 -0
  59. package/lib/types/changelog.d.ts +102 -0
  60. package/lib/types/channels.d.ts +56 -0
  61. package/lib/types/check.d.ts +266 -0
  62. package/lib/types/compatibility.d.ts +64 -0
  63. package/lib/types/diagnostics.d.ts +29 -0
  64. package/lib/types/discovery-compatibility.d.ts +80 -0
  65. package/lib/types/dsh-cli.d.ts +324 -0
  66. package/lib/types/dsh-install.d.ts +46 -0
  67. package/lib/types/gist.d.ts +53 -0
  68. package/lib/types/groups.d.ts +34 -0
  69. package/lib/types/home-paths.d.ts +16 -0
  70. package/lib/types/hot.d.ts +221 -0
  71. package/lib/types/http.d.ts +12 -0
  72. package/lib/types/index.d.ts +14 -0
  73. package/lib/types/install.d.ts +172 -0
  74. package/lib/types/log.d.ts +43 -0
  75. package/lib/types/ndjson.d.ts +52 -0
  76. package/lib/types/net.d.ts +58 -0
  77. package/lib/types/order.d.ts +100 -0
  78. package/lib/types/patch.d.ts +129 -0
  79. package/lib/types/pnpm-compat.d.ts +93 -0
  80. package/lib/types/presets.d.ts +90 -0
  81. package/lib/types/profile.d.ts +253 -0
  82. package/lib/types/region-probe.d.ts +50 -0
  83. package/lib/types/regions.d.ts +122 -0
  84. package/lib/types/registry.d.ts +86 -0
  85. package/lib/types/restart.d.ts +194 -0
  86. package/lib/types/routes.d.ts +67 -0
  87. package/lib/types/settings.d.ts +78 -0
  88. package/lib/types/skill-market/api.d.ts +10 -0
  89. package/lib/types/skill-market/config.d.ts +3 -0
  90. package/lib/types/skill-market/installer.d.ts +23 -0
  91. package/lib/types/skill-market/runtime.d.ts +10 -0
  92. package/lib/types/skill-market/tui.d.ts +4 -0
  93. package/lib/types/skill-market/types.d.ts +71 -0
  94. package/lib/types/skill-market/web-routes.d.ts +19 -0
  95. package/lib/types/snapshot.d.ts +93 -0
  96. package/lib/types/source-migration.d.ts +11 -0
  97. package/lib/types/sources.d.ts +216 -0
  98. package/lib/types/store.d.ts +30 -0
  99. package/lib/types/themes.d.ts +40 -0
  100. package/lib/types/trial.d.ts +61 -0
  101. package/lib/types/update-api-v1.d.ts +66 -0
  102. package/lib/types/updates.d.ts +123 -0
  103. package/lib/types/verify.d.ts +139 -0
  104. package/lib/update-api-v1.js +215 -0
  105. package/lib/updates.js +361 -0
  106. package/lib/verify.js +453 -0
  107. package/package.json +121 -0
  108. package/src/accelerate.ts +213 -0
  109. package/src/agents.ts +43 -0
  110. package/src/backup.ts +583 -0
  111. package/src/catalog-local-match.ts +144 -0
  112. package/src/catalog-npm.ts +120 -0
  113. package/src/changelog.ts +282 -0
  114. package/src/channels.ts +70 -0
  115. package/src/check.ts +1239 -0
  116. package/src/client/CommentsModal.tsx +119 -0
  117. package/src/client/Diagnostics.tsx +907 -0
  118. package/src/client/ErrorBoundary.tsx +111 -0
  119. package/src/client/InstallToast.tsx +31 -0
  120. package/src/client/Market.module.css +830 -0
  121. package/src/client/MarketSection.tsx +5301 -0
  122. package/src/client/OperationsPanel.tsx +365 -0
  123. package/src/client/SettingsCard.tsx +621 -0
  124. package/src/client/SkillMarket.module.css +21 -0
  125. package/src/client/SkillMarketSection.tsx +162 -0
  126. package/src/client/comments.ts +54 -0
  127. package/src/client/globals.d.ts +13 -0
  128. package/src/client/index.ts +180 -0
  129. package/src/client/locales.ts +1112 -0
  130. package/src/client/market-data.ts +1321 -0
  131. package/src/client/operations.ts +201 -0
  132. package/src/client/preset-panel.tsx +263 -0
  133. package/src/client/primitives.d.ts +140 -0
  134. package/src/client/self-check.ts +147 -0
  135. package/src/client/snapshot-panel.tsx +244 -0
  136. package/src/compatibility.ts +237 -0
  137. package/src/diagnostics.ts +84 -0
  138. package/src/discovery-compatibility.ts +315 -0
  139. package/src/dsh-cli.ts +1126 -0
  140. package/src/dsh-install.ts +118 -0
  141. package/src/gist.ts +362 -0
  142. package/src/groups.ts +111 -0
  143. package/src/home-paths.ts +53 -0
  144. package/src/hot.ts +628 -0
  145. package/src/http.ts +41 -0
  146. package/src/index.ts +128 -0
  147. package/src/install.ts +420 -0
  148. package/src/log.ts +206 -0
  149. package/src/ndjson.ts +185 -0
  150. package/src/net.ts +106 -0
  151. package/src/order.ts +303 -0
  152. package/src/patch.ts +522 -0
  153. package/src/pnpm-compat.ts +527 -0
  154. package/src/presets.ts +344 -0
  155. package/src/profile.ts +940 -0
  156. package/src/region-probe.ts +97 -0
  157. package/src/regions.ts +310 -0
  158. package/src/registry.ts +250 -0
  159. package/src/restart.ts +396 -0
  160. package/src/routes.ts +4506 -0
  161. package/src/settings.ts +141 -0
  162. package/src/skill-market/api.ts +130 -0
  163. package/src/skill-market/config.ts +32 -0
  164. package/src/skill-market/installer.ts +337 -0
  165. package/src/skill-market/runtime.ts +14 -0
  166. package/src/skill-market/tui.ts +157 -0
  167. package/src/skill-market/types.ts +80 -0
  168. package/src/skill-market/web-routes.ts +132 -0
  169. package/src/snapshot.ts +532 -0
  170. package/src/source-migration.ts +61 -0
  171. package/src/sources.ts +565 -0
  172. package/src/store.ts +89 -0
  173. package/src/themes.ts +125 -0
  174. package/src/trial.ts +156 -0
  175. package/src/update-api-v1.ts +277 -0
  176. package/src/updates.ts +400 -0
  177. package/src/verify.ts +492 -0
@@ -0,0 +1,71 @@
1
+ /** Sort orders accepted by the public skill catalog. */
2
+ export type SkillSort = 'latest' | 'downloads' | 'name';
3
+ /** Supported installation scopes. */
4
+ export type SkillInstallLocation = 'project' | 'user';
5
+ /** One skill returned by the public catalog. */
6
+ export interface SkillMarketItem {
7
+ readonly id: number;
8
+ readonly name: string;
9
+ readonly description: string;
10
+ readonly category: string;
11
+ readonly status: string;
12
+ readonly downloads: number;
13
+ readonly views: number;
14
+ readonly readme: string;
15
+ readonly version: string;
16
+ readonly author: string;
17
+ readonly creatorName: string;
18
+ readonly tags: string;
19
+ readonly createTime: string;
20
+ readonly updateTime: string;
21
+ readonly visibility: string;
22
+ }
23
+ /** Catalog list filters. */
24
+ export interface SkillListQuery {
25
+ readonly search?: string;
26
+ readonly category?: string;
27
+ readonly sort?: SkillSort;
28
+ readonly tag?: string;
29
+ readonly page?: number;
30
+ readonly pageSize?: number;
31
+ }
32
+ /** One page returned by the public catalog. */
33
+ export interface SkillListResult {
34
+ readonly items: readonly SkillMarketItem[];
35
+ readonly total: number;
36
+ readonly page: number;
37
+ readonly pageSize: number;
38
+ }
39
+ /** Marketplace ownership metadata stored beside an installed skill. */
40
+ export interface SkillMarketMeta {
41
+ readonly id: number;
42
+ readonly name: string;
43
+ readonly version?: string;
44
+ }
45
+ /** A skill installed in one of the roots visible to the current session. */
46
+ export interface InstalledSkill {
47
+ readonly name: string;
48
+ readonly location: SkillInstallLocation;
49
+ readonly path: string;
50
+ readonly marketId?: number;
51
+ readonly version?: string;
52
+ }
53
+ /** Successful installation result returned to Web and terminal callers. */
54
+ export interface SkillInstallResult {
55
+ readonly skillName: string;
56
+ readonly installPath: string;
57
+ readonly location: SkillInstallLocation;
58
+ readonly updated: boolean;
59
+ }
60
+ /** Resolved runtime configuration for the skill marketplace. */
61
+ export interface ResolvedSkillMarketConfig {
62
+ readonly baseUrl: string;
63
+ readonly requestTimeoutMs: number;
64
+ readonly downloadTimeoutMs: number;
65
+ readonly defaultPageSize: number;
66
+ readonly maxDownloadBytes: number;
67
+ readonly maxArchiveEntries: number;
68
+ readonly maxUncompressedBytes: number;
69
+ }
70
+ /** Operator overrides accepted below the plugin's `skillMarket` key. */
71
+ export type SkillMarketConfig = Partial<ResolvedSkillMarketConfig>;
@@ -0,0 +1,19 @@
1
+ import type { WebServerService } from '../routes.ts';
2
+ import type { SkillMarketRuntime } from './runtime.ts';
3
+ interface SessionLike {
4
+ readonly header: {
5
+ readonly cwd?: string;
6
+ };
7
+ }
8
+ interface SkillMarketWebHost {
9
+ readonly webServer: WebServerService;
10
+ readonly sessions: {
11
+ get(id: string): SessionLike | undefined;
12
+ };
13
+ readonly logger?: {
14
+ warn(message: string): void;
15
+ };
16
+ }
17
+ /** Mount local-session skill catalog and installation routes. */
18
+ export declare function mountSkillMarketRoutes(host: SkillMarketWebHost, runtime: SkillMarketRuntime): () => void;
19
+ export {};
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Profile snapshots — issue #98 (phase 3), implementing the snapshot half of
3
+ * #19: before any ordering / preset change is applied, the profile's
4
+ * composition-critical files are captured as a timestamped snapshot; a failed
5
+ * or unwanted change can be rolled back in one step.
6
+ *
7
+ * A snapshot is a single JSON document under `<profile>/.dsh-market/
8
+ * snapshots/<timestamp>-<seq>.json` describing the files that define the
9
+ * profile's composition: package.json (dependency + bundle list),
10
+ * cordis.patch.yml (the user patch layer) and state.json (market disable
11
+ * list + groups). Version 2 records optional-file absence explicitly;
12
+ * restoring validates every path, reconciles writes and deletions, and rolls
13
+ * completed actions back if a later action fails.
14
+ *
15
+ * Retention: after every create, snapshots are pruned to the most recent
16
+ * `maxSnapshots` (default 20); the cap is configurable through the market
17
+ * plugin config (`maxSnapshots`) and each snapshot can also be deleted
18
+ * individually from the UI.
19
+ */
20
+ declare const SNAPSHOT_FORMAT = "dsh-market/profile-snapshot";
21
+ declare const SNAPSHOT_VERSION = 2;
22
+ interface SnapshotFile {
23
+ path: string;
24
+ /** JSON documents keep their parsed form (re-serialized on restore). */
25
+ json?: unknown;
26
+ /** Line-oriented text files (cordis.patch.yml) keep their lines. */
27
+ lines?: string[];
28
+ /** V2 records absence explicitly so restore can remove a later file. */
29
+ absent?: true;
30
+ }
31
+ export interface ProfileSnapshot {
32
+ /** Present on exact snapshots; omitted by legacy snapshots. */
33
+ format?: typeof SNAPSHOT_FORMAT;
34
+ /** Version 2 records all tracked paths, including explicit absence. */
35
+ version?: typeof SNAPSHOT_VERSION;
36
+ /** Snapshot id: the file basename without the .json suffix. */
37
+ id: string;
38
+ createdAt: number;
39
+ files: SnapshotFile[];
40
+ }
41
+ export type SnapshotCaptureResult = {
42
+ ok: true;
43
+ snapshot: ProfileSnapshot;
44
+ } | {
45
+ ok: false;
46
+ error: string;
47
+ };
48
+ /** Default number of snapshots retained; configurable via `maxSnapshots`. */
49
+ export declare const DEFAULT_MAX_SNAPSHOTS = 20;
50
+ /**
51
+ * Prune the snapshot directory to the `max` most recent snapshots (newest
52
+ * first). Extra ones are deleted oldest-first. A non-positive cap is clamped
53
+ * to 1 — a 0/negative max must never drop the snapshot that was just created
54
+ * nor invert to keeping the OLDEST set (issue #98 review hardening).
55
+ * @returns the ids that were deleted.
56
+ */
57
+ export declare function pruneSnapshots(profileDir: string, max: number): string[];
58
+ /** @internal Deterministic newest-first order for equal-createdAt documents. */
59
+ export declare function compareSnapshotIdsNewest(a: string, b: string): number;
60
+ /**
61
+ * Capture the profile's composition into a version 2 snapshot. Missing
62
+ * optional files are represented explicitly. An existing optional file that
63
+ * cannot be read makes capture fail. Invalid market state is represented as
64
+ * absent because every state reader already observes it as empty and the next
65
+ * state write replaces it. A missing, unreadable, or invalid package.json is
66
+ * not snapshot-able. After creating, the directory is pruned to the most
67
+ * recent `maxSnapshots`.
68
+ */
69
+ export declare function createProfileSnapshot(profileDir: string, maxSnapshots?: number): SnapshotCaptureResult;
70
+ /**
71
+ * All snapshots for a profile, newest first. Files that are unparseable,
72
+ * shape-invalid (bad id/createdAt/files), or whose internal id does not match
73
+ * the file name are skipped — a snapshot that could never be restored is not
74
+ * listed (issue #98 analysis: snapshot JSON validation).
75
+ */
76
+ export declare function listSnapshots(profileDir: string): ProfileSnapshot[];
77
+ /**
78
+ * Restore a snapshot's files into the profile. Version 2 absence markers
79
+ * remove files created after capture; omitted optional files in unversioned
80
+ * legacy snapshots remain untouched. Every path is validated before mutation,
81
+ * each write uses a same-directory temp + rename, and a failure mid-restore
82
+ * rolls completed writes/deletions back to their pre-restore contents.
83
+ * @returns the paths restored; an error result when the snapshot is unsafe,
84
+ * unknown, or a write failed (with any partial writes rolled back).
85
+ */
86
+ export declare function restoreSnapshot(profileDir: string, id: string): {
87
+ ok: boolean;
88
+ restored: string[];
89
+ error?: string;
90
+ };
91
+ /** Delete one snapshot; true only when it existed and was removed. */
92
+ export declare function deleteSnapshot(profileDir: string, id: string): boolean;
93
+ export {};
@@ -0,0 +1,11 @@
1
+ export interface GitToNpmMigration {
2
+ kind: 'git-to-npm';
3
+ /** Normalized owner/repo or owner/repo#path:/subdir identity. */
4
+ repo: string;
5
+ /** Verified npm package declared by the same catalog entry. */
6
+ target: string;
7
+ }
8
+ export declare function findGitToNpmMigration<T extends {
9
+ url: string;
10
+ npm?: unknown;
11
+ }>(plugins: readonly T[], spec: string): GitToNpmMigration | null;
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Registry-source knowledge: how a curated registry entry's URL maps to an
3
+ * installable pnpm target. Pure string logic, no I/O.
4
+ */
5
+ /** Registry tarball names must be plain npm package names, nothing fancier. */
6
+ export declare const NPM_NAME_RE: RegExp;
7
+ /**
8
+ * Parse a registry source url: a github repo, optionally with a
9
+ * `/tree/<branch>/<subpath>` suffix (how the curated list links monorepo
10
+ * subpackages, e.g. dsh-plugins#theme-gallery).
11
+ */
12
+ export declare function parseSourceUrl(url: string): {
13
+ repo: string;
14
+ subpath: string | null;
15
+ } | null;
16
+ /** Parse repository forms accepted by package.json.repository. */
17
+ export declare function parseGitHubRepository(value: string): {
18
+ repo: string;
19
+ } | null;
20
+ /**
21
+ * Parse a Git remote. Unlike package metadata, a local origin may contain a
22
+ * proxy prefix (for example `https://proxy/https://github.com/o/r.git`). In
23
+ * that case only the last GitHub occurrence is considered.
24
+ */
25
+ export declare function parseGitHubRemote(url: string): {
26
+ repo: string;
27
+ } | null;
28
+ /** Normalized repo identity shared by server discovery and client matching. */
29
+ export declare function githubRepoIdentity(url: string, directory?: string | null): string | null;
30
+ /**
31
+ * Repository evidence used for installed-source matching. A monorepo package
32
+ * contributes both its collection root and exact subpath, mirroring the
33
+ * identities extracted from `github:owner/repo#path:/package` specs.
34
+ */
35
+ export declare function githubRepoIdentities(url: string, directory?: string | null): string[];
36
+ /** Weak identity hints from a local Git origin; never used to reject a unique match. */
37
+ export declare function githubRemoteIdentities(url: string, directory?: string | null): string[];
38
+ /** GitHub `owner/repo` for a registry URL, or null when it is not a GitHub repo URL. */
39
+ export declare function repoOf(url: string): string | null;
40
+ /**
41
+ * A commit-pinned codeload tarball URL, optionally behind a prefix proxy.
42
+ *
43
+ * Pinned to a SHA rather than `HEAD` on purpose. pnpm records whatever URL
44
+ * it was given, and the profile's version detection reads the installed
45
+ * commit back out of the lockfile by matching `codeload.github.com/owner/
46
+ * repo/tar.gz/<40 hex>` (src/profile.ts). A `HEAD` URL installs fine and
47
+ * then reports no version forever, which is a worse outcome than being slow.
48
+ *
49
+ * @param repo - `owner/repo`.
50
+ * @param sha - full 40-character commit SHA.
51
+ * @param proxy - prefix proxy, or null to address codeload directly.
52
+ */
53
+ export declare function codeloadTarball(repo: string, sha: string, proxy: string | null): string;
54
+ /**
55
+ * Extract a GitHub repo URL from a URL that may be a Release asset tarball
56
+ * (the format used by the catalog: releases/latest/download/ or
57
+ * releases/download/vX.Y.Z/).
58
+ *
59
+ * THIS IS FOR DISPLAY/LOOKUP PURPOSES ONLY — e.g., finding update notes for a
60
+ * plugin installed via npm. It MUST NOT be used for any decision that affects
61
+ * installation, rollback, duplicate detection, or build-script approval.
62
+ * Those paths use `repoFromTarget` / `repoOfTarget` which are stricter and
63
+ * intentionally do NOT recognize Release asset URLs (because the same asset
64
+ * URL can serve different bytes at different times).
65
+ */
66
+ export declare function lookupRepoFromUrl(url: string): string | null;
67
+ /**
68
+ * Normalized identity of an install target, for comparing two targets that
69
+ * may be spelled differently — lowercased, matching `githubRepoIdentity`.
70
+ *
71
+ * @returns the identity, or null when the spec is not a GitHub source (an
72
+ * npm package name, a `file:` link, anything else).
73
+ */
74
+ export declare function repoOfTarget(spec: string): string | null;
75
+ /**
76
+ * The branch or tag a GitHub spec selects, or null for the default branch.
77
+ *
78
+ * Update detection has to ask about the same ref the install used, or it
79
+ * compares the installed commit against a line the user never chose (#446).
80
+ * A commit pin yields null: the answer for a pinned install is the default
81
+ * branch, which is what "is there something newer" means there.
82
+ */
83
+ export declare function githubRefOfTarget(spec: string): string | null;
84
+ /**
85
+ * An immutable GitHub commit already carried by an install target.
86
+ *
87
+ * Build-script approval on pnpm below 11.21 needs the exact commit-pinned
88
+ * codeload key. Re-resolving HEAD after an install can race a repository push
89
+ * and approve a different URL, so consume an existing pin whenever the spec
90
+ * has one (#285/#385).
91
+ */
92
+ export declare function githubCommitOfTarget(spec: string): string | null;
93
+ /**
94
+ * Pin a GitHub shortcut to one immutable commit without losing its subpath.
95
+ * Revision selectors are replaced; one valid `path:` selector is preserved.
96
+ */
97
+ export declare function githubTargetAtCommit(spec: string, sha: string): string | null;
98
+ /**
99
+ * The allowBuilds key that actually authorizes a git-hosted dependency's
100
+ * build scripts. Verified against pnpm 11.21 (#68 by @yzr278892): for a
101
+ * `github:owner/repo` install, a bare `name: true` entry does NOT match —
102
+ * pnpm's own hint names a commit-pinned codeload URL that changes on every
103
+ * push; the stable form that matches is `name@git+https://github.com/owner/repo.git`.
104
+ *
105
+ * A legacy China-region install may address the SAME repo through a proxied
106
+ * codeload URL, and must authorize under the same key: the plugin a user
107
+ * approved build scripts for does not become a different plugin because its
108
+ * stored source spelling differs.
109
+ *
110
+ * @param name - installed package name.
111
+ * @param spec - the dependency spec from package.json, or the install target.
112
+ * @returns the stable key, or null when the spec is not github-hosted.
113
+ */
114
+ export declare function gitAllowBuildsKey(name: string, spec: string): string | null;
115
+ /**
116
+ * The OTHER allowBuilds key form, for pnpm below 11.21 (#285 by @omdsh-dev,
117
+ * following #267).
118
+ *
119
+ * The stable `name@git+https://…` key above is what pnpm 11.21+ matches, and
120
+ * it is the better key precisely because it does not change when the
121
+ * repository is pushed to. Older pnpm does not match it at all: 11.8.0 — the
122
+ * version DSH Desktop still bundles — matches only the commit-pinned
123
+ * codeload URL it names in its own `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`
124
+ * message. On those versions the "allow build scripts and retry" button
125
+ * could never work, because the key it wrote was one pnpm would never read.
126
+ *
127
+ * Both are written. The pinned form goes stale the moment the repository
128
+ * moves, which is why it cannot REPLACE the stable one — but a stale entry
129
+ * costs a line in a YAML file, and a missing one costs the user the only
130
+ * button that could have unblocked them.
131
+ *
132
+ * @param sha - the commit the install will actually fetch.
133
+ * @returns the key, or null when the spec is not github-hosted.
134
+ */
135
+ export declare function codeloadAllowBuildsKey(name: string, spec: string, sha: string): string | null;
136
+ /**
137
+ * The pnpm install target for a registry entry. Repo-verified npm packages
138
+ * win, followed by author-supplied prebuilt GitHub Release tarballs; both avoid
139
+ * full-repo downloads and local build scripts.
140
+ * @returns the target spec, or null when the source url is unsupported.
141
+ */
142
+ export declare function installTargetFor(entry: {
143
+ url: string;
144
+ npm?: unknown;
145
+ tarball?: unknown;
146
+ }): string | null;
147
+ /** True for profile specs that are a local checkout or tarball, not a registry pin. */
148
+ export declare function isLocalSpec(spec: string): boolean;
149
+ /**
150
+ * True when the install came from a git remote — GitHub shortcuts, codeload
151
+ * tarballs, and any other host (Gitea, GitLab self-host, raw `git+https://…`).
152
+ *
153
+ * Update detection used to ask only `repoOfTarget` (GitHub spellings). A
154
+ * private-host URL then fell through to the npm branch and was looked up by
155
+ * package name; a colliding registry package read as an "update" and
156
+ * `name@latest` replaced the git install (#525).
157
+ */
158
+ export declare function isGitHostedSpec(spec: string): boolean;
159
+ /**
160
+ * Immutable commit already carried by a non-shortcut git URL (`…git#<sha>`).
161
+ * GitHub shortcuts keep using `githubCommitOfTarget`.
162
+ */
163
+ export declare function gitCommitOfTarget(spec: string): string | null;
164
+ /**
165
+ * pnpm add target for updating a non-shortcut git install: drop a full-SHA
166
+ * pin so the remote re-resolves to HEAD, keep any branch/tag fragment.
167
+ * GitHub-hosted `git+https://github.com/…` is rewritten to `github:` — the
168
+ * market's canonical spelling — so a successful update rematerializes the
169
+ * dependency that way and later check/update/rollback can use the first-class
170
+ * GitHub path. This turn still installs through the generic-git target slot
171
+ * (no region acceleration on the rewritten shortcut itself).
172
+ */
173
+ export declare function gitUpdateTarget(spec: string): string | null;
174
+ /**
175
+ * Smart-HTTP info/refs URL for a git-hosted install target, or null when the
176
+ * transport cannot be probed with `fetch` (scp / git:// / ssh://).
177
+ * Userinfo is stripped so update checks do not resend embedded credentials.
178
+ */
179
+ export declare function gitUploadPackUrl(spec: string): string | null;
180
+ export { findCatalogEntryForLocal, resolveCatalogRestore } from './catalog-local-match.ts';
181
+ /**
182
+ * pnpm add target for restoring a local checkout onto a catalog entry.
183
+ * When the catalog only lists the collection root but the checkout declared
184
+ * `repository.directory`, keep that subdirectory — otherwise we install the
185
+ * repo tarball and get the wrong package name (and its build scripts).
186
+ */
187
+ export declare function restoreTargetForLocal(entry: {
188
+ url: string;
189
+ npm?: unknown;
190
+ }, identities?: readonly string[]): string | null;
191
+ /**
192
+ * Dependency names that use pnpm's `workspace:` protocol.
193
+ * Those specs only resolve inside the author's monorepo; a git `#path:`
194
+ * install into a profile cannot see the sibling packages. pnpm installs
195
+ * optional dependencies and auto-installs peers too, so all three maps are
196
+ * scanned; devDependencies are never installed and stay out.
197
+ */
198
+ export declare function workspaceProtocolDeps(manifest: unknown): string[];
199
+ /** Git subdirectory restores cannot satisfy `workspace:` dependencies. npm can. */
200
+ export declare function restoreBlockedByWorkspace(target: string, workspaceDeps: readonly string[]): boolean;
201
+ /**
202
+ * The name an entry is ALREADY installed under, or null — the server-side
203
+ * duplicate guard (#27): the same plugin listed under an alias entry must
204
+ * never install twice (two loader entries with one id brick the next boot).
205
+ *
206
+ * Identity is subpath-aware so monorepo siblings stay independent: an entry
207
+ * with a /tree/ subpath identifies as repo#path:/sub (never the bare repo),
208
+ * while an installed dependency contributes its bare repo AND its #path:
209
+ * form — so a collection root still matches the pieces it was retargeted
210
+ * into, but two different subpackages of one repo never cross-match.
211
+ */
212
+ export declare function findInstalledAlias(entry: {
213
+ name: string;
214
+ npm?: unknown;
215
+ url: string;
216
+ }, installed: Record<string, string>): string | null;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * pnpm store hygiene: reclaim staging directories orphaned by aborted runs.
3
+ *
4
+ * pnpm extracts every fetched tarball under the store's `tmp/` directory as
5
+ * `tmp/_tmp_<pid>_<hex>/`. A run that is killed, cancelled, or timed out —
6
+ * or one that hard-fails mid-fetch — never finishes that staging step, so
7
+ * the directory survives. For `github:` sources the staged payload is the
8
+ * WHOLE repository tarball, so a single aborted install can leave hundreds
9
+ * of megabytes behind (e.g. an OpenViking monorepo install killed at ~88MB).
10
+ *
11
+ * The directory name carries the owning pnpm process id, which makes
12
+ * reclamation safe by construction: when that pid is gone, no live download
13
+ * can be using the directory. Live pnpm tmp dirs are never touched.
14
+ */
15
+ import type { PluginRunner } from './dsh-cli.ts';
16
+ /**
17
+ * Remove every orphaned staging directory under a pnpm store's `tmp/` whose
18
+ * owning pid is no longer alive. Directories that do not match the pnpm
19
+ * staging shape, and any that are locked or in use, are left alone.
20
+ * @param storePath - the pnpm store root (as printed by `pnpm store path`).
21
+ * @returns the removed directory names.
22
+ */
23
+ export declare function cleanOrphanedStoreTmp(storePath: string): string[];
24
+ /**
25
+ * Resolve the active profile's pnpm store root through the same runner the
26
+ * market uses for installs (so both the web and Desktop pnpm paths agree)
27
+ * and reclaim its orphaned staging directories.
28
+ * @returns the removed directory names, empty when the store cannot be resolved.
29
+ */
30
+ export declare function cleanOrphanedStore(run: PluginRunner, profile: string): Promise<string[]>;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Theme lifecycle: classifying installed packages as themes (by the
3
+ * registry's theme category), live-toggling bundle-layer entries through
4
+ * the loader, and keeping exactly one theme active with the choice
5
+ * persisted across restarts.
6
+ */
7
+ /** The slice of a cordis loader entry the market needs for live enable/disable. */
8
+ export interface LoaderEntry {
9
+ options: {
10
+ id?: string;
11
+ name?: string;
12
+ disabled?: boolean | null;
13
+ };
14
+ fiber?: unknown;
15
+ update(options: {
16
+ disabled: boolean | null;
17
+ }, create?: boolean, force?: boolean): Promise<void>;
18
+ }
19
+ /** The host surface the theme manager needs (loader entries + hot-mount context). */
20
+ export interface ThemeHost {
21
+ loader: {
22
+ entries(): Iterable<LoaderEntry>;
23
+ };
24
+ plugin(plugin: unknown, config: unknown): {
25
+ await(): Promise<unknown>;
26
+ dispose(): Promise<unknown> | void;
27
+ };
28
+ }
29
+ /** Manages theme exclusivity for one profile. */
30
+ export interface ThemeManager {
31
+ installedThemeNames(): Promise<Set<string>>;
32
+ setEntryDisabled(name: string, disabledFlag: boolean): Promise<boolean>;
33
+ activateTheme(name: string): Promise<boolean>;
34
+ }
35
+ /**
36
+ * Create the theme manager. `disabledThemes` is the live, shared set of
37
+ * themes the user switched off — the caller owns reading it at boot and
38
+ * replaying it; the manager mutates and persists it on switches.
39
+ */
40
+ export declare function createThemeManager(host: ThemeHost, profile: string, disabledThemes: Set<string>, explicitDir?: string): ThemeManager;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Trial validation for composition changes — issue #98 (phase 3), the
3
+ * "trial boot" half of #19 reduced to what is safe and offline: before any
4
+ * bundle-order or preset change is written to the profile, replay the
5
+ * composition with the CANDIDATE order using the same entry-list machinery
6
+ * the real boot uses (src/check.ts's buildBundleLayers + composeLayers), and
7
+ * refuse the change when the composed tree would fail to boot (duplicate
8
+ * loader entry ids, unresolvable bundle layers, unparseable patches).
9
+ *
10
+ * No process, no network, no writes to the profile: the real profile is only
11
+ * read; if the candidate is bad, the failure is reported and nothing is
12
+ * applied (the caller then skips the write-back entirely).
13
+ *
14
+ * Issue #125 review: the CURRENT composition is replayed alongside the
15
+ * candidate, so the response also carries a current-vs-candidate diff
16
+ * (overrides / orphans / duplicates the reorder introduces) — not just
17
+ * whether it boots.
18
+ *
19
+ * Bundle resolution is deliberately SHARED with the check report
20
+ * (check.ts's buildBundleLayers): the dsh installation anchor first, then
21
+ * Node's module search from the profile (workspace-root hoisting) — so the
22
+ * trial can never disagree with the diagnostics about what a bundle is or
23
+ * where it lives, and official bundles resolve even when they are only
24
+ * hoisted to the workspace root.
25
+ */
26
+ import { type DuplicateId, type OrphanRow, type OverrideRow } from './check.ts';
27
+ export interface TrialIssue {
28
+ layer: string;
29
+ message: string;
30
+ }
31
+ /** Current-vs-candidate composition diff (issue #125 review). */
32
+ export interface TrialDiff {
33
+ /** Override relationships introduced by the candidate (not present in the current composition). */
34
+ overrides: OverrideRow[];
35
+ /** Orphan rows introduced by the candidate. */
36
+ orphans: OrphanRow[];
37
+ /** Duplicate loader entry ids introduced by the candidate. */
38
+ duplicates: DuplicateId[];
39
+ }
40
+ export interface TrialResult {
41
+ ok: boolean;
42
+ errors: TrialIssue[];
43
+ warnings: TrialIssue[];
44
+ duplicates: DuplicateId[];
45
+ /** The composed loader rows under the candidate order. */
46
+ rows: {
47
+ id: string;
48
+ layer: string;
49
+ }[];
50
+ /** What the reorder would change vs the current composition (issue #125 review). */
51
+ diff: TrialDiff;
52
+ }
53
+ /**
54
+ * Replay the profile composition with `newCommunityOrder` (the candidate
55
+ * community-bundle order; official bundles keep their exact positions) and
56
+ * report anything that would break the boot. Pure read.
57
+ */
58
+ export declare function trialValidate(profileDir: string, newCommunityOrder: string[], options?: {
59
+ dshInstallDir?: string | null;
60
+ homeDir?: string;
61
+ }): TrialResult;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Stable, versioned contract for plugin-owned update surfaces.
3
+ *
4
+ * The market UI has richer internal response shapes that evolve with its UI.
5
+ * Third-party plugins must not depend on those shapes, so this module owns the
6
+ * small JSON envelope exposed under `/dsh-market/api/v1`.
7
+ */
8
+ import type { InstallProgress } from './dsh-cli.ts';
9
+ export declare const UPDATE_API_V1_SCHEMA: 'dsh-market/update-api/v1';
10
+ export declare const MAX_UPDATE_OPERATIONS_V1 = 50;
11
+ export type UpdateOperationState = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'rolled-back';
12
+ export interface UpdateFailureV1 {
13
+ code: string;
14
+ message: string;
15
+ retryable: boolean;
16
+ }
17
+ export interface UpdateOperationV1 {
18
+ schema: typeof UPDATE_API_V1_SCHEMA;
19
+ operationId: string;
20
+ kind: 'update';
21
+ packageName: string;
22
+ state: UpdateOperationState;
23
+ createdAt: number;
24
+ startedAt: number | null;
25
+ finishedAt: number | null;
26
+ beforeVersion: string | null;
27
+ installedVersion: string | null;
28
+ progress: {
29
+ phase: string | null;
30
+ done: number;
31
+ total: number | null;
32
+ percent: number | null;
33
+ currentPackage: string | null;
34
+ detail: string | null;
35
+ downloaded: number | null;
36
+ size: number | null;
37
+ };
38
+ outcome: {
39
+ refreshRequired: boolean;
40
+ restartRequired: boolean;
41
+ rollback: {
42
+ available: boolean;
43
+ state: 'unavailable' | 'available' | 'running' | 'succeeded' | 'failed';
44
+ detail: string | null;
45
+ };
46
+ };
47
+ failure: UpdateFailureV1 | null;
48
+ }
49
+ /** Process-local operation registry. A boot id scopes ids across restarts. */
50
+ export declare class UpdateOperationStoreV1 {
51
+ private readonly bootId;
52
+ private readonly now;
53
+ private readonly maxOperations;
54
+ private sequence;
55
+ private readonly operations;
56
+ private activeId;
57
+ constructor(bootId: string, now?: () => number, maxOperations?: number);
58
+ hasActive(): boolean;
59
+ create(packageName: string, beforeVersion: string | null): UpdateOperationV1;
60
+ start(operationId: string): void;
61
+ finish(operationId: string, status: number, payload: unknown, installedVersion: string | null): UpdateOperationV1 | null;
62
+ beginRollback(operationId: string): string | null;
63
+ finishRollback(operationId: string, status: number, payload: unknown, installedVersion?: string | null): UpdateOperationV1 | null;
64
+ get(operationId: string, progress?: InstallProgress): UpdateOperationV1 | null;
65
+ private snapshot;
66
+ }