dsh-plugin-shop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/README.zh.md +57 -0
- package/cordis.patch.yml +9 -0
- package/lib/client.js +5246 -0
- package/lib/index.js +725 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +312 -0
- package/lib/typert.remote-client.d.ts +30 -0
- package/lib/typert.remote-client.js +200 -0
- package/lib/types/client/StoreTab.d.ts +30 -0
- package/lib/types/client/index.d.ts +30 -0
- package/lib/types/client/locales.d.ts +103 -0
- package/lib/types/client/present.d.ts +78 -0
- package/lib/types/client/useInstall.d.ts +14 -0
- package/lib/types/host/catalog.d.ts +37 -0
- package/lib/types/host/executor.d.ts +32 -0
- package/lib/types/host/index.d.ts +130 -0
- package/lib/types/host/install.d.ts +21 -0
- package/lib/types/host/profile.d.ts +30 -0
- package/lib/types/host/types.d.ts +31 -0
- package/lib/types/index.d.ts +3 -0
- package/package.json +120 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** Copy dictionaries for the store Settings tab. */
|
|
2
|
+
/** Simplified Chinese dictionary and key source of truth. */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
tab: string;
|
|
5
|
+
loading: string;
|
|
6
|
+
error: string;
|
|
7
|
+
retry: string;
|
|
8
|
+
search: string;
|
|
9
|
+
catalog: string;
|
|
10
|
+
catalogStats: string;
|
|
11
|
+
categoryTool: string;
|
|
12
|
+
categoryProvider: string;
|
|
13
|
+
categoryUi: string;
|
|
14
|
+
categoryWorkflow: string;
|
|
15
|
+
categoryIntegration: string;
|
|
16
|
+
categoryOther: string;
|
|
17
|
+
empty: string;
|
|
18
|
+
emptySearch: string;
|
|
19
|
+
tierVerified: string;
|
|
20
|
+
tierVerifiedStale: string;
|
|
21
|
+
tierCommunity: string;
|
|
22
|
+
unclaimed: string;
|
|
23
|
+
capabilitiesNote: string;
|
|
24
|
+
install: string;
|
|
25
|
+
confirm: string;
|
|
26
|
+
cancel: string;
|
|
27
|
+
acknowledgementTitle: string;
|
|
28
|
+
acknowledgementBody: string;
|
|
29
|
+
installing: string;
|
|
30
|
+
installedRestartNotice: string;
|
|
31
|
+
installedNoRestartNotice: string;
|
|
32
|
+
installFailed: string;
|
|
33
|
+
installTransportFailed: string;
|
|
34
|
+
refresh: string;
|
|
35
|
+
staleLabel: string;
|
|
36
|
+
version: string;
|
|
37
|
+
repository: string;
|
|
38
|
+
license: string;
|
|
39
|
+
reviewedVersionLine: string;
|
|
40
|
+
deniedCode: string;
|
|
41
|
+
notInCatalogCode: string;
|
|
42
|
+
versionMismatchCode: string;
|
|
43
|
+
needsAcknowledgementCode: string;
|
|
44
|
+
installedSection: string;
|
|
45
|
+
installedVersion: string;
|
|
46
|
+
latestVersion: string;
|
|
47
|
+
update: string;
|
|
48
|
+
enabledSwitch: string;
|
|
49
|
+
toggleFailed: string;
|
|
50
|
+
hotApplyNote: string;
|
|
51
|
+
};
|
|
52
|
+
/** Store locale key union. */
|
|
53
|
+
export type StoreLocaleKey = keyof typeof zh;
|
|
54
|
+
/** English dictionary checked against the Chinese key set. */
|
|
55
|
+
export declare const en: {
|
|
56
|
+
tab: string;
|
|
57
|
+
loading: string;
|
|
58
|
+
error: string;
|
|
59
|
+
retry: string;
|
|
60
|
+
search: string;
|
|
61
|
+
catalog: string;
|
|
62
|
+
catalogStats: string;
|
|
63
|
+
categoryTool: string;
|
|
64
|
+
categoryProvider: string;
|
|
65
|
+
categoryUi: string;
|
|
66
|
+
categoryWorkflow: string;
|
|
67
|
+
categoryIntegration: string;
|
|
68
|
+
categoryOther: string;
|
|
69
|
+
empty: string;
|
|
70
|
+
emptySearch: string;
|
|
71
|
+
tierVerified: string;
|
|
72
|
+
tierVerifiedStale: string;
|
|
73
|
+
tierCommunity: string;
|
|
74
|
+
unclaimed: string;
|
|
75
|
+
capabilitiesNote: string;
|
|
76
|
+
install: string;
|
|
77
|
+
confirm: string;
|
|
78
|
+
cancel: string;
|
|
79
|
+
acknowledgementTitle: string;
|
|
80
|
+
acknowledgementBody: string;
|
|
81
|
+
installing: string;
|
|
82
|
+
installedRestartNotice: string;
|
|
83
|
+
installedNoRestartNotice: string;
|
|
84
|
+
installFailed: string;
|
|
85
|
+
installTransportFailed: string;
|
|
86
|
+
refresh: string;
|
|
87
|
+
staleLabel: string;
|
|
88
|
+
version: string;
|
|
89
|
+
repository: string;
|
|
90
|
+
license: string;
|
|
91
|
+
reviewedVersionLine: string;
|
|
92
|
+
deniedCode: string;
|
|
93
|
+
notInCatalogCode: string;
|
|
94
|
+
versionMismatchCode: string;
|
|
95
|
+
needsAcknowledgementCode: string;
|
|
96
|
+
installedSection: string;
|
|
97
|
+
installedVersion: string;
|
|
98
|
+
latestVersion: string;
|
|
99
|
+
update: string;
|
|
100
|
+
enabledSwitch: string;
|
|
101
|
+
toggleFailed: string;
|
|
102
|
+
hotApplyNote: string;
|
|
103
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Pure presentation core for the store tab (§5.1 Client half). No React, no
|
|
2
|
+
* timers, no I/O: every function here maps a value to a value, so fixtures
|
|
3
|
+
* drive all of it. */
|
|
4
|
+
import type { StoreLocaleKey } from './locales.ts';
|
|
5
|
+
import type { CatalogEntry, InstallRejectionCode } from '../host/index.ts';
|
|
6
|
+
/** Tier → locale key, for the entry-card tier badge (§6.2). */
|
|
7
|
+
export declare function tierKey(tier: CatalogEntry['tier']): StoreLocaleKey;
|
|
8
|
+
/** A derived listing has no author-declared catalog section; the store
|
|
9
|
+
* presents it as unclaimed, which is the signal that prompts an author to add
|
|
10
|
+
* one (§6.1). Never present a derived entry as though the author wrote it. */
|
|
11
|
+
export declare function isUnclaimed(entry: CatalogEntry): boolean;
|
|
12
|
+
/** Spec §9.3 verbatim — the community-tier acknowledgement. The zh dictionary
|
|
13
|
+
* states the same facts in its own register; never soften this wording. */
|
|
14
|
+
export declare const ACKNOWLEDGEMENT_EN = "Once installed, this plugin holds the same privileges as a built-in one: reading and writing your files, running shell commands, and reading and modifying the requests sent to the model. It has not been reviewed.";
|
|
15
|
+
/** Spec §9.3 in the zh register — the same facts, the same force. */
|
|
16
|
+
export declare const ACKNOWLEDGEMENT_ZH = "\u5B89\u88C5\u540E\uFF0C\u6B64\u63D2\u4EF6\u5C06\u62E5\u6709\u4E0E\u5185\u7F6E\u63D2\u4EF6\u76F8\u540C\u7684\u6743\u9650\uFF1A\u8BFB\u5199\u4F60\u7684\u6587\u4EF6\u3001\u6267\u884C shell \u547D\u4EE4\uFF0C\u4EE5\u53CA\u8BFB\u53D6\u548C\u4FEE\u6539\u53D1\u9001\u7ED9\u6A21\u578B\u7684\u8BF7\u6C42\u3002\u5B83\u672A\u7ECF\u5BA1\u6838\u3002";
|
|
17
|
+
/** Install rejection code → locale key, for the rejected-state code label
|
|
18
|
+
* (§7.2): every rejection the host can return has a human label here. */
|
|
19
|
+
export declare function rejectionCodeKey(code: InstallRejectionCode): StoreLocaleKey;
|
|
20
|
+
/** One polled install status (§7.3 wire data), structural. */
|
|
21
|
+
export interface InstallStatusShape {
|
|
22
|
+
found: boolean;
|
|
23
|
+
state: 'running' | 'done' | 'failed';
|
|
24
|
+
log: string[];
|
|
25
|
+
needsRestart?: boolean;
|
|
26
|
+
detail?: string;
|
|
27
|
+
}
|
|
28
|
+
/** The install view state machine (§7.2). */
|
|
29
|
+
export type InstallView = {
|
|
30
|
+
kind: 'idle';
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'rejected';
|
|
33
|
+
code: InstallRejectionCode;
|
|
34
|
+
detail: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'running';
|
|
37
|
+
installId: string;
|
|
38
|
+
log: string[];
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'done';
|
|
41
|
+
needsRestart: boolean;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'failed';
|
|
44
|
+
detail: string;
|
|
45
|
+
log: string[];
|
|
46
|
+
};
|
|
47
|
+
/** One event the install view reacts to. */
|
|
48
|
+
export type InstallEvent = {
|
|
49
|
+
type: 'rejected';
|
|
50
|
+
code: InstallRejectionCode;
|
|
51
|
+
detail: string;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'started';
|
|
54
|
+
installId: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'status';
|
|
57
|
+
status: InstallStatusShape;
|
|
58
|
+
};
|
|
59
|
+
/** §7.2 once-per-second poll cadence, as a named constant. */
|
|
60
|
+
export declare const INSTALL_POLL_MS = 1000;
|
|
61
|
+
/** Pure install view reducer. A `status` event only applies to the install the
|
|
62
|
+
* view is tracking — a status for an install it never started (idle) is an
|
|
63
|
+
* unrelated event and a no-op, like any other event it does not recognize. */
|
|
64
|
+
export declare function reduceInstall(state: InstallView, event: InstallEvent): InstallView;
|
|
65
|
+
/**
|
|
66
|
+
* Whether a package name reads as a plugin store (a marketplace for dsh
|
|
67
|
+
* plugins, e.g. `dsh-plugin-shop`, `dsh-store`, `pluginstore`). The store
|
|
68
|
+
* tab hides these so the market does not list competing markets.
|
|
69
|
+
*
|
|
70
|
+
* Precision over recall: a name merely CONTAINING a keyword segment without
|
|
71
|
+
* a plugin qualifier or a keyword ending (`market-data-provider`,
|
|
72
|
+
* `marketplace-hub`) is NOT a store plugin, and `dsh-restore` must never
|
|
73
|
+
* match on the substring "store".
|
|
74
|
+
*/
|
|
75
|
+
export declare function isStoreLike(name: string): boolean;
|
|
76
|
+
/** The locale key for one entry's category — derived entries read as `other`
|
|
77
|
+
* (§6.1: a derived listing has no declared category). */
|
|
78
|
+
export declare function categoryKey(entry: CatalogEntry): StoreLocaleKey;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Install driving hook for one entry: start, poll to terminal, reset. */
|
|
2
|
+
import { type InstallView } from './present.ts';
|
|
3
|
+
import type { InstallArgs, StoreInstallResult, StoreInstallStatusResult } from '../host/index.ts';
|
|
4
|
+
export interface UseInstallResult {
|
|
5
|
+
view: InstallView;
|
|
6
|
+
start: (args: InstallArgs) => Promise<void>;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
}
|
|
9
|
+
/** Drive one install: rejections, the polling loop at INSTALL_POLL_MS, and
|
|
10
|
+
* terminal states. The interval is cleared on unmount and whenever the view
|
|
11
|
+
* leaves `running`, so a done/failed/rejected install never polls again. */
|
|
12
|
+
export declare function useInstall(install: (args: InstallArgs) => Promise<StoreInstallResult>, installStatus: (args: {
|
|
13
|
+
installId: string;
|
|
14
|
+
}) => Promise<StoreInstallStatusResult>): UseInstallResult;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Catalog fetch, verification, and disk cache — the Host's only network path. */
|
|
2
|
+
import type { CatalogEntry, DeniedEntry } from './types.ts';
|
|
3
|
+
/** Highest schemaVersion this build understands; a higher one is refused (§10). */
|
|
4
|
+
export declare const SUPPORTED_SCHEMA_VERSION = 2;
|
|
5
|
+
export interface CatalogSnapshot {
|
|
6
|
+
schemaVersion: number;
|
|
7
|
+
builtAt: string;
|
|
8
|
+
entries: CatalogEntry[];
|
|
9
|
+
denied: DeniedEntry[];
|
|
10
|
+
}
|
|
11
|
+
export interface CatalogResult {
|
|
12
|
+
snapshot: CatalogSnapshot;
|
|
13
|
+
stale: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface CatalogFs {
|
|
16
|
+
exists: (path: string) => boolean;
|
|
17
|
+
read: (path: string) => string;
|
|
18
|
+
write: (path: string, data: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface LoadCatalogOptions {
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
cacheDir: string;
|
|
23
|
+
refresh?: boolean;
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
now?: () => Date;
|
|
26
|
+
fsImpl?: CatalogFs;
|
|
27
|
+
sleep?: (ms: number) => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load the catalog snapshot: fetch the pointer, verify the data file's sha256
|
|
31
|
+
* against it, cache both on disk, and serve the cached copy with `stale: true`
|
|
32
|
+
* only when the transport itself failed — the fetch threw or returned a
|
|
33
|
+
* non-2xx (§10). A schemaVersion higher than this build supports, a malformed
|
|
34
|
+
* pointer or data file, an absolute data URL, or a sha256 mismatch — fresh or
|
|
35
|
+
* cached — throws even when a cache exists; never silently degraded.
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadCatalog(options: LoadCatalogOptions): Promise<CatalogResult>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Install executor: spawn the dsh CLI, stream its output, serialize per profile. */
|
|
2
|
+
export type InstallState = 'running' | 'done' | 'failed';
|
|
3
|
+
export interface InstallStatus {
|
|
4
|
+
state: InstallState;
|
|
5
|
+
log: string[];
|
|
6
|
+
needsRestart?: boolean;
|
|
7
|
+
detail?: string;
|
|
8
|
+
}
|
|
9
|
+
interface RunningInstall {
|
|
10
|
+
installId: string;
|
|
11
|
+
status: () => InstallStatus;
|
|
12
|
+
finished: Promise<InstallStatus>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Run one `dsh plugin --profile <profile> add <spec>` and track it.
|
|
16
|
+
* Never rolls back; a failure surfaces stderr verbatim plus the recovery hint
|
|
17
|
+
* (§10). The store never passes build-script flags: `allowBuilds` stays the
|
|
18
|
+
* user's explicit decision in the CLI (§7.2).
|
|
19
|
+
* The child inherits the current environment unless `env` is given — the
|
|
20
|
+
* real-install test pins DSH_HOME to a temporary directory this way.
|
|
21
|
+
* When `expectedName` is given, a zero exit is confirmed against the profile
|
|
22
|
+
* manifest (§7.2 step 6) before the install reports `done`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startInstall(options: {
|
|
25
|
+
profile: string;
|
|
26
|
+
spec: string;
|
|
27
|
+
dshBin?: string;
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
expectedName?: string;
|
|
30
|
+
onStatus?: (status: InstallStatus) => void;
|
|
31
|
+
}): RunningInstall;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** StoreGateway: the Host half of dsh-plugin-shop (§5.1). */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
4
|
+
import { loadCatalog, type LoadCatalogOptions } from './catalog.ts';
|
|
5
|
+
import type { CatalogEntry, DeniedEntry } from './types.ts';
|
|
6
|
+
import { type InstallArgs, type InstallRejectionCode } from './install.ts';
|
|
7
|
+
import { type InstallStatus } from './executor.ts';
|
|
8
|
+
export type { InstallArgs, InstallRejectionCode } from './install.ts';
|
|
9
|
+
export type { CatalogEntry } from './types.ts';
|
|
10
|
+
/** One Loader inventory entry, structurally — the store never depends on
|
|
11
|
+
* cordis-plugin-loader, whose types do not reach this package's typecheck. */
|
|
12
|
+
export interface InventoryEntry {
|
|
13
|
+
entryId: string;
|
|
14
|
+
moduleName: string;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Test-only injection points; production callers pass nothing. */
|
|
18
|
+
export interface StoreGatewayOptions {
|
|
19
|
+
catalogUrl?: string;
|
|
20
|
+
cacheDir?: string;
|
|
21
|
+
loadCatalog?: (options: LoadCatalogOptions) => ReturnType<typeof loadCatalog>;
|
|
22
|
+
/** The profile dsh installs into; discovered from this module's own
|
|
23
|
+
* location when omitted. */
|
|
24
|
+
profile?: string;
|
|
25
|
+
/** The profile directory the user layer lives in; discovered when omitted. */
|
|
26
|
+
profileDir?: string;
|
|
27
|
+
/** The Loader plugin inventory; read from `ctx` when omitted. */
|
|
28
|
+
inventory?: {
|
|
29
|
+
list(): InventoryEntry[];
|
|
30
|
+
};
|
|
31
|
+
dshBin?: string;
|
|
32
|
+
}
|
|
33
|
+
/** `store/installStart` result (§7.3): rejections are typed wire values with an
|
|
34
|
+
* author-readable `detail`, not thrown RPC errors. */
|
|
35
|
+
export type StoreInstallResult = {
|
|
36
|
+
ok: true;
|
|
37
|
+
installId: string;
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
code: InstallRejectionCode;
|
|
41
|
+
detail: string;
|
|
42
|
+
};
|
|
43
|
+
export interface StoreInstallStatusResult extends InstallStatus {
|
|
44
|
+
found: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** `store/setEnabled` result (§7.3): an unknown name is a typed wire value,
|
|
47
|
+
* not a thrown RPC error. */
|
|
48
|
+
export interface StoreSetEnabledResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
detail?: string;
|
|
51
|
+
}
|
|
52
|
+
/** `store/outdated` entry (§7.3): one installed plugin behind the catalog. */
|
|
53
|
+
export interface StoreOutdatedEntry {
|
|
54
|
+
name: string;
|
|
55
|
+
installed: string;
|
|
56
|
+
latest: string;
|
|
57
|
+
}
|
|
58
|
+
/** `store/catalog` result (§7.3), plus the denied list for the install gate's UI. */
|
|
59
|
+
export interface StoreCatalogResult {
|
|
60
|
+
schemaVersion: number;
|
|
61
|
+
builtAt: string;
|
|
62
|
+
stale: boolean;
|
|
63
|
+
plugins: CatalogEntry[];
|
|
64
|
+
denied: DeniedEntry[];
|
|
65
|
+
}
|
|
66
|
+
/** Remote-only service exposing the store Remote methods of §7.3.
|
|
67
|
+
*
|
|
68
|
+
* @typert service store */
|
|
69
|
+
export declare class StoreGateway extends TypertRemoteService {
|
|
70
|
+
private readonly options;
|
|
71
|
+
/** The profile dsh installs into; discovered from this module's own
|
|
72
|
+
* location when the caller does not supply one. */
|
|
73
|
+
private readonly profile;
|
|
74
|
+
private readonly profileDir?;
|
|
75
|
+
private readonly inventory?;
|
|
76
|
+
private readonly dshBin;
|
|
77
|
+
/** The install gate runs against the last loaded snapshot, never a fresh
|
|
78
|
+
* fetch per request (§7.2: the Host's cached snapshot is the truth). */
|
|
79
|
+
/** Finished install records retained, so a poll sees the true terminal
|
|
80
|
+
* state (§8: done / needsRestart / failure detail). Oldest evicted on add. */
|
|
81
|
+
private static readonly MAX_FINISHED_INSTALLS;
|
|
82
|
+
/** The install gate runs against the last loaded snapshot, never a fresh
|
|
83
|
+
* fetch per request (§7.2: the Host's cached snapshot is the truth). */
|
|
84
|
+
private lastSnapshot;
|
|
85
|
+
/** Install records, running and finished; a poll finds one here or reports not found. */
|
|
86
|
+
private readonly installs;
|
|
87
|
+
/** Every install id in insertion order, oldest first; finished-record eviction walks this from the front. */
|
|
88
|
+
private readonly installOrder;
|
|
89
|
+
constructor(ctx: Context, options?: StoreGatewayOptions);
|
|
90
|
+
/** The boot's Loader root directory (the active profile's `cordis.yml`
|
|
91
|
+
* directory, carried on `ctx.baseUrl`), when present. A `link:` install
|
|
92
|
+
* keeps this package at its source location, so the walk-up from
|
|
93
|
+
* `import.meta.url` finds the repo rather than a profile; `ctx.baseUrl`
|
|
94
|
+
* is the boot-provided authoritative answer. */
|
|
95
|
+
private bootBaseDir;
|
|
96
|
+
/** The profile directory the user layer lives in — the discovered default
|
|
97
|
+
* stays lazy so `setEnabled` works in tests via the `profileDir` option
|
|
98
|
+
* without requiring a real profile above this module. */
|
|
99
|
+
private profileDirResolved;
|
|
100
|
+
private listInventory;
|
|
101
|
+
/** Enable or disable one installed plugin, hot (§8): a disable writes the
|
|
102
|
+
* row to the user layer, an enable drops it again so the bundle default
|
|
103
|
+
* rules — the CLI's watchUserPatches applies either through HMR. */
|
|
104
|
+
setEnabled(args: {
|
|
105
|
+
name: string;
|
|
106
|
+
enabled: boolean;
|
|
107
|
+
}): StoreSetEnabledResult;
|
|
108
|
+
private rowConfig;
|
|
109
|
+
/** Browse the catalog (§7.3): cached snapshot, refreshed on demand. */
|
|
110
|
+
catalog(args?: {
|
|
111
|
+
refresh?: boolean;
|
|
112
|
+
}): Promise<StoreCatalogResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Install one cataloged version into the profile (§7.2). The four rejection
|
|
115
|
+
* paths run against this Host's snapshot before anything is spawned; only a
|
|
116
|
+
* passing request reaches the executor.
|
|
117
|
+
*/
|
|
118
|
+
install(args: InstallArgs): Promise<StoreInstallResult>;
|
|
119
|
+
/** Bound retained finished records at MAX_FINISHED_INSTALLS, evicting the
|
|
120
|
+
* oldest finished ones (insertion order, oldest first). Running records
|
|
121
|
+
* are never evicted; an id absent from the map reports `found: false`. */
|
|
122
|
+
private evictFinishedInstalls;
|
|
123
|
+
/** Poll one install's progress (§7.2); unknown ids report `found: false`. */
|
|
124
|
+
installStatus(args: {
|
|
125
|
+
installId: string;
|
|
126
|
+
}): StoreInstallStatusResult;
|
|
127
|
+
/** Installed plugins whose installed version is older than the catalog's (§7.3). */
|
|
128
|
+
outdated(): Promise<StoreOutdatedEntry[]>;
|
|
129
|
+
}
|
|
130
|
+
export default StoreGateway;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Install gate: the four rejection paths of §7.2, as a pure function. */
|
|
2
|
+
import type { CatalogSnapshot } from './catalog.ts';
|
|
3
|
+
export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement';
|
|
4
|
+
export interface InstallArgs {
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
acknowledged?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type ValidateResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
code: InstallRejectionCode;
|
|
14
|
+
detail: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Decide whether one install request may proceed, against the Host's own
|
|
18
|
+
* snapshot (§5.3). The browser sends a name; nothing the browser says about
|
|
19
|
+
* the package is trusted.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateInstall(snapshot: CatalogSnapshot, args: InstallArgs): ValidateResult;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Profile directory discovery and user-layer writes (§8: hot enable/disable). */
|
|
2
|
+
/** One id-targeted user-layer row (§8: the CLI hot-reloads this file). */
|
|
3
|
+
export interface UserLayerRow {
|
|
4
|
+
id: string;
|
|
5
|
+
disabled: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Find the profile directory that owns `startPath`.
|
|
9
|
+
*
|
|
10
|
+
* `baseDir` is the boot-provided profile directory (the Loader root's own
|
|
11
|
+
* directory, `ctx.baseUrl`) and is authoritative when it is a profile. The
|
|
12
|
+
* walk-up from `startPath` covers the case where the package is materialized
|
|
13
|
+
* inside the profile's node_modules — but a `link:` install keeps the package
|
|
14
|
+
* at its source location, so no ancestor of the module path is a profile at
|
|
15
|
+
* all. In that case only `baseDir` can answer.
|
|
16
|
+
*/
|
|
17
|
+
export declare function discoverProfile(startPath: string, baseDir?: string): {
|
|
18
|
+
name: string;
|
|
19
|
+
dir: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Upsert one row of the profile's user layer (`cordis.patch.yml`). Enabling
|
|
23
|
+
* removes the row so the bundle default rules again; disabling writes
|
|
24
|
+
* `{ id, disabled: true }` (§8: the CLI's watchUserPatches applies the change
|
|
25
|
+
* hot through HMR). Existing rows for other ids are preserved verbatim.
|
|
26
|
+
*/
|
|
27
|
+
export declare function setUserLayerRow(options: {
|
|
28
|
+
profileDir: string;
|
|
29
|
+
row: UserLayerRow;
|
|
30
|
+
}): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Wire and catalog types for the store Host half (§6.2, §7.3). */
|
|
2
|
+
export interface CatalogSummary {
|
|
3
|
+
en: string;
|
|
4
|
+
zh?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CatalogSection {
|
|
7
|
+
category: 'tool' | 'provider' | 'ui' | 'workflow' | 'integration' | 'other';
|
|
8
|
+
summary: CatalogSummary;
|
|
9
|
+
capabilities: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface CatalogEntry {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
integrity: string | null;
|
|
15
|
+
publishedAt: string | null;
|
|
16
|
+
repository: string | null;
|
|
17
|
+
license: string | null;
|
|
18
|
+
tier: 'verified' | 'verified-stale' | 'community';
|
|
19
|
+
metadata: 'declared' | 'derived';
|
|
20
|
+
review?: {
|
|
21
|
+
reviewedVersion: string;
|
|
22
|
+
reviewer: string;
|
|
23
|
+
reviewCommit: string;
|
|
24
|
+
notes: string;
|
|
25
|
+
};
|
|
26
|
+
catalog?: CatalogSection;
|
|
27
|
+
}
|
|
28
|
+
export interface DeniedEntry {
|
|
29
|
+
name: string;
|
|
30
|
+
detail: string;
|
|
31
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-plugin-shop",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The DeepSeek Harness plugin store: browse, install, enable, and update dsh plugins from a git-auditable catalog.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/LivXue/dsh-plugin-shop.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/LivXue/dsh-plugin-shop#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/LivXue/dsh-plugin-shop/issues"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./lib/types/client/index.d.ts",
|
|
23
|
+
"default": "./lib/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./remote": {
|
|
26
|
+
"types": "./lib/typert.remote-client.d.ts",
|
|
27
|
+
"default": "./lib/typert.remote-client.js"
|
|
28
|
+
},
|
|
29
|
+
"./typert": {
|
|
30
|
+
"types": "./lib/typert.host.d.ts",
|
|
31
|
+
"default": "./lib/typert.host.js"
|
|
32
|
+
},
|
|
33
|
+
"./types": {
|
|
34
|
+
"types": "./lib/types/host/index.d.ts",
|
|
35
|
+
"default": "./lib/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"lib/index.js",
|
|
42
|
+
"lib/client.js",
|
|
43
|
+
"lib/types/**/*.js",
|
|
44
|
+
"lib/types/**/*.d.ts",
|
|
45
|
+
"lib/typert.host.js",
|
|
46
|
+
"lib/typert.host.d.ts",
|
|
47
|
+
"lib/typert.remote-client.js",
|
|
48
|
+
"lib/typert.remote-client.d.ts",
|
|
49
|
+
"cordis.patch.yml"
|
|
50
|
+
],
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
54
|
+
},
|
|
55
|
+
"dsh": {
|
|
56
|
+
"bundle": {
|
|
57
|
+
"patch": "./cordis.patch.yml"
|
|
58
|
+
},
|
|
59
|
+
"client": {
|
|
60
|
+
"inject": [],
|
|
61
|
+
"platform": "web"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"js-yaml": "^4.2.0",
|
|
66
|
+
"semver": "^7.6.3",
|
|
67
|
+
"zod": "^4.4.3"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
71
|
+
"@deepseek-ai/dsh-app-boot": "^0.1.1-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.1-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"@deepseek-ai/cordis": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"@deepseek-ai/dsh-app-boot": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"@deepseek-ai/dsh-home-paths": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@deepseek-ai/dsh-typert-protocol": {
|
|
86
|
+
"optional": true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
91
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-app-boot": "^0.1.1-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.1-rc.2",
|
|
96
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
97
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
|
|
98
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.1-rc.2",
|
|
99
|
+
"@deepseek-ai/dsh-typert-generator": "^0.1.1-rc.2",
|
|
100
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
|
|
101
|
+
"@testing-library/react": "^16.1.0",
|
|
102
|
+
"@types/js-yaml": "^4.0.9",
|
|
103
|
+
"@types/node": "^22.9.0",
|
|
104
|
+
"@types/react": "^18.3.31",
|
|
105
|
+
"@types/react-dom": "^18.3.7",
|
|
106
|
+
"@types/semver": "^7.5.8",
|
|
107
|
+
"jsdom": "^25.0.1",
|
|
108
|
+
"playwright": "^1.49.0",
|
|
109
|
+
"react": "18.3.1",
|
|
110
|
+
"react-dom": "18.3.1",
|
|
111
|
+
"tsdown": "^0.22.2",
|
|
112
|
+
"typescript": "^5.6.3",
|
|
113
|
+
"vitest": "^2.1.4"
|
|
114
|
+
},
|
|
115
|
+
"scripts": {
|
|
116
|
+
"build": "tsdown && tsc && tsdown -c tsdown.client.config.ts",
|
|
117
|
+
"test": "tsdown && tsdown -c tsdown.client.config.ts && vitest run",
|
|
118
|
+
"typecheck": "tsdown && tsc --noEmit && tsc -p tsconfig.test.json"
|
|
119
|
+
}
|
|
120
|
+
}
|