510 Commits
Author SHA1 Message Date
EDDYCRAZY-CC 196fe4320d fix(scripts): repoint the split-leftover skin and pet paths at the satellites
The skin center and the pet assets moved to the satellites submodules, so
`official-tokens-snapshot.mjs` read a contract path and a generated registry
that no longer exist and `banner.html` loaded two images that no longer exist.

The tokens tool now writes into `satellites/dsh-skins`, resolves the theme from
that working tree (it is the skin center's devDependency, so this repository no
longer installs it), keeps resolving the shell bundle from this repository, and
names a missing submodule instead of reporting only a module resolution failure.
The banner loads the blue-fantasy artwork and the pet spritesheet from the
submodules.

Verified: `official-tokens-snapshot.mjs --check` reports the contract current
(299 tokens, both sources resolved), and banner.html loads both images in
headless Chrome (1920x1079 and 1536x1872).
2026-09-24 09:22:24 +08:00
EDDYCRAZY-CC bd27251482 fix(scripts): read an absolute Windows secret path in the attestation lane
`attestSecret` treated only a `/`-prefixed path as absolute, so a Windows
caller's `C:\...` path was joined onto the repository root and the local dev
secret store never resolved: the lane fell through to the empty secret. On
Linux and macOS `tmpdir()` starts with `/`, which is why the suite passed
there.

`isAbsolute` covers both platforms; the relative default
(`market/worker/.dev.vars`) still resolves against the repository.
2026-09-24 09:22:24 +08:00
zhu1090093659 ecd7a22942 fix(scripts): drop the gates pr-review cannot run
The build verification listed `pnpm skin-center:check` and
`pnpm community:check`. Both moved to the satellite repositories and no
longer exist here, so every review listed steps that cannot succeed. The
sequence now matches `.github/workflows/ci.yml`, which runs neither.
2026-09-24 08:58:07 +08:00
zhu1090093659 d6d8ffdbcb feat(market): read the satellite working trees when asked
market-fetch-inputs.mjs gains --local: it materializes each input from
the submodule working tree whatever commit that tree sits on, and records
the commit it read in the cache stamp instead of the pin. The pinned
commit stays the default source, and a checkout that has left the pin is
now reported instead of being ignored in silence, which is what made a
satellite edit stop reaching the build without saying so.

The contributor guide and the owning note describe both paths, including
that a market/dist built from --local content must not be committed.
2026-09-24 08:58:03 +08:00
zhu1090093659 df5cc9db7b docs(market): describe the refusal the lane actually meets
The note and both modules described the attestation route as the vantage the
edge does not refuse. The lane's first attested run disproved that: the request
from a runner never reached the worker, and the sweep reported HTTP 403 with
cf-ray, cf-mitigated=challenge, server=cloudflare and the Just a moment...
interstitial as the body. That is a managed challenge on the calling vantage,
the same product that answers part of every asset burst from that range, and it
repeats on every ask rather than clearing on a retry.

Say it as it is: the measurement stays inside Cloudflare, the call does not, and
what the lane reports is the refuser rather than a verdict about the assets. The
note carries the evidence and the ray, and the sidecar re-records both sides.
2026-09-24 08:52:48 +08:00
zhu1090093659 889d941518 fix(scripts): name and re-ask the edge refusal the attestation gets
The deploy lane's first attested run failed with a bare 403. The route writes
its own refusals as JSON, so a 403 carrying no error name came from whatever
the zone put in front of the route rather than from the route's secret check,
and the sweep reported the status alone: the refusing party stayed unidentified
and the event could not be looked up.

A refusal the route did not write is now described by its status, the edge
identifiers (cf-ray, cf-mitigated, server) and the start of the body, and the
window is re-asked, because that answer came from in front of the route and a
second ask may reach the route itself. The route's own verdict is reported as
it stands and is never re-asked, because it does not change on a second ask.
2026-09-24 08:49:24 +08:00
zhu1090093659 b71ecbe417 fix(scripts): write skin previews into the dsh-skins submodule
capture-previews still wrote preview/{light,dark}.jpg under packages/skins/skin-center, which the skin collection left when it moved to dsh-skins. The previews now land in the satellite checkout at satellites/dsh-skins/skins/<id>/preview/, the same working tree the pinned market input is copied from, and the script fails with an explicit message when that submodule is missing or sits on another commit. The hook-scene imports use the market layout the static server actually serves (/assets/skins/<id>/) instead of the removed monorepo path.

docs/development.md's 新增皮肤 section documents the new output location, the market:fetch --force step that local edits require (the gitlink is unchanged, so the input cache is not considered stale), and the current skin distribution facts.
2026-09-24 08:44:05 +08:00
zhu1090093659 ec81b5526e feat(market): measure the deployed assets inside the worker
The deploy lane proves that the version it published serves every path its
manifests advertise, but the public origin refuses part of every sweep sent
from a GitHub runner range: six consecutive runs reported the same ~150 of
3075 paths, a serial re-check recovered none of them after a three-minute
probe, and those exact paths answer 200 with the committed byte lengths from a
residential network, from two public cloud fetchers and from a local sweep.
The lane can read that only as a policy on the vantage, which leaves the
deployed artifacts unverified from CI.

POST /api/asset-attest moves the measurement inside Cloudflare: a caller
posts a window of up to 500 site-relative paths and reads back the byte
length the deployed version serves for each one out of its own ASSETS
binding. It is a read-only measurement over already-public assets and stays
out of the client-facing surface; one call still causes up to that many
internal asset fetches, so it fails closed behind a shared secret (503 when
the binding is unset, 403 when the secret is wrong, neither touching the
assets).

market-verify-assets.mjs keeps --origin for manual runs from a network the
edge allows, with its transient retry, serial re-check and all-403 refusal
notice, and the deploy lane verifies through the new route instead.
deploy-market writes the binding from MARKET_ATTEST_SECRET and fails loudly
when it is missing, because the verification step fails closed without it.
2026-09-24 08:43:50 +08:00
zhu1090093659 f6c8fb2e87 feat(market): pin market content through the satellite submodules
The skin, pet and community content repositories are now git submodules
under satellites/, so the commit a branch records for them is the pin the
market build reads. market-inputs.lock.json drops repo and sha and keeps
only what git cannot express: which submodule carries an input and where
its content sits inside it.

market-fetch-inputs.mjs resolves the repository URL from .gitmodules and
the commit from the gitlink, materializes the content directory from a
submodule working tree that sits on that commit, and otherwise downloads
that commit as a tarball, so a clone that never initializes the
submodules still builds the same content without fetching history.

The test-standards and emoji audits skip satellites/ the way they skip
.market-inputs/, the deploy lane also triggers when a gitlink moves, and
the owning note records that its rejection of submodules was about
mounting them as workspace packages.
2026-09-24 08:42:24 +08:00
zhu1090093659 8b5ce59838 fix(scripts): name the deploy lane's edge refusals as a vantage policy
Six Deploy Market runs in a row report the same ~150 paths as missing after a
successful deploy. They are not missing: those exact paths answer 200 with the
committed byte lengths from a residential network, from two public cloud
fetchers, and across a full 3075/3075 local sweep, and the serial re-check this
lane now runs recovered none of them after a three-minute probe. The edge
refuses the GitHub runner range for those requests, which is a policy on that
vantage rather than a verdict about the assets.

Say so in the output when every failure is such a refusal, so a red lane on a
market push is read as the zone's policy on the runner range and not as a
broken deploy. The artifact verdicts stay as they were: a missing path, a
truncated file and any other status still fail the lane.
2026-09-24 05:05:15 +08:00
zhu1090093659 abc292fb75 fix(scripts): believe only the asset statuses the edge repeats
The deploy lane's post-deploy sweep walks 3075 paths from a GitHub runner, and
Cloudflare answers a run of them with 403 until its rate window resets. Five
consecutive Deploy Market runs reported the same 150 paths as missing, while
those exact paths answer 200 with the committed byte lengths from another
network and a local sweep passes 3075/3075 at concurrency 32.

Retry the transient statuses inside the burst as before, then re-check the
paths that still fail one at a time: probe the first until it answers, inside a
budget, so a window still in force is not read as a verdict; walk the rest
serially; and stop on a run of consecutive failures so a real outage is still
reported promptly instead of being walked path by path. A status the origin
means and a size mismatch remain verdicts and are never re-checked.
2026-09-24 04:54:02 +08:00
zhu1090093659 0005ef1c09 fix(scripts): retry transient edge statuses in the asset sweep
The deploy lane's post-deploy sweep walked 3075 paths at concurrency 8
and the edge answered a run of them with 403, which the lane reported as
150 missing assets. The assets serve: probing the same origin returns
200, and a sweep at lower concurrency passes 3075/3075.

Retry 403/408/429/5xx with backoff before believing a status, allow
three attempts, and lower the default burst to four requests. A status
the origin means (404) is still reported on the first answer, and a
transient status that survives every attempt is reported unchanged, so
the assertion is not weakened, only made resilient to the rate window.
2026-09-24 01:39:55 +08:00
zhu1090093659 98a796acd8 fix(e2e): cover only this workspace in the family-dir mount override
The plugin-mount lane packs every packages/dsh-* directory and passes
FAMILY_TGZS_DIR, which rewrote every @linxin666/* dependency to a local
tarball and failed with a missing tarball for the three satellites: they
are family-scoped but not built here any more.

The override now covers what this repository builds. A family-scoped
dependency that is not a workspace package keeps resolving from the
registry, and a workspace package the directory fails to cover is still
a loud failure, so the lane's guarantee is unchanged for the packages it
actually packs.

Reproduced the lane locally before and after: 16 workspace tarballs
rewrite to file:, the three satellites stay on npm, and the mount smoke
passes.
2026-09-24 00:09:19 +08:00
zhu1090093659 24dcb12c00 fix(test): tolerate a missing packages/skins root in the walkers
The extraction left packages/skins/ with no tracked file, so it does not
exist in a fresh clone: git does not carry empty directories. Two test
walkers still readdirSync'd the root unconditionally and died with
ENOENT in CI. A stale untracked skin-center/node_modules in the
development checkout kept the directory present and hid this locally,
which is why the failure only appeared on the runner.

Guard both walkers the way the shared family-package walker and
coverage-gate already do. Verified on a clone-like tree with the
directory removed: script tests, aggregate check, libs check, docs,
i18n, standards, the full suite, and the mount smoke all pass.
2026-09-24 00:02:11 +08:00
zhu1090093659 58f01b72e2 fix(ci): resolve satellite dependencies by spec, not by exact version
Four gates were left broken by the extraction. Every one of them passed
before the split, and each failed only because a script or a dependency
shape that the split changed was still assumed to be the old one.

The release pipeline and the market deploy lane called pnpm
skin-center:check and pnpm community:check, scripts that live in the
satellite repositories now; the tag pipeline would have failed before
reaching npm. Root package.json still declared skin:new against a
deleted script.

The mount smoke's auto-mode registry probe compared the dependency
string against the registry's version map. That held while every family
dependency was a workspace protocol entry, which pnpm rewrites to an
exact version when packing, but the three satellite rows are semver
ranges, so an already-published ^0.3.24 looked unpublished and the gate
tried to substitute a workspace tarball that no longer exists. The probe
now evaluates the spec: exact versions and ranges both resolve against
the published list, and semver is a declared devDependency for it.

market-layout.test.mjs asserted that every skin points at the catalog
path when the catalog input is absent, but skins may declare an upstream
sourceUrl; the assertion now runs only where it can be true, and the
strict comparison still runs when the catalog has been fetched.

Verified: mount smoke green on the registry path with all three
satellites resolving from npm, test:scripts green with and without
fetched market inputs, and the full suite green.
2026-09-23 23:53:14 +08:00
zhu1090093659 0ad0d9e5ab docs(notes): record the satellite-repository decision
Adds the Agent Note for the split (problem, decision, the external-row
trade, the commit-pinned market content, the release-order constraint, the
alternatives that lost and the consequences) as a bilingual triplet, and
updates the aggregate-positioning note, whose frozen-identifier list still
named the packages/skins/skin-center directory that no longer exists here.

Also clears the leftovers the split made stale: market-build's path comments
and its "no skins found" error now name .market-inputs and the fetch script,
the plugin template comment points at the dsh-skins repository, the labeler
routes skin/pet content changes to the lockfile instead of removed package
globs, the wallpaper/renderer review route keeps only its title matcher (its
paths moved to dsh-skins), the READMEs link the community index and the pet
notices on their new repositories, and the test-standards baseline drops the
entries for the removed packages' test files.
2026-09-23 23:22:15 +08:00
zhu1090093659 9004d0ee7d refactor: consume the extracted plugins instead of carrying them
The skin center, pet and community-index plugins now live in their own
repositories and are consumed as published npm packages, so this tree drops
them and everything that only existed to serve them in-repo.

Removed: packages/dsh-pet, packages/dsh-community-plugins and
packages/skins/skin-center (history stays in this repository), plus the root
scripts that moved with them (skin center catalog check, the reviewed-hooks
registry, the skin and pet authoring CLIs and their tests, community-index).
scripts/skins-montage.mjs stays: it renders the root README collage from
market/dist, which is this repository's tooling.

Decoupled:
- scripts/sync-shared.mjs no longer mirrors the settings trio, host helpers or
  http/mount-once copies into the three packages (114 copies -> 99);
- lib-artifact-fingerprints.json tracks three committed lib/ packages now that
  skin-center left;
- packages/dsh-usage drops an unused @linxin666/dsh-pet devDependency;
- the skin/pet/community gates leave ci.yml and the root scripts (they run in
  the satellite repositories' own CI);
- scripts/i18n-audit.mjs no longer reads pet's client locale file, and the
  test-standards and emoji audits skip .market-inputs (fetched content is not
  ours to audit);
- the market build pins the community index in market-inputs.lock.json like the
  skin and pet content, so the plugin list the site serves is reproducible from
  a commit instead of following whatever npm resolves, and the catalog's skin
  repo URLs point at dsh-skins;
- docs (root AGENTS.md, architecture, plugins, development, publish-prep) and
  CONTRIBUTING point at the new repositories.

Gated by: aggregate:check, libs:check, runtime-deps:check, market:check,
sync-shared:check, test:standards, docs:check, i18n:check, emoji:check,
typecheck and 307/307 script tests.

Not yet safe to merge: the aggregate's three dependencies resolve from npm,
and the published 0.3.24 still injects the retired settingsScope service, so
the mount smoke stays red until the satellite repositories publish a build
containing the migration.
2026-09-23 23:19:08 +08:00
zhu1090093659 efc0618d3e feat(market): pin the content sources and verify the deployed assets
The market build no longer reads the skin and pet assets out of this
monorepo. market-inputs.lock.json pins the dsh-skins and dsh-pet commits,
scripts/market-fetch-inputs.mjs unpacks just the content directory of each
pinned commit into .market-inputs/ (idempotent, --check verifies without
downloading, and a missing or stale input fails the run instead of shipping a
partial catalogue), and market-build reads the content from there. The
community index and the skin-center CSS pipeline are resolved through the
aggregate's dependency tree rather than a repository path, so both keep
working once the packages leave the monorepo; the in-repo paths stay as a
fallback for a checkout that still carries them.

market-build reproduces the committed market/dist byte for byte from the
fetched inputs, so the pipeline change is behaviour preserving.

scripts/market-verify-assets.mjs walks every path the emitted manifests
advertise and checks it against market/dist or a deployed origin, comparing
the served size with the local dist file. It uses a one-byte ranged GET
because the Workers static-asset layer answers HEAD with content-length: 0,
which would report every asset as truncated. Wired into deploy-market.yml
after the deploy; a full sweep against https://dsh-market.com passes for all
3075 advertised paths (238.5 MiB).
2026-09-23 23:07:22 +08:00
zhu1090093659 9bfb650eb1 feat(dsh-web-all): mount the extracted satellites as npm rows
The skin center, pet and community index are consumed as published npm
packages instead of in-repo children: aggregate.yml moves the three from
patchFrom/deps to rows:, package.json declares explicit semver ranges, and
tombstones keep ./pet, ./skin-center and ./community-plugins resolving to
the shared shell re-export so a profile written before the split still
imports instead of throwing ERR_PACKAGE_PATH_NOT_EXPORTED.

Row ids stay byte-identical (web-ui-*), so existing profiles need no
migration. External rows mount the real package name directly: they carry
no fault-isolation shell and their client half is no longer inlined into
this package (lib/client.js 2.63 MB -> 2.44 MB, fingerprints refreshed).

Pinned by tests/satellite-rows.spec.ts (row ids, direct mount, semver
range, the ./package.json export aggregate.mjs resolves the row through,
compat tombstones, remote-web-ui-before-pet ordering, no inlining) and by
allowlist.spec.ts resolving each satellite from both spellings.

Verified end to end with scripts/e2e-mount.sh against local family tarballs
(FAMILY_TGZS_DIR): the published 0.3.24 still injects the removed
settingsScope service, so the registry path stays red until the satellites
release the migrated build.
2026-09-23 22:59:24 +08:00
zhu1090093659 65de1ace9a chore(sdk): advance the plugin cohort to 0.1.7-rc.1
Move every consumed @deepseek-ai family specifier (272 across 23 manifests),
the dsh.engines.dsh floors and the matching @deepseek-ai/dsh host peers, the
plugin scaffold, the release-age ledger and the packageExtensions keys, the
desktop host payload pin with its regenerated hoisted lock, the root lock,
the CI and release mount pins, the README host badge, the cohort-version
source literals and the cohort prose in docs.

Regenerate the official token contract from the 0.1.7-rc.1 surfaces (293 to
299 names, additive, contractVersion stays 1) and verify the browser platform
seed against the rc.1 shell bundle. Harden the shell-isolation real-boot spec
so it only accepts an app-boot copy whose own non-optional peers resolve: this
cohort pulls an incomplete copy into the workspace for the first time, through
dsh-agent-preset-registry.

Record the cohort decision, the official release-notes and compare evidence,
the published-surface delta and the native-first overlap inventory in
.agents/notes/implemented/architecture/2026-09-23-sdk-cohort-0.1.7-rc.1.
2026-09-23 22:11:06 +08:00
zhu1090093659 ded1050a35 fix(sidebar): seat the usage glance above Settings, share the foot row
The usage foot card moves from below the Settings row to directly before it
and adopts the shell's row geometry instead of card chrome (no border, hover
fill, 36 px collapsed strip). The remote control package lays the wide
desktop foot out as one wrapping row so the Settings trigger shares its line
with the action pair, while the collapsed rail keeps the shell's stacked
foot. README pairs, the package description, the aggregate client bundle and
the committed lib fingerprints move with the two changes.

Notes:
- .agents/notes/implemented/feature/2026-09-23-usage-foot-card-above-settings.md
- .agents/notes/implemented/architecture/2026-09-23-remote-desktop-foot-row.md
2026-09-23 21:22:30 +08:00
zhu1090093659 f8e9d2fd8e feat(packages): declare the DSH host as a family peer dependency
The family stated which DSH host it needs only through dsh.engines.dsh, a
field the plugin manager reads; nothing in the npm manifest told a resolver
that the requirement existed. Every family package, the aggregate included,
and the plugin scaffold now also carry
peerDependencies["@deepseek-ai/dsh"] at the same >=<cohort> literal.

- scripts/family-dsh-engines.test.mjs covers both declarations over every
  family package plus the scaffold: presence, the supported >=<semver> form,
  and no declaration trailing the scaffold cohort floor.
- scripts/aggregate.mjs keeps the host peer when it rebuilds the aggregate
  manifest, still dropping leftover child-plugin peers.
- docs/plugins.md and packages/AGENTS.md state the rule; the host-floor Agent
  Note records the new surface and the extra cohort-bump move.
- The personal dsh-sdk-upgrade skill now names the host peer as the sixth
  host-floor surface to move with every cohort (outside this repository).
2026-09-23 20:38:05 +08:00
zhu1090093659 037b928d83 test(scripts): hold every family DSH floor to the declared cohort floor
family-dsh-engines.test.mjs asserted only that each family package declares a
supported >=<semver> dsh.engines.dsh form, so a package could silently drop
below the cohort the family is built against. It now also requires every
family floor to satisfy the scaffold cohort floor via rollout-verify's
satisfiesFloor, and pins that the family walker covers the aggregate package
packages/dsh-web-all, whose floor is the family's machine-readable statement.

docs/plugins.md records the aggregate coverage and the value check.
2026-09-23 20:28:52 +08:00
EDDYCRAZY-CC 0da5046cd2 feat(usage): seat a collapsible price glance card below the sidebar Settings row
The 2026-09-18 removal left the usage overview reachable only from the
settings section. The user asked for the sidebar card back, redesigned at
the bottom-left below Settings. The new surface is a glance plus gateway,
not the retired panel: one card with a price-first headline (today's
estimated spend, falling back to today's tokens when nothing priced is
recorded), a tokens/calls line, and up to two configured-provider
balances. The card body opens the settings panel on the usage section;
a corner chevron folds it into a one-line strip persisted in localStorage
(dsh-usage.foot-card.collapsed).

- UsageFootCard.tsx + foot-card-mount.tsx: the mount appends the container
  as the shell foot area's last child (no slot exists below
  sidebar.settings) and self-heals through the shared body-mutation hub;
  the card shares the section's store/poll and runs its own 30 s
  visible-tab loop; it bows out while disabled or while the host answers
  404, and hides in the 56 px rail.
- New zh/en keys usage.foot.* with ru mirrors; the body-mutations
  sync-shared target returns for dsh-usage.
- The semantic-attrs contract gains the foot-card part rows and drops the
  stale usage sidebar anchors; the README pair, the package description,
  and the agent notes move with the change (the new foot-card note
  partially supersedes the removal note, which keeps its decision with a
  cross-link).

Live GUI evidence (Edge via Playwright against the running host at
127.0.0.1:3080): the card seats as the foot area's last child below the
settingsArea (256x96 expanded, 256x28 collapsed), the collapse choice
survives a reload, clicking the body opens the settings panel with the
usage section active, and the card hides in the collapsed rail; zero
console errors.
2026-09-23 19:48:33 +08:00
zhu1090093659 8e852cc271 fix(skin-center): adapt skin rendering to the dsh 0.1.7 shell layout
dsh 0.1.7 keeps the right sidebar panel shell mounted and visible while
closed, so ungated skin paints plated it as a phantom column covering the
conversation background art. Gate blue-fantasy right-panel fills on
[data-sidebar-right-open] and strip paint from the closed shell in the
shared shell-rendering CSS so every skin is protected. Re-anchor the
semantic adapter on 0.1.7 hooks (centerCol class suffix, data-rightbar-col,
data-composer-input) and extend the wallpaper exclusion list accordingly.
2026-09-23 18:38:36 +08:00
zhu1090093659 62450aedeb fix(host): request identity encoding from every remote fetch
The alpha.2 host import of npm undici costs Node built-in fetch its

content-encoding decompression. shared/host/http.ts now owns the rule as

withIdentityEncoding(init), and every host fetch that parses a remote body

adopts it: the market installer (manifest and assets), the plugin-manager

npm-registry probe, the remote-web-ui registry/GitHub probes and inner

app-shell fetch, and the usage provider probes.
2026-09-23 18:28:54 +08:00
zhu1090093659 5332d1bdf3 fix(market): keep Workshop installs working on the alpha.2 host fetch
The alpha.2 host imports npm undici through @deepseek-ai/dsh-http-proxy.

That import replaces the legacy undici global dispatcher Node built-in fetch

reads, and the cross-major wrapper loses content-encoding decompression, so a

plain fetch() returns raw brotli/gzip/zstd bytes. The market installer now

requests accept-encoding: identity for the manifest and every asset, so the

install never depends on the host fetch decoding a body.
2026-09-23 18:14:09 +08:00
zhu1090093659 53ec2ed18e fix(aggregate): drop the retire override for a row alpha.2 no longer mounts
DSH 0.1.7-alpha.2 no longer mounts ui-settings-unarchive-sessions, so the
aggregate's retire override named a foreign row no bundle inserts and made
every profile boot log a patch-not-found warning. The retire mechanism
stays in scripts/aggregate.mjs for a foreign row that genuinely exists;
declaring a target again requires proving it in the same cohort with
dsh --profile <name> --dump-config. dsh-session-archive's takeover facts
(module comment, spec header, bilingual README) and the two cohort notes
are updated to the same-cohort reality.
2026-09-23 17:19:59 +08:00
zhu1090093659 9cddebd6d6 test(standards): ratchet the violation baseline down after the package removals 2026-09-23 17:05:26 +08:00
zhu1090093659 072e8fe99e ci(plugin-mount): pack family tarballs from the checkout for the mount smoke
The lane's auto mode resolved published family tarballs built against an
older cohort (skin-center still waits on the removed settingsScope
service), so the scratch profile never activated. Pack every family
package from the checkout into FAMILY_TGZS_DIR before the smoke so the
lane tests what the branch ships; release.yml keeps registry semantics.
Also make the empty-array expansion in e2e-mount.sh safe under macOS
bash 3.2 set -u.
2026-09-23 17:05:26 +08:00
zhu1090093659 ef637f657d feat(aggregate)!: remove dsh-doctor and dsh-tool-describe-image from the family
Both plugins are no longer useful and leave the family per owner direction.
The packages are deleted from the workspace; the aggregate drops their
patchFrom/deps rows and inactive entries, keeps one tombstone shell export
per removed id so old profiles degrade to an inert plugin instead of
ERR_PACKAGE_PATH_NOT_EXPORTED, and drops the stale retire block whose
target row the 0.1.7-alpha.2 host no longer mounts. The reference sweep
covers sync-shared targets and counts, the i18n audit, the ru dictionaries,
the web-settings allowlist, plugin-manager fixtures, the skin-center
semantic-attrs contract, the labeler, coverage baseline, lockfile, and
docs; desktop/runtime/profile-web stays pinned to the published 0.3.19
that still contains both packages.
2026-09-23 17:05:26 +08:00
EDDYCRAZY-CC c1cba19515 fix(web-all): lock the viewport root for installs with no active visual
An install with no catalog skin, custom theme or wallpaper had no viewport
lock: the skin-center stylesheet that owns one is scoped to
html[data-dsh-skin] / html[data-dsh-custom-theme]:not([data-dsh-skin]) /
html[data-dsh-wallpaper-active], so html and body kept their inherited
overflow and the document stayed scrollable.

Two things then displaced the page. The mobile frame recipe pinned
[data-dsh-frame] to height: 100dvh next to padding-bottom:
env(safe-area-inset-bottom), so under content-box sizing a device with a home
indicator measured the frame at 100dvh plus the inset and carried the
difference as document overflow. And every conversation disclosure control
(the tool/step-process collapse bar carrying the step summary, the whole-turn
process bar) calls focus() on itself when toggled: a focused element below a
scrollable document scrolls the page to it, which pushes the titlebar and
sidebar top out of the viewport and leaves a blank band at the bottom
(issue #1135's symptom, reachable with no visual active).

RESPONSIVE_CSS now owns the unconditional lock, scoped through :has() so it
stays inert until the shell frame exists and never touching the app root
element whose own lock was removed for clipping the sidebar foot and
overriding the side-panel column push (#1222, #1225), and the mobile block
gains box-sizing: border-box with max-height: 100dvh so the safe-area padding
sits inside the pinned height.

Verified against a scratch dsh web host with the packed aggregate and no
active visual: at 1280x800 and 390x844 the document equals the viewport,
html and body report overflow: hidden, and focus() on an element placed below
the fold leaves the document offset at 0 with no page or console errors.
Deleting exactly the two new rule sets on the loaded page restores
overflow: visible and the 34px of document overflow that the same focus()
scrolls by. Package tests, typecheck, test:scripts, test:standards,
docs:check, i18n:check, emoji:check, libs:check, aggregate:check and
runtime-deps:check pass.
2026-09-23 16:25:53 +08:00
EDDYCRAZY-CC db9ae37636 fix(scripts): stop dsh-skin and e2e-mount from deleting the ambient DSH home
`dsh-skin uninstall <id>` joined raw argv under `$DSH_HOME/skins` and then removed the
result recursively, so `uninstall ..` took the whole home and `uninstall ../..` the OS
user directory. The id is now validated against the skin-center contract and the
resolved target must stay under the user skins root; scripts/dsh-skin.test.mjs pins the
refusal plus the survival of the home beside the skins dir and of the installed skin.

`e2e-mount.sh` took `DSH_HOME_BASE` verbatim as its scratch root and ran
`rm -rf "$SCRATCH"` on exit, so `DSH_HOME_BASE=~/.dsh pnpm test:mount` deleted the home.
It now refuses a root resolving to /, $HOME or $HOME/.dsh, and removes only the run's
own home/ and workspace/ subdirectories when the caller supplied the root; a root the
script created with mktemp -d is still removed as before.

The agent note records the audit behind both fixes: the restore-then-delete teardown in
packages/dsh-task-board/tests/host-apply.spec.ts (already fixed in 7c513ad5), the
per-package delete surfaces that stay inside their own $DSH_HOME subdirectory, and the
decoy-home gate run whose 16 marker files came out untouched.
2026-09-23 16:20:17 +08:00
zhu1090093659 210a9aad70 fix(skin-center): cover the alpha.2 official token surface in the fallback guard
Regenerate the official token contract from both surfaces that define it
(dsh-client-ui-theme lib plus the dsh-web-frontend dist bundle) instead of
the shell bundle alone: the alpha.2 shell inlines far fewer definitions,
so the shell-only snapshot had been contracting while the real surface
grew. The contract goes from 278 to 293 non-static custom properties with
no loss; only --dsw-alias-code-diff-added and --dsw-alias-code-diff-deleted
are new in alpha.2, the other 13 were already defined and simply missing.

Match excluded roles by role word rather than by prefix, because the
official surface names state colors inconsistently (state-error-primary
next to label-error and interactive-bg-hover-danger), and keep
deriveFallbacks opt-in so the tint guard never changes market output.

Rebuild the committed lib, refresh the artifact fingerprints, and correct
the compatibility scan report whose earlier conclusions this supersedes.
2026-09-23 15:45:09 +08:00
zhu1090093659 66ba8ede6e chore(sdk): advance the plugin cohort to 0.1.7-alpha.2
Bump every @deepseek-ai family specifier (258 specifiers across 25
manifests), every dsh.engines.dsh floor (22 packages), cordis to ^4.0.4
and schemastery to ^3.18.4. Align the shared ledger, the desktop host
runtime seed (overrides, exclude list, regenerated lockfile) and the root
lockfile, plus the CI and release mount pins, the cohort-version docs and
the source literals that name the cohort.

The floors had been written as ">=>=0.1.7-alpha.2"; repair them to
">=0.1.7-alpha.2" so the family-engine regex gate passes again.

dsh-market/lib is rebuilt because its committed bundle embeds the resolved
cordis and schemastery paths; the Skin Center output is rebuilt in the
token-contract change that follows.
2026-09-23 15:45:08 +08:00
zhu1090093659 4a57386743 feat(dsh-pet): persist pet selection without a served host form
Aggregate shell rows expose no pet Config form, so the settings card now
detects the missing form, reads the selection from /api/pet/state, and
saves it through /api/pet/set-pet with a state read-back, showing only
the pet selector in that mode. The petId Config field loses its schema
default so an absent profile choice keeps the persisted pet.json
selection across restarts. Rebuilds the dsh-web-all aggregate client
bundle, refreshes lib fingerprints, and records the behavior in the
sdk-cohort owner note (en/zh + pair record).
2026-09-23 11:31:10 +08:00
zhu1090093659 e93bc4eba8 chore(alpha): merge latest alpha changes into dev
# Conflicts:
#	scripts/lib-artifact-fingerprints.json
2026-09-23 10:27:22 +08:00
zhu1090093659 5106ea7102 chore(plugin-manager): trim trailing CSS blank line 2026-09-23 10:20:28 +08:00
EDDYCRAZY-CC dd2e02c157 fix(skin-center): fix orca-link ptc scroll jump and settings sidebar alignment 2026-09-23 10:19:05 +08:00
zhu1090093659 a46c52dad8 chore(alpha): merge latest alpha changes into dev 2026-09-23 10:17:11 +08:00
EDDYCRAZY-CC 87203e9b36 fix(settings): gracefully render form fields and suppress unexposed banner for web plugin cards 2026-09-23 09:59:27 +08:00
zhu1090093659 c5cd120033 chore(alpha): merge local alpha commits into dev 2026-09-23 09:58:52 +08:00
EDDYCRAZY-CC be8077983b fix(market): hide notExposed banner when renderChildrenWhenNotExposed is enabled 2026-09-23 02:41:28 +08:00
EDDYCRAZY-CC b7012f5b45 fix: resolve workshop missing namespace, streaming flicker, and liangshen restore target 2026-09-23 02:33:18 +08:00
EDDYCRAZY-CC 4e8d6f6cc7 fix(settings,skin-center): fix wallpaper and theme saving on 0.1.7 settings forms
- Exempt skin-center from aggregate fault-isolation shell in scripts/aggregate.mjs so its native Config is scanned directly by the Host Loader
- Preserve multi-segment path arrays in dsh-web-settings compat scope mutate and writeBatch to prevent flatten-key errors on nested volatile fields
- Fall back to candidate profile entry ids in skin-center bindConfigForm when resolving native configForms
- Regenerate aggregate patch, recompile lib/ bundles, and update lib fingerprints
2026-09-22 23:46:03 +08:00
EDDYCRAZY-CC c33de63e5a fix(settings): import the pre-cohort family settings sections the Host orphans
The Host's own legacy import moves $DSH_HOME/settings.yaml into the profile
configuration section by section, using the section name as the entry id. Family
row ids are ui-pet / web-ui-pet, ui-liangshen / web-ui-liangshen, web-ui-usage
and so on, so a pre-cohort family section matched no entry: it was logged as
rejected and stayed orphaned in the renamed document, and every family setting
silently reverted to its schema default.

dsh-web-settings now imports those sections itself, once, after the composition
settles:

- A section the family alias table knows resolves to the served entry that
  family namespace belongs to; otherwise a section named exactly like a
  top-level Config field declared by exactly ONE served entry resolves to that
  field (the Skin Center declares skin-background, skin-custom-theme and
  skin-wallpaper as top-level fields of one entry). Two or more declaring
  entries, or none, leaves the section alone.
- Both rules place the value by the same decision: inside the field when the
  resolved entry declares one, at the entry root otherwise.
- Only paths the entry's own user layer does not already carry are written, so
  an existing value is never overwritten and a re-run is a no-op; writes go
  through the official settings.update(entryId, patch, revision) path.
- Every section that lands is recorded in
  $DSH_HOME/dsh-web-settings-legacy-import.json as soon as its write succeeds,
  so a partial failure records nothing it did not write and a value the user
  later clears is not resurrected on the next boot. An unreadable or
  unknown-version marker makes the import a no-op rather than a re-import.
- The import never rejects into plugin activation and degrades to doing nothing
  when the Host serves no settings surface or no config editor.

Also in this change: the yaml dependency the import reads the legacy document
with, the preset-center Russian wording aligned with the zh/en enable/disable
vocabulary for the state and action keys, two comments that still named the
deleted @deepseek-ai/dsh-agent-presets package or its discovery root, and the
cohort note updated for the migration and for the autoGenerate fact it had
guessed at.
2026-09-22 22:55:22 +08:00
EDDYCRAZY-CC 5daf6942e6 chore(sdk): move the family to the 0.1.7-alpha.1 cohort on the permanent alpha branch
The official family published 0.1.7-alpha.1 while the alpha branch stood on
0.1.6-alpha.2, and the machine's DSH moved to the new cohort. Four changes in
the release reach this family:

- The settings subsystem is now keyed by the profile entry: ctx.settingsScope,
  SettingsScope* and SettingsProvider.installSection/register are gone, replaced
  by ctx.configForms, ConfigForm* and SettingsForms. Every family host half
  carries its settings on its own Config with volatile fields, every browser
  half binds through the webUiSettings service (which resolves a family
  namespace to the owning profile entry id and then binds natively, with the
  loopback bridge as the fallback), and the shared card/form layer speaks the
  new contract including its boolean refusal answers.
- The agent-preset domain became declarative: directory discovery is gone, a
  preset is a declaration a plugin registers at runtime. dsh-preset-center now
  declares installed presets from its host half (install = declare) and
  dsh-liangshen declares its own preset instead of syncing files into
  ~/.dsh/.agent-presets.
- Session V4 moved the tool-result failure flag to the message root; dsh-pet
  read the removed content block and silently rendered failed turns as
  successes.
- The client design system renamed its icons and reshaped SessionListState;
  the affected call sites and fixtures follow.

Cohort: every consumed @deepseek-ai/dsh-* range, the scaffold, the README
badges, the CI and release mount-smoke pins, the desktop runtime pin and both
lockfiles, and the docs move together; the vendor pins follow the cohort's own
release (cordis 4.0.3, cosmokit 1.8.4, schemastery 3.18.3,
cordis-plugin-include 1.0.8, cordis-plugin-loader 1.0.4). The workspace
packageExtensions restore @deepseek-ai/dsh-util-workspace-path, which
dsh-client-ui-primitives now imports from its emitted lib while declaring no
dependencies.

scripts/e2e-mount.sh, scripts/e2e-mount-rewrite and its test, and
scripts/publish-legacy-aggregate.mjs pass GNU tar --force-local on the
Windows/MSYS lane, where a C:\ output path is otherwise read as a remote host.

The mount smoke does not pass on this workstation: the local dsh CLI is a
symlink into a DSH checkout whose built lib/ predates this cohort's own
multi-file dsh.bundle.patch support, so profile boot crashes on the official
dsh-web-app array before any family row is evaluated. That is host-side build
staleness outside this repository; the lane passes once the DSH checkout is
rebuilt. Recorded with the rest of the decision in
.agents/notes/implemented/architecture/2026-09-22-sdk-cohort-0.1.7-alpha.1.md.
2026-09-22 22:18:38 +08:00
zhu1090093659 8aa2ec64e7 Merge origin/dev into dev
Sync the integration branch: nine upstream commits (remote-web-ui channel
gate for issue #1665, skin-store traversal fix, desktop runtime payload,
community/plugin registry, issue-template enforcer, contributor list) merged
into local dev while keeping the five local remote-web-ui commits.

Conflict resolution in packages/dsh-remote-web-ui:
- Adopt origin/dev's unconditional /remote pairing gate: an unpaired caller
  is refused before any byte or upstream hit, and requirePairingForLan is no
  longer an authorization input for the channel (it still selects the
  transport and governs the plain /api surface). The local
  policy-conditional variant and its two contradicting tests are dropped,
  and the README security-model bullets follow the merged behavior.
- Keep the local work: one-time landing grant, server-granted host mode,
  tunnel and update lifecycle fixes, mobile adaptation, and the WebSocket
  query credential note.
- Regenerate README.i18n.yaml, packages/dsh-web-all/lib/client.js(.map) and
  scripts/lib-artifact-fingerprints.json from the merged sources.
2026-09-22 20:24:56 +08:00
zhu1090093659 ba488e4cd7 fix(remote-web-ui): close the remaining audit findings across host and browser halves
Security and resource:
- the named-tunnel account token no longer rides the child argv (TUNNEL_TOKEN for
  the spawn call only); a cloudflared binary that never validates is not
  re-downloaded on every restart round (install cap of 3 per process).
- a revocation that cannot be persisted removes the stale device store instead of
  letting a restart restore the revoked session.
- disabling the plugin stops the tunnel, the relay registration and the public
  base; named mode publishes its fixed hostname before the tunnel runs.
- the posture probe treats the harness 401 (fence passed, browser auth refused) as
  gated rather than open; unpaired status callers no longer receive the private
  LAN literals.
- manifest package names are validated before reaching the win32 update command
  line; the POSIX update timeout kills pnpm's process group; update output is
  decoded once per candidate.
- update status probes are memoized (60s) and shared, and the plugin no longer
  carries the dead 'already used' (409) accept code.

Browser half:
- official nodes are resolved through an isConnected-invalidated cache (7 -> 1
  document-wide sweeps per 600ms tick); the swipe veto is judged at release
  instead of on every touchstart; the whale is re-clamped on a viewport change;
  the global focus patch is removed when the layer is disabled.
- attach() copies a caller-owned Headers instance; the QR symbol is memoized on
  the link; the LAN-bind poll stops on a loopback-only 401/403.

Copy and docs: the panel/README now state the real token window (time-limited,
several devices per link) in zh/en/ru, and the WebSocket device-query residual is
documented in the security model.

Rebuilt the dsh-web-all client bundle and re-recorded the lib fingerprints; the
Agent Note for this pass records every decision and the remaining ticket design.
2026-09-22 19:51:59 +08:00
EDDYCRAZY-CC 03c3b712c7 Merge remote-tracking branch 'origin/dev' into alpha
# Conflicts:
#	scripts/lib-artifact-fingerprints.json
2026-09-22 19:18:59 +08:00