@dsh-workbench/core 1.1.3 → 1.1.4

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 (26) hide show
  1. package/client.js +686 -505
  2. package/dist/client/client.body.js +686 -505
  3. package/dist/client/index.js +541 -360
  4. package/dist/host/index.js +244 -6
  5. package/dist/types/app/layouts/market/discover-confirm.d.ts +3 -2
  6. package/dist/types/app/layouts/market/market-check.d.ts +10 -0
  7. package/dist/types/app/layouts/market/market-header.d.ts +1 -3
  8. package/dist/types/app/layouts/market/use-discover.d.ts +2 -1
  9. package/dist/types/app/layouts/market/use-git-transport.d.ts +2 -2
  10. package/dist/types/app/layouts/market/use-module-versions.d.ts +3 -0
  11. package/dist/types/app/layouts/market/use-settling-modules.d.ts +1 -0
  12. package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
  13. package/dist/types/app/layouts/rightbar/market-freshness.d.ts +1 -0
  14. package/dist/types/app/layouts/rightbar/market-row-types.d.ts +4 -1
  15. package/dist/types/app/layouts/rightbar/market-rows.d.ts +2 -0
  16. package/dist/types/app/layouts/rightbar/market-section.d.ts +4 -2
  17. package/dist/types/app/layouts/rightbar/use-market-default.d.ts +12 -0
  18. package/dist/types/kernel/contract/market-index.d.ts +2 -2
  19. package/dist/types/modules/panels/model/market-installed.d.ts +19 -0
  20. package/dist/types/modules/panels/model/market-installed.test.d.ts +1 -0
  21. package/dist/types/modules/panels/model/market-route.d.ts +1 -0
  22. package/dist/types/shared/api/host/git-probe.d.ts +3 -0
  23. package/dist/types/shared/api/host/market-sources-store.d.ts +2 -1
  24. package/dist/types/shared/api/host/profile-dependencies.d.ts +1 -0
  25. package/dist/types/shared/hooks/market-queries.d.ts +2 -1
  26. package/package.json +3 -3
@@ -388,6 +388,99 @@ function boardSituation(probe, workshop) {
388
388
  if (probe.where !== "" && !insideWorkshop(workshop, probe.where)) return "off-workspace";
389
389
  return "panel-closed";
390
390
  }
391
+ //#endregion
392
+ //#region ../shared/dist/lib/git-address.js
393
+ var SCP = /^(?:([A-Za-z0-9._-]+)@)?([A-Za-z0-9.-]+):(?!\/)(\S+)$/;
394
+ function bare(url) {
395
+ return url.trim().replace(/^git\+/, "");
396
+ }
397
+ function pathOf(pathname) {
398
+ return pathname.replace(/^\/+/, "").replace(/\/+$/, "");
399
+ }
400
+ /**
401
+ * 把一个 git 地址拆成「哪台主机 + 哪个路径」,不管发布者当初用的是 https、ssh 还是
402
+ * scp 那种 `git@host:path` 写法;认不出来给 undefined。
403
+ */
404
+ function gitAddressOf(url) {
405
+ const text = bare(url);
406
+ if (text === "") return void 0;
407
+ if (text.includes("://")) {
408
+ let parsed;
409
+ try {
410
+ parsed = new URL(text);
411
+ } catch {
412
+ return;
413
+ }
414
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" && parsed.protocol !== "ssh:") return;
415
+ const path = pathOf(parsed.pathname);
416
+ if (path === "") return void 0;
417
+ const port = parsed.port === "" ? void 0 : Number(parsed.port);
418
+ const shared = {
419
+ host: parsed.hostname,
420
+ path,
421
+ ...port === void 0 ? {} : { port }
422
+ };
423
+ if (parsed.protocol !== "ssh:") return {
424
+ kind: "https",
425
+ ...shared
426
+ };
427
+ return {
428
+ kind: "ssh",
429
+ ...shared,
430
+ ...parsed.username === "" ? {} : { user: parsed.username }
431
+ };
432
+ }
433
+ const scp = SCP.exec(text);
434
+ if (scp === null) return void 0;
435
+ const path = pathOf(scp[3] ?? "");
436
+ if (path === "") return void 0;
437
+ return {
438
+ kind: "ssh",
439
+ host: scp[2] ?? "",
440
+ path,
441
+ ...scp[1] === void 0 ? {} : { user: scp[1] }
442
+ };
443
+ }
444
+ /** 一份地址的规范写法:ssh 一律成 `ssh://user@host/path`(pnpm 认这个),https 原样。 */
445
+ function gitAddressText(address) {
446
+ const port = address.port === void 0 ? "" : `:${String(address.port)}`;
447
+ if (address.kind === "ssh") return `ssh://${address.user === void 0 || address.user === "" ? "git" : address.user}@${address.host}${port}/${address.path}`;
448
+ return `https://${address.host}${port}/${address.path}`;
449
+ }
450
+ /**
451
+ * 同一个仓库的两种读法,发布者用的那种排前面。ssh 那条一律规范成 `ssh://` 形式,
452
+ * 因为 `git@host:path` 拼进安装 spec 时 pnpm 不认;https 那条保留发布者的原样(含 http)。
453
+ */
454
+ function gitTransportCandidates(url) {
455
+ const address = gitAddressOf(url);
456
+ if (address === void 0) {
457
+ const only = bare(url);
458
+ return only === "" ? [] : [{
459
+ kind: only.startsWith("ssh") ? "ssh" : "https",
460
+ url: only
461
+ }];
462
+ }
463
+ const primary = {
464
+ kind: address.kind,
465
+ url: address.kind === "ssh" ? gitAddressText(address) : bare(url)
466
+ };
467
+ const other = address.kind === "https" ? {
468
+ kind: "ssh",
469
+ url: gitAddressText({
470
+ kind: "ssh",
471
+ host: address.host,
472
+ path: address.path
473
+ })
474
+ } : {
475
+ kind: "https",
476
+ url: gitAddressText({
477
+ kind: "https",
478
+ host: address.host,
479
+ path: address.path
480
+ })
481
+ };
482
+ return other.url === primary.url ? [primary] : [primary, other];
483
+ }
391
484
  /**
392
485
  * 外壳愿意认的协议版本;不在表里的模块当场被拒,不猜语义。
393
486
  */
@@ -4546,6 +4639,7 @@ function checkVersionRange(contract, version) {
4546
4639
  var MARKET_DISCOVER_ROUTE = "panels/market-discover";
4547
4640
  var MARKET_DOC_ROUTE = "panels/market-doc";
4548
4641
  var MARKET_GIT_PROBE_ROUTE = "panels/git-probe";
4642
+ var MARKET_INSTALLED_ROUTE = "panels/market-installed";
4549
4643
  var MARKET_PUBLISH_PREVIEW_ROUTE = "panels/market-publish-preview";
4550
4644
  var MARKET_SOURCES_RESOURCE = "market-sources";
4551
4645
  var MARKET_SOURCES_ROUTE = "panels/market-sources";
@@ -5581,6 +5675,18 @@ async function probeGitAddress(shell, url, options = {}) {
5581
5675
  ...detail === "" ? {} : { detail }
5582
5676
  };
5583
5677
  }
5678
+ var PROBE_CACHE_MS = 6e4;
5679
+ var probeCache = /* @__PURE__ */ new Map();
5680
+ async function probeGitAddressCached(shell, url, options = {}) {
5681
+ const hit = probeCache.get(url);
5682
+ if (hit !== void 0 && Date.now() - hit.at < PROBE_CACHE_MS) return hit.payload;
5683
+ const payload = await probeGitAddress(shell, url, options);
5684
+ probeCache.set(url, {
5685
+ at: Date.now(),
5686
+ payload
5687
+ });
5688
+ return payload;
5689
+ }
5584
5690
  //#endregion
5585
5691
  //#region src/shared/api/host/git-routes.ts
5586
5692
  function gitProbeHandler(ctx) {
@@ -11749,10 +11855,22 @@ function pluginNameOf(entry) {
11749
11855
  }
11750
11856
  //#endregion
11751
11857
  //#region src/modules/panels/model/market-route.ts
11858
+ function packageNameInPath(path) {
11859
+ const parts = path.split("/").filter((part) => part !== "");
11860
+ const last = parts[parts.length - 1];
11861
+ if (last === void 0 || last === "") return void 0;
11862
+ const parent = parts[parts.length - 2];
11863
+ return parent !== void 0 && parent.startsWith("@") ? `${parent}/${last}` : last;
11864
+ }
11752
11865
  function bundleNameOf(change, spec) {
11753
11866
  if (change.bundle !== void 0 && change.bundle !== "") return change.bundle;
11754
- const at = spec.lastIndexOf("@");
11755
- return at > 0 ? spec.slice(0, at) : spec;
11867
+ const at = spec.indexOf("&path:");
11868
+ if (at !== -1) {
11869
+ const inPath = packageNameInPath(spec.slice(at + 6));
11870
+ if (inPath !== void 0) return inPath;
11871
+ }
11872
+ const version = spec.lastIndexOf("@");
11873
+ return version > 0 ? spec.slice(0, version) : spec;
11756
11874
  }
11757
11875
  function installedPluginName(spec, pkg) {
11758
11876
  const local = localDirOf(spec);
@@ -12994,6 +13112,85 @@ function marketDiscoverRoutes(discover) {
12994
13112
  };
12995
13113
  }
12996
13114
  //#endregion
13115
+ //#region src/modules/panels/model/market-installed.ts
13116
+ async function transportOf(source, spec, probe, preferred) {
13117
+ if (source.type !== "git" || probe === void 0) return source;
13118
+ const candidates = gitTransportCandidates(source.url);
13119
+ if (candidates.length === 0) return source;
13120
+ const installed = spec === void 0 ? void 0 : gitAddressOf(spec);
13121
+ const first = preferred ?? installed?.kind;
13122
+ const ordered = first === void 0 ? candidates : [...candidates].sort((left, right) => Number(right.kind === first) - Number(left.kind === first));
13123
+ for (const candidate of ordered) if (await probe(candidate.url).catch(() => "unreachable") === "ok") return {
13124
+ ...source,
13125
+ url: candidate.url
13126
+ };
13127
+ return {
13128
+ ...source,
13129
+ url: ordered[0]?.url ?? source.url
13130
+ };
13131
+ }
13132
+ async function installedFromIndexes(input) {
13133
+ const modules = new Map(input.modules.map((module) => [module.pkg, module]));
13134
+ const rows = [];
13135
+ const seen = /* @__PURE__ */ new Set();
13136
+ for (const source of input.sources) {
13137
+ const index = input.cached(source);
13138
+ if (index === void 0) continue;
13139
+ for (const entry of index.plugins) {
13140
+ const module = modules.get(entry.name);
13141
+ const newest = entry.versions[0];
13142
+ if (module === void 0 || newest === void 0 || seen.has(entry.name)) continue;
13143
+ if (!input.isNewer(newest.version, module.version)) continue;
13144
+ seen.add(entry.name);
13145
+ rows.push({
13146
+ pkg: entry.name,
13147
+ unit: module.unit,
13148
+ version: newest.version,
13149
+ sourceId: source.id,
13150
+ sourceLabel: source.label,
13151
+ source: newest.source
13152
+ });
13153
+ const wanted = input.dependencyOf(entry.name);
13154
+ const host = gitAddressOf(source.url)?.host;
13155
+ const resolved = await transportOf(newest.source, wanted, input.probe, host === void 0 ? void 0 : input.preferred?.(host));
13156
+ rows[rows.length - 1] = {
13157
+ ...rows[rows.length - 1],
13158
+ source: resolved
13159
+ };
13160
+ }
13161
+ }
13162
+ return rows;
13163
+ }
13164
+ function marketInstalledRoutes(read) {
13165
+ return { [MARKET_INSTALLED_ROUTE]: async () => ({
13166
+ ok: true,
13167
+ ...await read()
13168
+ }) };
13169
+ }
13170
+ function transportOfSpec(spec) {
13171
+ const address = gitAddressOf(spec);
13172
+ return address === void 0 ? void 0 : {
13173
+ host: address.host,
13174
+ kind: address.kind
13175
+ };
13176
+ }
13177
+ //#endregion
13178
+ //#region src/shared/api/host/profile-dependencies.ts
13179
+ function dependencyReader(profileDir) {
13180
+ if (profileDir === null) return () => void 0;
13181
+ let dependencies;
13182
+ return (pkg) => {
13183
+ if (dependencies === void 0) try {
13184
+ const raw = JSON.parse(readFileSync(join(profileDir, "package.json"), "utf8")).dependencies;
13185
+ dependencies = typeof raw === "object" && raw !== null ? raw : {};
13186
+ } catch {
13187
+ dependencies = {};
13188
+ }
13189
+ const value = dependencies[pkg];
13190
+ return typeof value === "string" ? value : void 0;
13191
+ };
13192
+ }
13193
+ //#endregion
12997
13194
  //#region src/modules/panels/model/market-sources-act.ts
12998
13195
  function marketSourcesRoutes(store) {
12999
13196
  return { [MARKET_SOURCES_ROUTE]: async () => ({
@@ -13581,6 +13778,15 @@ function createMarketDocReader(options) {
13581
13778
  function fileOf(root) {
13582
13779
  return join(root, "market-index", "sources.json");
13583
13780
  }
13781
+ function transportsOf(raw) {
13782
+ const record = typeof raw === "object" && raw !== null ? raw : {};
13783
+ const found = {};
13784
+ for (const [host, kind] of Object.entries(record)) {
13785
+ if (host === "" || kind !== "https" && kind !== "ssh") continue;
13786
+ found[host] = kind;
13787
+ }
13788
+ return found;
13789
+ }
13584
13790
  function readPayload(path, logger) {
13585
13791
  let raw;
13586
13792
  try {
@@ -13588,7 +13794,8 @@ function readPayload(path, logger) {
13588
13794
  } catch {
13589
13795
  return {
13590
13796
  sources: [],
13591
- selected: null
13797
+ selected: null,
13798
+ transports: {}
13592
13799
  };
13593
13800
  }
13594
13801
  const record = typeof raw === "object" && raw !== null ? raw : {};
@@ -13605,7 +13812,8 @@ function readPayload(path, logger) {
13605
13812
  const selectedRaw = record["selected"];
13606
13813
  return {
13607
13814
  sources,
13608
- selected: typeof selectedRaw === "string" && sources.some((source) => source.id === selectedRaw) ? selectedRaw : sources[0]?.id ?? null
13815
+ selected: typeof selectedRaw === "string" && sources.some((source) => source.id === selectedRaw) ? selectedRaw : sources[0]?.id ?? null,
13816
+ transports: transportsOf(record["transports"])
13609
13817
  };
13610
13818
  }
13611
13819
  async function writePayload(path, payload) {
@@ -13624,6 +13832,18 @@ function createMarketSourcesStore(options) {
13624
13832
  function read() {
13625
13833
  return readPayload(path, options.logger);
13626
13834
  }
13835
+ async function rememberTransport(host, kind) {
13836
+ if (host === "") return;
13837
+ const current = read();
13838
+ const transports = {
13839
+ ...current.transports ?? {},
13840
+ [host]: kind
13841
+ };
13842
+ await writePayload(path, {
13843
+ ...current,
13844
+ transports
13845
+ });
13846
+ }
13627
13847
  async function apply(action) {
13628
13848
  const current = read();
13629
13849
  if (action.action === "save") {
@@ -13655,7 +13875,8 @@ function createMarketSourcesStore(options) {
13655
13875
  }
13656
13876
  return {
13657
13877
  read,
13658
- apply
13878
+ apply,
13879
+ rememberTransport
13659
13880
  };
13660
13881
  }
13661
13882
  //#endregion
@@ -15190,6 +15411,15 @@ var OWNED = [{
15190
15411
  ...diyRecordRoutes(record),
15191
15412
  ...marketDiscoverRoutes(discover),
15192
15413
  ...marketSourcesRoutes(marketSources),
15414
+ ...marketInstalledRoutes(async () => ({ modules: await installedFromIndexes({
15415
+ sources: marketSources.read().sources,
15416
+ cached: (source) => marketIndex.cached(source)?.index,
15417
+ modules: supply.profileModules(profileDir),
15418
+ dependencyOf: dependencyReader(profileDir),
15419
+ isNewer: supply.isNewerVersion,
15420
+ preferred: (host) => marketSources.read().transports?.[host],
15421
+ ...shell === void 0 ? {} : { probe: (url) => probeGitAddressCached(shell, url, { timeoutMs: 4e3 }).then((one) => one.status) }
15422
+ }) })),
15193
15423
  ...publishPreviewRoutes(workshopPathOf(ctx), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell))
15194
15424
  },
15195
15425
  registerTools: () => [
@@ -15227,7 +15457,15 @@ var OWNED = [{
15227
15457
  handle: (payload) => applyPanelsPref(panels, payload)
15228
15458
  },
15229
15459
  provision: {
15230
- handle: (request) => dispatchMarket(market, request),
15460
+ handle: async (request) => {
15461
+ const result = await dispatchMarket(market, request);
15462
+ const application = result.change?.application;
15463
+ if (request.spec !== void 0 && (application === "applied" || application === "restart-required" || application === "overridden")) {
15464
+ const used = transportOfSpec(request.spec);
15465
+ if (used !== void 0) await marketSources.rememberTransport(used.host, used.kind);
15466
+ }
15467
+ return result;
15468
+ },
15231
15469
  affectedUnits: (request) => affectedUnitsOf(manifests(), request)
15232
15470
  }
15233
15471
  };
@@ -1,5 +1,5 @@
1
1
  import type { Translate } from '@dsh-workbench/shared';
2
- import type { DiscoverEntry, DiscoverVersionRow } from '../../../kernel/contract/market-index';
2
+ import type { DiscoverEntry, DiscoverVersionRow, GitTransportKind } from '../../../kernel/contract/market-index';
3
3
  export interface DiscoverChoice {
4
4
  readonly entry: DiscoverEntry;
5
5
  readonly version: DiscoverVersionRow;
@@ -8,7 +8,8 @@ export interface DiscoverConfirmProps {
8
8
  readonly t: Translate;
9
9
  readonly locale: string;
10
10
  readonly choice: DiscoverChoice | null;
11
+ readonly transports: Readonly<Record<string, GitTransportKind>>;
11
12
  readonly onCancel: () => void;
12
13
  readonly onConfirm: (url?: string) => void;
13
14
  }
14
- export declare function DiscoverConfirm({ t, locale, choice, onCancel, onConfirm }: DiscoverConfirmProps): JSX.Element | null;
15
+ export declare function DiscoverConfirm({ t, locale, choice, transports, onCancel, onConfirm }: DiscoverConfirmProps): JSX.Element | null;
@@ -0,0 +1,10 @@
1
+ import type { Translate } from '@dsh-workbench/shared';
2
+ export interface MarketCheckProps {
3
+ readonly t: Translate;
4
+ readonly checkedAt: number | null;
5
+ readonly checking: boolean;
6
+ readonly failed: boolean;
7
+ readonly disabled: boolean;
8
+ readonly onCheck: () => void;
9
+ }
10
+ export declare function MarketCheck({ t, checkedAt, checking, failed, disabled, onCheck }: MarketCheckProps): JSX.Element;
@@ -5,9 +5,7 @@ export interface MarketHeaderProps {
5
5
  readonly idPrefix: string;
6
6
  readonly tab: MarketTab;
7
7
  readonly busy: boolean;
8
- readonly checking: boolean;
9
8
  readonly onTab: (tab: MarketTab) => void;
10
- readonly onReload: () => void;
11
9
  readonly onAdd: () => void;
12
10
  }
13
- export declare function MarketHeader({ t, idPrefix, tab, busy, checking, onTab, onReload, onAdd }: MarketHeaderProps): JSX.Element;
11
+ export declare function MarketHeader({ t, idPrefix, tab, busy, onTab, onAdd }: MarketHeaderProps): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import type { Translate } from '@dsh-workbench/shared';
2
- import type { DiscoverEntry, DiscoverPayload, DiscoverVersionRow, MarketSourceConfig } from '../../../kernel/contract/market-index';
2
+ import type { DiscoverEntry, DiscoverPayload, DiscoverVersionRow, GitTransportKind, MarketSourceConfig } from '../../../kernel/contract/market-index';
3
3
  import { type DiscoverSort } from './discover-view';
4
4
  interface InstallTarget {
5
5
  readonly entry: DiscoverEntry;
@@ -25,6 +25,7 @@ export interface DiscoverController {
25
25
  readonly failure: string | null;
26
26
  readonly sources: DiscoverPayload['sources'];
27
27
  readonly selected: string | null;
28
+ readonly transports: Readonly<Record<string, GitTransportKind>>;
28
29
  readonly entries: readonly DiscoverEntry[];
29
30
  readonly shown: readonly DiscoverEntry[];
30
31
  readonly total: number;
@@ -1,4 +1,4 @@
1
- import type { GitProbeStatus, MarketIndexGitSource } from '../../../kernel/contract/market-index';
1
+ import type { GitProbeStatus, GitTransportKind, MarketIndexGitSource } from '../../../kernel/contract/market-index';
2
2
  export type TransportProbe = GitProbeStatus | 'checking' | 'unknown';
3
3
  export interface GitTransportRow {
4
4
  readonly kind: 'https' | 'ssh';
@@ -16,4 +16,4 @@ export interface GitTransportState {
16
16
  setCustom(value: string): void;
17
17
  checkCustom(): void;
18
18
  }
19
- export declare function useGitTransport(source: MarketIndexGitSource | null): GitTransportState;
19
+ export declare function useGitTransport(source: MarketIndexGitSource | null, preferredKind?: GitTransportKind | undefined): GitTransportState;
@@ -1,4 +1,5 @@
1
1
  import type { ModuleVersion, WorkbenchSelfUpdate, WorkbenchVersion } from '../../../kernel/contract/bundle-install';
2
+ import type { MarketInstalledModule } from '../../../kernel/contract/market-index';
2
3
  import type { InstalledPlugin } from '../../../shared/api/installed-plugins';
3
4
  export interface ModuleVersionsState {
4
5
  readonly modules: readonly ModuleVersion[] | null;
@@ -7,6 +8,7 @@ export interface ModuleVersionsState {
7
8
  readonly selfUpdate: WorkbenchSelfUpdate | null;
8
9
  readonly read: boolean;
9
10
  readonly checking: boolean;
11
+ readonly failed: boolean;
10
12
  readonly checkedAt: number | null;
11
13
  }
12
14
  export interface ModuleVersionsView extends ModuleVersionsState {
@@ -19,5 +21,6 @@ export declare function bumpModuleVersionsEpoch(): void;
19
21
  export declare function moduleVersionsValue(): ModuleVersionsState;
20
22
  export declare function resetModuleVersions(): void;
21
23
  export declare function subscribeModuleVersions(listener: () => void): () => void;
24
+ export declare function withMarketUpdates(modules: readonly ModuleVersion[], rows: readonly MarketInstalledModule[]): readonly ModuleVersion[];
22
25
  export declare function runModuleVersionsRead(force?: boolean, fresh?: boolean): Promise<void>;
23
26
  export declare function useModuleVersions(): ModuleVersionsView;
@@ -0,0 +1 @@
1
+ export declare function useSettlingModules(installedKey: string, rosterKey: string, activeKey: string): ReadonlySet<string>;
@@ -20,6 +20,8 @@ export interface MarketViewProps {
20
20
  }) => void;
21
21
  readonly onPending?: ((pending: boolean) => void) | undefined;
22
22
  readonly updates?: ModuleUpdateMap | undefined;
23
+ readonly updatesCheckedAt?: number | null | undefined;
24
+ readonly updatesFailed?: boolean | undefined;
23
25
  readonly installed?: readonly InstalledWorkbenchModule[] | undefined;
24
26
  readonly installedRead?: boolean | undefined;
25
27
  readonly workbench?: WorkbenchVersion | null | undefined;
@@ -30,4 +32,4 @@ export interface MarketViewProps {
30
32
  readonly defaults?: readonly string[] | undefined;
31
33
  readonly changed?: Readonly<Record<string, number>> | undefined;
32
34
  }
33
- export declare function MarketView({ t, titleOf, manifests, installable, capabilities, freshness, onChanged, onPending, updates, installed, installedRead, workbench, selfUpdate, plugins, settling, mock, defaults, changed, }: MarketViewProps): JSX.Element;
35
+ export declare function MarketView({ t, titleOf, manifests, installable, capabilities, freshness, onChanged, onPending, updates, updatesCheckedAt, updatesFailed, installed, installedRead, workbench, selfUpdate, plugins, settling, mock, defaults, changed, }: MarketViewProps): JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import type { Translate } from '@dsh-workbench/shared';
2
2
  export declare const MARKET_STALE_MS = 600000;
3
3
  export declare function checkedAgoText(at: number, now: number, t: Translate): string;
4
+ export declare function checkedAgo(at: number, t: Translate): string;
4
5
  export interface MarketFreshness {
5
6
  readonly loaded: boolean;
6
7
  readonly checking: boolean;
@@ -1,5 +1,6 @@
1
1
  import type { ModuleUpdateBlock } from '../../../kernel/contract/bundle-install';
2
- export type MarketRowAction = 'install' | 'update' | 'update-bundle' | 'remove-bundle' | 'uninstall' | 'update-dependency' | 'update-plugin' | 'uninstall-plugin' | 'update-workbench' | 'migrate-workbench' | 'dismiss-followup';
2
+ import type { MarketInstalledModule } from '../../../kernel/contract/market-index';
3
+ export type MarketRowAction = 'install' | 'update' | 'update-bundle' | 'install-version' | 'remove-bundle' | 'uninstall' | 'update-dependency' | 'update-plugin' | 'uninstall-plugin' | 'update-workbench' | 'migrate-workbench' | 'dismiss-followup';
3
4
  export type MarketState = 'checking' | 'installed' | 'inactive' | 'unusable' | 'broken' | 'outdated' | 'pending-restart' | 'pending-followup' | 'missing' | 'unchecked' | 'unknown';
4
5
  export interface MarketNote {
5
6
  readonly key: 'panels.market.needEnv' | 'panels.market.noSource' | 'panels.market.badSource' | 'panels.market.needCommands' | 'panels.market.needBinary' | 'panels.market.needLib' | 'panels.market.moduleRefused' | 'panels.market.moduleRestartHint' | 'panels.market.pluginAlsoKnownAs' | 'panels.market.pluginBroken';
@@ -12,6 +13,7 @@ export interface ModuleUpdate {
12
13
  readonly latest?: string;
13
14
  readonly local?: boolean;
14
15
  readonly blocked?: ModuleUpdateBlock;
16
+ readonly market?: MarketInstalledModule;
15
17
  }
16
18
  export interface MarketFollowUp {
17
19
  readonly id: string;
@@ -35,6 +37,7 @@ export interface MarketRow {
35
37
  readonly latest?: string;
36
38
  };
37
39
  readonly moduleUpdate?: ModuleUpdate;
40
+ readonly market?: MarketInstalledModule;
38
41
  readonly pkg?: string;
39
42
  readonly local?: boolean;
40
43
  readonly versionLabel?: string;
@@ -1,4 +1,5 @@
1
1
  import type { ModuleUpdateBlock, ModuleVersion } from '../../../kernel/contract/bundle-install';
2
+ import type { MarketInstalledModule } from '../../../kernel/contract/market-index';
2
3
  import type { Translate, UnitManifest, UnitTitle } from '@dsh-workbench/shared';
3
4
  import type { MarketNameMap } from './market-names';
4
5
  import type { MarketRow, MarketRowAction, MarketState, ModuleUpdate } from './market-row-types';
@@ -21,6 +22,7 @@ export interface InstalledWorkbenchModule {
21
22
  readonly local?: boolean | undefined;
22
23
  readonly refused?: readonly string[] | undefined;
23
24
  readonly clientRefused?: readonly string[] | undefined;
25
+ readonly market?: MarketInstalledModule | undefined;
24
26
  }
25
27
  export declare function withClientRefusals(modules: readonly InstalledWorkbenchModule[], refusals: readonly {
26
28
  readonly unit: string;
@@ -1,9 +1,9 @@
1
- import type { Translate } from '@dsh-workbench/shared';
1
+ import type { ReactNode, Translate } from '@dsh-workbench/shared';
2
2
  import type { MarketRow } from './market-row-types';
3
3
  import { type MarketRowAction } from './market-rows';
4
4
  import type { MarketSectionIcon } from './market-section-icon';
5
5
  import { type Pending } from './market-view-text';
6
- export declare function MarketSection({ t, titleKey, icon, rows, pending, failureOf, act, confirmActions, onConfirming, onDefault, boardBusy, }: {
6
+ export declare function MarketSection({ t, titleKey, icon, rows, pending, failureOf, act, confirmActions, onConfirming, onDefault, boardBusy, headAction, keepHead, }: {
7
7
  readonly t: Translate;
8
8
  readonly titleKey?: string;
9
9
  readonly icon?: MarketSectionIcon;
@@ -15,4 +15,6 @@ export declare function MarketSection({ t, titleKey, icon, rows, pending, failur
15
15
  readonly boardBusy?: boolean | undefined;
16
16
  readonly confirmActions: readonly MarketRowAction[];
17
17
  readonly onConfirming: (id: string | null) => void;
18
+ readonly headAction?: ReactNode;
19
+ readonly keepHead?: boolean;
18
20
  }): JSX.Element | null;
@@ -0,0 +1,12 @@
1
+ import type { Translate } from '@dsh-workbench/shared';
2
+ export interface MarketDefaultSeat {
3
+ readonly pending: boolean;
4
+ setDefault(id: string, next: boolean): Promise<void>;
5
+ }
6
+ export declare function useMarketDefault(input: {
7
+ readonly t: Translate;
8
+ readonly mocked: boolean;
9
+ readonly defaults: readonly string[];
10
+ readonly changed: Readonly<Record<string, number>>;
11
+ readonly setNotice: (next: string | null) => void;
12
+ }): MarketDefaultSeat;
@@ -1,2 +1,2 @@
1
- export { MARKET_DISCOVER_ROUTE, MARKET_DOC_ROUTE, MARKET_GIT_PROBE_ROUTE, MARKET_INDEX_FORMAT, MARKET_INDEX_PATH, MARKET_PUBLISH_PREVIEW_ROUTE, MARKET_SOURCES_RESOURCE, MARKET_SOURCES_ROUTE, MARKET_WORKBENCH_LABEL, } from '@dsh-workbench/shared/contract/protocol/market-index';
2
- export type { DiscoverCompat, DiscoverDoc, DiscoverDocKind, DiscoverEntry, DiscoverFilter, DiscoverInstallState, DiscoverPayload, DiscoverProblem, DiscoverSourceRow, DiscoverState, DiscoverVersionRow, GitProbePayload, GitProbeStatus, IndexSourceSpec, MarketClaim, MarketIndex, MarketIndexCategory, MarketIndexEngines, MarketIndexEntry, MarketIndexEntryMetadata, MarketIndexGitSource, MarketIndexLabel, MarketIndexMetadata, MarketIndexNpmSource, MarketIndexParse, MarketIndexProblem, MarketIndexProblemKind, MarketIndexSortKey, MarketIndexSource, MarketIndexVersion, MarketLocalizedText, MarketPublishDestination, MarketSourceConfig, MarketSourceKind, MarketSourcePolicy, MarketSourcesAction, MarketSourcesPayload, PublishChangelog, PublishPreview, } from '@dsh-workbench/shared/contract/protocol/market-index';
1
+ export { MARKET_DISCOVER_ROUTE, MARKET_DOC_ROUTE, MARKET_GIT_PROBE_ROUTE, MARKET_INSTALLED_ROUTE, MARKET_INDEX_FORMAT, MARKET_INDEX_PATH, MARKET_PUBLISH_PREVIEW_ROUTE, MARKET_SOURCES_RESOURCE, MARKET_SOURCES_ROUTE, MARKET_WORKBENCH_LABEL, } from '@dsh-workbench/shared/contract/protocol/market-index';
2
+ export type { DiscoverCompat, DiscoverDoc, DiscoverDocKind, DiscoverEntry, DiscoverFilter, DiscoverInstallState, DiscoverPayload, DiscoverProblem, DiscoverSourceRow, DiscoverState, DiscoverVersionRow, GitProbePayload, GitProbeStatus, GitTransportKind, MarketInstalledModule, MarketInstalledPayload, IndexSourceSpec, MarketClaim, MarketIndex, MarketIndexCategory, MarketIndexEngines, MarketIndexEntry, MarketIndexEntryMetadata, MarketIndexGitSource, MarketIndexLabel, MarketIndexMetadata, MarketIndexNpmSource, MarketIndexParse, MarketIndexProblem, MarketIndexProblemKind, MarketIndexSortKey, MarketIndexSource, MarketIndexVersion, MarketLocalizedText, MarketPublishDestination, MarketSourceConfig, MarketSourceKind, MarketSourcePolicy, MarketSourcesAction, MarketSourcesPayload, PublishChangelog, PublishPreview, } from '@dsh-workbench/shared/contract/protocol/market-index';
@@ -0,0 +1,19 @@
1
+ import type { GitTransportKind, MarketIndex, MarketInstalledModule, MarketInstalledPayload, MarketSourceConfig } from '@dsh-workbench/shared/contract/protocol/market-index';
2
+ import type { RouteTable } from '@dsh-workbench/shared/host';
3
+ import type { ProfileModule } from '@dsh-workbench/shared/host';
4
+ import type { GitProbeStatus } from '@dsh-workbench/shared/contract/protocol/market-index';
5
+ export interface InstalledIndexInput {
6
+ readonly sources: readonly MarketSourceConfig[];
7
+ readonly cached: (source: MarketSourceConfig) => MarketIndex | undefined;
8
+ readonly modules: readonly ProfileModule[];
9
+ readonly dependencyOf: (pkg: string) => string | undefined;
10
+ readonly isNewer: (latest: string, installed: string) => boolean;
11
+ readonly probe?: ((url: string) => Promise<GitProbeStatus>) | undefined;
12
+ readonly preferred?: ((host: string) => GitTransportKind | undefined) | undefined;
13
+ }
14
+ export declare function installedFromIndexes(input: InstalledIndexInput): Promise<readonly MarketInstalledModule[]>;
15
+ export declare function marketInstalledRoutes(read: () => MarketInstalledPayload | Promise<MarketInstalledPayload>): RouteTable;
16
+ export declare function transportOfSpec(spec: string): {
17
+ readonly host: string;
18
+ readonly kind: GitTransportKind;
19
+ } | undefined;
@@ -25,6 +25,7 @@ export interface BundleInstallDeps {
25
25
  readonly rememberSource: (manager: BundleManager, pkg: string, registry: string | undefined) => Promise<void>;
26
26
  readonly remove?: ((pkg: string) => Promise<BundleChange>) | undefined;
27
27
  }
28
+ export declare function bundleNameOf(change: BundleChange, spec: string): string;
28
29
  export declare function installDshBundle(deps: Omit<BundleInstallDeps, 'spec' | 'options'>, spec: string, options?: BundleInstallOptions): Promise<BundleInstallOutcome>;
29
30
  export type ReplaceBundleDeps = Omit<BundleInstallDeps, 'spec' | 'options' | 'remove'> & {
30
31
  readonly remove: (pkg: string) => Promise<BundleChange>;
@@ -4,3 +4,6 @@ export declare function classifyGitProbe(stderr: string, timedOut: boolean): Git
4
4
  export declare function probeGitAddress(shell: DshShell, url: string, options?: {
5
5
  readonly timeoutMs?: number;
6
6
  }): Promise<GitProbePayload>;
7
+ export declare function probeGitAddressCached(shell: DshShell, url: string, options?: {
8
+ readonly timeoutMs?: number;
9
+ }): Promise<GitProbePayload>;
@@ -1,4 +1,4 @@
1
- import type { MarketSourcesAction, MarketSourcesPayload } from '@dsh-workbench/shared/contract/protocol/market-index';
1
+ import type { GitTransportKind, MarketSourcesAction, MarketSourcesPayload } from '@dsh-workbench/shared/contract/protocol/market-index';
2
2
  export type MarketSourcesWrite = Exclude<MarketSourcesAction, {
3
3
  readonly action: 'init';
4
4
  } | {
@@ -7,6 +7,7 @@ export type MarketSourcesWrite = Exclude<MarketSourcesAction, {
7
7
  export interface MarketSourcesStore {
8
8
  read(): MarketSourcesPayload;
9
9
  apply(action: MarketSourcesWrite): Promise<MarketSourcesPayload>;
10
+ rememberTransport(host: string, kind: GitTransportKind): Promise<void>;
10
11
  }
11
12
  export interface MarketSourcesStoreOptions {
12
13
  readonly root: string;
@@ -0,0 +1 @@
1
+ export declare function dependencyReader(profileDir: string | null): (pkg: string) => string | undefined;
@@ -1,10 +1,11 @@
1
1
  import type { ApiResponse } from '@dsh-workbench/shared';
2
- import { type DiscoverDoc, type DiscoverDocKind, type DiscoverPayload, type GitProbePayload, type MarketSourcesPayload, type PublishPreview } from '../../kernel/contract/market-index';
2
+ import { type DiscoverDoc, type DiscoverDocKind, type DiscoverPayload, type GitProbePayload, type MarketInstalledPayload, type MarketSourcesPayload, type PublishPreview } from '../../kernel/contract/market-index';
3
3
  export declare function readMarketDiscover(options?: {
4
4
  readonly force?: boolean;
5
5
  }): Promise<ApiResponse<DiscoverPayload>>;
6
6
  export declare function readMarketSources(): Promise<ApiResponse<MarketSourcesPayload>>;
7
7
  export declare function readGitProbe(url: string): Promise<ApiResponse<GitProbePayload>>;
8
+ export declare function readMarketInstalled(): Promise<ApiResponse<MarketInstalledPayload>>;
8
9
  export declare function readMarketDoc(name: string, version: string, kind: DiscoverDocKind): Promise<ApiResponse<{
9
10
  readonly doc: DiscoverDoc;
10
11
  }>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -100,8 +100,8 @@
100
100
  "proper-lockfile": "^4.1.2",
101
101
  "which": "^7.0.0",
102
102
  "write-file-atomic": "^8.0.0",
103
- "@dsh-workbench/shared": "1.1.3",
104
- "@dsh-workbench/adapters-default": "1.1.3"
103
+ "@dsh-workbench/shared": "1.1.4",
104
+ "@dsh-workbench/adapters-default": "1.1.4"
105
105
  },
106
106
  "license": "MIT",
107
107
  "repository": {