dshm-compat 0.1.3 → 0.1.5

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/AGENTS.md ADDED
@@ -0,0 +1,17 @@
1
+ # dshm-compat development rules
2
+
3
+ ## No per-plugin adaptation
4
+
5
+ - **Never** add a production branch, recipe, rule, or hand-built artifact for one
6
+ package name. `dshm-compat` must adapt plugins through its generic engines.
7
+ - S2/native plugins go through `lib/s2-native.js`: detect the worker and the
8
+ standard runtime/spawn patterns, stage the worker, normalize the manifest, and
9
+ validate the result. Package names may appear only in tests and documentation
10
+ examples, never in `if (name === ...)` logic.
11
+ - If a plugin has a genuinely unique protocol, expose a **generic extension
12
+ point** in the plugin engine (patterns, hooks, or sidecar descriptor), not a
13
+ one-off patch.
14
+ - `dsh-ops` is a regression fixture. It proves the generic S2 adapter; it is not
15
+ a special case.
16
+ - Deliverables are the plugin and its generic tooling, not a converted tarball
17
+ for a single plugin.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,82 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.5
4
+
5
+ - Spawn rewriting now uses `['bash', file]` plus a staged shell wrapper instead
6
+ of `bash -c 'exec "$0" "$@"'`. `dshm-vm-bash` drops positional parameters for
7
+ `-c`, so the old form produced `Permission denied` and lost worker arguments.
8
+ - `runGuestFile` and sidecars use the same non-`-c` path (`bash <wrapper> <args>`).
9
+ - Release acquisition tries the package-version tag first, then the latest
10
+ release, falls back to the API asset endpoint, verifies `sha256` when
11
+ available, and returns attempts instead of a silent failure.
12
+ - Source-only packages return `needs-build` with an external build contract;
13
+ when the same package has a published npm build, `convert` automatically
14
+ retries that build instead. Verified with `dsh-ops` 0.4.0 GitHub source:
15
+ it falls back to `dsh-ops@0.3.0` and produces a validated package.
16
+ - Added `compat_build`: a source build planner for `Cargo.toml`/Node
17
+ `build`/`prepack` packages, plus an opt-in `backend:"guest"` attempt that
18
+ provisions Rust or Node in the Alpine guest and runs the build there. The
19
+ guest path depends on guest network/disk and is explicitly experimental.
20
+ - Added `compat_uninstall` and `engines.dsh` normalization.
21
+ - Offline regression suite: 30/30.
22
+
23
+ ## 0.1.4
24
+
25
+ - The generic S2 adapter now accepts **x86_64** workers: it bundles
26
+ `qemu-x86_64` and a minimal x86_64 glibc rootfs, stages a shell wrapper, and
27
+ runs the worker under QEMU in the guest. No AArch64 rebuild is required for
28
+ plugins that publish a `linux-x64` runtime.
29
+ - If the package contains no worker, the adapter looks at the GitHub repository
30
+ release assets, prefers `linux-arm64` then `linux-x64`, extracts the archive
31
+ (ZIP or tar.gz), and uses the first AArch64/x86_64 ELF. No package name is
32
+ special-cased.
33
+ - Source-only native packages (for example a Rust `native/` tree with no
34
+ prebuilt runtime) now classify as S2 instead of S0, so the native adapter gets
35
+ a chance to build or acquire a worker.
36
+ - Verified end-to-end: `compat_convert("https://github.com/T-Auto/dsh-ops")`
37
+ with **no workerPath** acquires the upstream `linux-x64` release asset, wraps
38
+ it in QEMU, patches the runtime/spawn/peer/os-cpu gates, and validates.
39
+ - Spawn rewrite now uses `['bash', file]` and stages a shell wrapper around the
40
+ real worker instead of `bash -c 'exec "$0" "$@"'`. `dshm-vm-bash` drops
41
+ `$0`/`$@` for `-c` commands, which caused `Permission denied` and lost
42
+ arguments; the non-`-c` path preserves them.
43
+ - Release selection first tries the tag matching the package version (`vX.Y.Z`),
44
+ then the latest release with assets; missing tags fall through instead of
45
+ returning an empty asset list.
46
+ - Release download falls back from `browser_download_url` to the GitHub API
47
+ asset endpoint (`Accept: application/octet-stream`), records every attempt,
48
+ and verifies the asset `sha256` digest when GitHub provides one. Failures are
49
+ returned in `acquisition.attempts`, not swallowed.
50
+ - The adapter also rewrites resolved-shell spawns (`[file, '-c', command]` and
51
+ `[file, scriptPath]`) to `['bash', '-c', command]` / `['bash', scriptPath]`,
52
+ so the shell component does not exec an ELF from the app sandbox.
53
+ - `runGuestFile` now invokes `bash <script> <args>` instead of
54
+ `bash -c 'exec "$0" "$@"'`; sidecars stage a wrapper script so positional
55
+ arguments survive `dshm-vm-bash`.
56
+ - Source packages with no built `main` and a `build`/`prepack` script return
57
+ `needs-build` with an external `npm ci && npm run build && npm pack` contract
58
+ instead of `recipe-mismatch`.
59
+ - Added `compat_uninstall` for profile-local removal (node_modules + dependency
60
+ + bundle entry), and `engines.dsh` is stripped alongside `os`/`cpu`.
61
+ - Offline regression suite: 28/28.
62
+
3
63
  ## 0.1.3
4
64
 
5
- - Fixed dsh-ops recipe dispatch for GitHub specs: the converter now keys on
6
- `report.package.name`, not `report.resolved.name` (which is `owner/repo` for
7
- GitHub). `compat_convert("https://github.com/T-Auto/dsh-ops", ...)` now
8
- reaches the recipe and returns `needs-native-build` instead of `skipped`.
9
- - The dsh-ops recipe relaxes `peerDependencies["@deepseek-ai/dsh"]` to
10
- `workspace:*` so installing it does not make the DSHM core fail its
11
- prerelease compatibility check on the next boot.
12
- - Clearer S2 skipped message ("needs a recipe or an external native build").
13
- - Offline regression suite: 24/24.
65
+ - Added a **generic S2 native adapter** (`lib/s2-native.js`). It discovers the
66
+ worker and the runtime matrix in the package, appends the DSHM target,
67
+ rewrites `${platform}-${arch}` templates, wraps standard `const/let argv =
68
+ [file]` spawn calls in `bash -c`, removes foreign `os`/`cpu` gates and
69
+ non-Linux optional payloads, and relaxes the DSH `peerDependencies` range.
70
+ There are no package-name branches and no built-in per-package rules.
71
+ - Added a generic guest sidecar bridge (`compat_sidecar` / `lib/sidecar.js`):
72
+ stage a Linux/AArch64 worker from a converted tarball into
73
+ `$DSH_HOME/dshm-compat/sidecars/<id>`, list/probe it, and call it with one
74
+ JSON request/response through the Alpine guest.
75
+ - `compat_plan` now infers `rust`, `c/c++`, or `node-gyp` from source files in
76
+ the report instead of relying on a package-specific rule.
77
+ - S2 conversion dispatches on the strategy and the generic patterns, not on a
78
+ package name; GitHub specs work through the same path.
79
+ - Offline regression suite: 25/25.
14
80
 
15
81
  ## 0.1.2
16
82
 
package/INSTALL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Installing dshm-compat
2
2
 
3
- The artifact is a normal DSH bundle package: `dshm-compat-0.1.3.tgz`.
4
- It is host-only, has no runtime dependencies, and adds eight agent tools.
3
+ The artifact is a normal DSH bundle package: `dshm-compat-0.1.5.tgz`.
4
+ It is host-only, has no runtime dependencies, and adds twelve agent tools.
5
5
 
6
6
  ## A. DSHM (the tablet)
7
7
 
@@ -15,7 +15,7 @@ input calls `pnpm view` before installing and fails with
15
15
  `Command failed with EACCES: pnpm view ... spawn pnpm EACCES`. Use one of these
16
16
  instead:
17
17
 
18
- - ask the DSHM Agent to run `pnpm add dshm-compat@0.1.3` in its bash tool (the
18
+ - ask the DSHM Agent to run `pnpm add dshm-compat@0.1.5` in its bash tool (the
19
19
  bundled pnpm shim routes that command to the host install queue); or
20
20
  - install from a GitHub repository URL; or
21
21
  - after any working install, use the plugin's own `compat_install` tool for
@@ -28,7 +28,7 @@ not accept local paths, `file:` or `http://` specs.
28
28
 
29
29
  ### A2. HTTPS tarball
30
30
 
31
- Put `dshm-compat-0.1.3.tgz` on any HTTPS host (GitHub Release, Gitee Release,
31
+ Put `dshm-compat-0.1.5.tgz` on any HTTPS host (GitHub Release, Gitee Release,
32
32
  object storage) and paste the `.tgz` URL into the same Add plugin input. DSHM's
33
33
  installer accepts tarball specs; if the current market UI refuses a plain URL,
34
34
  use A1.
@@ -38,7 +38,7 @@ use A1.
38
38
  If `hdc` is available and the app can read the path:
39
39
 
40
40
  ```sh
41
- hdc file send dshm-compat-0.1.3.tgz /data/storage/el2/base/haps/entry/files/dshm-compat-0.1.3.tgz
41
+ hdc file send dshm-compat-0.1.5.tgz /data/storage/el2/base/haps/entry/files/dshm-compat-0.1.5.tgz
42
42
  ```
43
43
 
44
44
  Then enter the absolute path (or `file:/...`) in the Add plugin input. This
@@ -47,7 +47,7 @@ depends on the DSHM build's local-file support; A1 is safer.
47
47
  ## B. Desktop DSH (for testing only)
48
48
 
49
49
  ```powershell
50
- & 'D:\安装\dsh-desktop\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'D:\dsh\dsh-26-09-30\dshm-compat\dshm-compat-0.1.3.tgz'
50
+ & 'D:\安装\dsh-desktop\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'D:\dsh\dsh-26-09-30\dshm-compat\dshm-compat-0.1.5.tgz'
51
51
  ```
52
52
 
53
53
  If the profile reports `ERR_PNPM_UNEXPECTED_VIRTUAL_STORE`, its `node_modules`
package/README.md CHANGED
@@ -5,11 +5,12 @@ them through the least invasive strategy that can work.
5
5
 
6
6
  ## Current phase
7
7
 
8
- 0.1.3 fixes dsh-ops recipe dispatch for GitHub specs and relaxes its DSH peer
9
- range; it also adds `compat_doctor`, rollback-safe local installs, and codeload
10
- fallbacks for GitHub. Phase 1 ships the analyzer, the S0/S1 converter, the
11
- dsh-ops recipe, guest probing/provisioning, the DSHM install-queue transport,
12
- and a profile-local fallback for artifacts the host queue rejects:
8
+ 0.1.5 adds runtime-bridge fixes (non-`-c` spawn wrappers), release asset
9
+ fallbacks, and `compat_uninstall`; 0.1.4 added the generic S2 adapter with
10
+ x86_64 QEMU and GitHub Release acquisition. Phase 1 ships the analyzer, the
11
+ S0/S1 converter, the generic S2 engine, guest probing/provisioning, the DSHM
12
+ install-queue transport, and a profile-local fallback for artifacts the host
13
+ queue rejects:
13
14
 
14
15
  - `compat_analyze` downloads an npm/GitHub/local package and classifies it.
15
16
  - `compat_plan` returns the concrete conversion plan without changing anything.
@@ -18,12 +19,21 @@ and a profile-local fallback for artifacts the host queue rejects:
18
19
  install lifecycle scripts, client platform), vendors missing pure-JS runtime
19
20
  dependencies into a flat `node_modules`, validates the resulting tarball, and
20
21
  repacks it as a DSHM tarball; for S1 it removes a native dependency that the
21
- DSHM core already provides. For `dsh-ops` it applies the 0.3.x recipe once an
22
- AArch64 Linux worker is supplied via `workerPath`; without one it returns
23
- `needs-native-build` with the exact cross-build command.
22
+ DSHM core already provides. For S2 it uses the generic native adapter: it
23
+ detects the worker, `PLATFORMS` matrix, `${platform}-${arch}` templates,
24
+ standard spawn calls, and foreign `os`/`cpu`/peer gates. A worker can come from
25
+ `workerPath`, the package's `runtime/linux-*`, or the GitHub Release assets
26
+ (linux-arm64 first, then linux-x64). An x86_64 worker is executed through the
27
+ bundled `qemu-x86_64` and glibc rootfs, so no AArch64 rebuild is required.
28
+ A source-only checkout is retried against the package's published npm build
29
+ (with a `fallback` field in the result); only when neither a worker nor a
30
+ published build exists does it return `needs-build`/`needs-native-build` with
31
+ the external build command. Package names are not special-cased.
24
32
  - `compat_guest_probe` reports the Alpine guest kernel/packages/tools.
25
33
  - `compat_guest_provision` installs a reviewed allowlist of Alpine packages in
26
34
  the guest (`confirm:true` required).
35
+ - `compat_sidecar` stages a native Linux/AArch64 worker from a converted
36
+ tarball and lists/probes/calls it in the guest with JSON stdin/stdout.
27
37
  - `compat_status` lists stored reports and install attempts.
28
38
  - `compat_doctor` checks DSHM mode, core, profile/bundle, queue, state, and the
29
39
  guest in one read-only call.
@@ -31,26 +41,40 @@ and a profile-local fallback for artifacts the host queue rejects:
31
41
  `.tgz` paths (and `file:`/`link:`) are unpacked directly into the owning DSH
32
42
  profile and its bundle list, then need a DSHM restart; this is required
33
43
  because the DSHM host queue rejects local specs.
44
+ - `compat_build` plans a source build (PC/CI commands) or, with
45
+ `backend:"guest"` and `confirm:true`, provisions Node/Rust in the Alpine
46
+ guest and attempts the build there. The guest path is experimental and
47
+ depends on guest network/disk.
48
+ - `compat_uninstall` removes a profile-local package, its dependency entry, and
49
+ its bundle entry; it also needs a restart.
34
50
  - `compat_convert` with `install:true` uses the same local fallback for its
35
51
  converted tarball.
36
52
 
37
- The converter deliberately stops at the manifest/repack/vendor boundary:
38
- source transpilation and native builds are later phases. A report saying
39
- `S2`/`S3`/`S4` is returned as-is rather than being reported as a successful
40
- conversion.
53
+ The converter stops at the manifest/repack/vendor boundary for S0/S1. S2 also
54
+ returns `needs-build` for a source-only package or `needs-native-build` when no
55
+ Linux worker can be obtained. It does not silently claim that an unsupported
56
+ native build succeeded.
41
57
 
42
58
  See [architecture](docs/ARCHITECTURE.md) and [conversion recipes](docs/RECIPES.md).
43
59
 
60
+ ## Development policy
61
+
62
+ There are **no per-plugin production branches**. S2 native plugins go through
63
+ the generic `lib/s2-native.js` engine; package names appear only in tests and
64
+ documentation examples. If a plugin has a unique protocol, add a generic
65
+ extension point (pattern, hook, or sidecar descriptor) instead of a one-off
66
+ patch. See [AGENTS.md](AGENTS.md).
67
+
44
68
  ## CLI
45
69
 
46
70
  The same converter runs on a PC without installing the DSH plugin:
47
71
 
48
72
  ```sh
49
73
  node bin/dshm-compat.mjs analyze dsh-m
50
- node bin/dshm-compat.mjs plan dsh-ops
74
+ node bin/dshm-compat.mjs plan some-native-plugin
51
75
  node bin/dshm-compat.mjs convert dsh-cache-billing --no-vendor
52
- node bin/dshm-compat.mjs convert dsh-ops --worker ./dsh-ops-worker
53
- node bin/dshm-compat.mjs scan dsh-m dsh-ops some-plugin
76
+ node bin/dshm-compat.mjs convert some-native-plugin --worker ./some-native-worker
77
+ node bin/dshm-compat.mjs scan dsh-m some-native-plugin some-plugin
54
78
  ```
55
79
 
56
80
  `convert` writes the tarball under `$DSH_HOME/dshm-compat/builds` (override with
@@ -0,0 +1,17 @@
1
+ # Bundled QEMU x86_64 user-mode runtime
2
+
3
+ This directory is a generic fallback for S2 plugins that ship a `linux-x64`
4
+ native worker but no AArch64 build. `qemu-x86_64` executes the x86_64 worker in
5
+ the DSHM Alpine guest; the `rootfs/` directory supplies the glibc loader and
6
+ libraries the worker links against.
7
+
8
+ - `qemu-x86_64`: Alpine 3.22 `qemu-x86_64` 10.0.0-r1 (aarch64 build), GPL-2.0.
9
+ Source: https://pkgs.alpinelinux.org/package/v3.22/community/aarch64/qemu-x86_64
10
+ - `rootfs/lib64/ld-linux-x86-64.so.2`,
11
+ `rootfs/lib/x86_64-linux-gnu/libc.so.6`,
12
+ `rootfs/lib/x86_64-linux-gnu/libgcc_s.so.1`: Ubuntu 24.04 x86_64 base rootfs,
13
+ LGPL-2.1/GPL-2.0 with GCC runtime exception.
14
+ Source: https://cdimage.ubuntu.com/ubuntu-base/releases/24.04/release/
15
+
16
+ The fallback is optional: when a plugin has a native AArch64 worker, the generic
17
+ S2 adapter stages that directly and these files are not used.
Binary file
@@ -37,7 +37,7 @@ spec ──► resolve (npm / GitHub / local .tgz)
37
37
  ──► extract (ustar/pax, no dependency)
38
38
  ──► analyze (manifest, native files, scripts, platform/Electron scan, core inventory)
39
39
  ──► plan (S0..S4 + concrete steps)
40
- ──► convert (S0/S1 now; S2+ is a plan)
40
+ ──► convert (S0/S1 manifest, S2 generic native adapter)
41
41
  ──► pack (ustar writer)
42
42
  ──► install queue (DSHM only)
43
43
  ```
@@ -50,14 +50,20 @@ spec ──► resolve (npm / GitHub / local .tgz)
50
50
  | `lib/tar.js` | ustar/pax extraction with traversal and size guards |
51
51
  | `lib/analyze.js` | classification evidence, strategy, confidence, next action |
52
52
  | `lib/core.js` | locate `$DSH_HOME/cores/<version>/node_modules` and compare runtime dependencies |
53
- | `lib/rules.js` | exact recipes for known packages (`dsh-ops`) |
54
- | `lib/convert.js` | S0 manifest rewrite/repack and vendoring; S1 native-dependency removal |
53
+ | `lib/rules.js` | operator-supplied custom rules only; no built-in per-package rules |
54
+ | `lib/convert.js` | S0 manifest rewrite/repack and vendoring; S1 native-dependency removal; dispatch to the S2 adapter |
55
+ | `lib/s2-native.js` | generic S2 worker/runtime/spawn/peer adapter |
56
+ | `lib/release.js` | GitHub Release Linux worker discovery and extraction |
57
+ | `lib/zip.js` | dependency-free ZIP reader for release assets |
55
58
  | `lib/vendor.js` | flat `node_modules` vendoring for missing pure-JS dependencies |
56
59
  | `lib/validate.js` | post-conversion manifest/entry/dependency validation |
57
60
  | `lib/plan.js` | user-visible conversion plan for every strategy |
58
61
  | `lib/guest.js` | read-only Alpine guest probe through `subprocess` |
59
62
  | `lib/install.js` | DSHM install-queue producer with polling and cancellation |
60
63
  | `lib/local-install.js` | profile-local tarball install with staging, backup, and rollback |
64
+ | `lib/uninstall.js` | profile-local removal of files, dependency, and bundle entry |
65
+ | `lib/source-build.js` | source-build planner and experimental guest build |
66
+ | `lib/sidecar.js` | generic guest worker staging and JSON-stdio calls |
61
67
  | `lib/doctor.js` | one-shot DSHM/core/profile/queue/guest diagnosis |
62
68
  | `lib/state.js` | bounded report/install index and atomic writes |
63
69
  | `lib/pack.js` | dependency-free ustar/gzip writer |
@@ -72,18 +78,22 @@ spec ──► resolve (npm / GitHub / local .tgz)
72
78
  | `compat_convert` | writes a converted tarball; optionally submits it to the install queue |
73
79
  | `compat_guest_probe` | read-only guest command |
74
80
  | `compat_guest_provision` | installs allowlisted Alpine packages in the guest |
81
+ | `compat_sidecar` | stages/lists/probes/calls a JSON-stdio native worker in the guest |
75
82
  | `compat_doctor` | read-only DSHM/core/profile/queue/guest diagnosis |
76
83
  | `compat_install` | writes one install-queue request; runs third-party code |
84
+ | `compat_build` | plans a source build, or provisions a guest toolchain and runs it with confirm:true |
85
+ | `compat_uninstall` | removes a profile-local package, dependency, and bundle entry |
77
86
  | `compat_status` | read-only |
78
87
 
79
88
  ## Phase status
80
89
 
81
90
  - **Phase 1 (done):** analyzer, batch triage, S0 manifest converter + flat
82
- dependency vendoring + output validation, S1 manifest substitution,
83
- dependency preflight, guest probe/provisioning, worker execution primitive,
84
- dsh-ops 0.3.x recipe (external AArch64 worker), install queue, offline tests.
85
- - **Phase 2 (next):** on-device guest verification, dsh-ops worker build, nested
86
- version-conflict vendoring, sidecar RPC.
91
+ dependency vendoring + output validation, S1 manifest substitution, generic S2
92
+ native adapter (AArch64 worker, x86_64 worker through bundled QEMU, GitHub
93
+ Release acquisition), dependency preflight, guest probe/provisioning, worker
94
+ execution primitive, install queue, offline tests.
95
+ - **Phase 2 (next):** on-device guest verification, automatic worker build
96
+ (desktop/CI), nested version-conflict vendoring, sidecar RPC.
87
97
  - **Phase 3:** WASM/remote fallback, market UI entry, compatibility registry
88
98
  updates.
89
99
 
package/docs/RECIPES.md CHANGED
@@ -1,104 +1,92 @@
1
- # Conversion recipes
2
-
3
- These are the concrete recipes behind `S1`/`S2` plans. They are intentionally
4
- written as patches to the upstream package, not as claims that the current
5
- converter executes them.
6
-
7
- ## `dsh-ops` (Rust native worker)
8
-
9
- Upstream: `T-Auto/dsh-ops`. The core is `native/` (Rust) and the published
10
- worker is `runtime/<target>/dsh-ops-worker`.
11
-
12
- ### 1. Support matrix
13
-
14
- `lib/ptc/runtime.js` hard-codes:
15
-
16
- ```js
17
- const PLATFORMS = ['win32-x64', 'linux-x64', 'darwin-arm64']
18
- ```
19
-
20
- `resolveWorker()` checks this matrix **before** considering
21
- `DSH_OPS_WORKER_BIN`, so a binary path override alone cannot unblock DSHM.
22
-
23
- Patch:
24
-
25
- - add `linux-arm64` to `PLATFORMS`;
26
- - map an OHOS `process.platform` to `linux-arm64` before the matrix check;
27
- - keep `workerName()` returning `dsh-ops-worker` on non-win32;
28
- - look for `runtime/linux-arm64/dsh-ops-worker`.
29
-
30
- Also update:
31
-
32
- - `package.json` `os`/`cpu` (remove the gates or add `openharmony`/`arm64`);
33
- - `dsh-plugin.json` `platforms`, `architectures`, and `targets` for each
34
- `x-components` entry.
35
-
36
- ### 2. Build the worker
37
-
38
- From a Linux build host or CI:
1
+ # Generic conversion patterns
2
+
3
+ These are the patterns behind `S1`/`S2` plans. They are **generic**: no
4
+ production branch is keyed on a package name. A package name may appear only in
5
+ tests or as a documentation example.
6
+
7
+ ## S2: native worker
8
+
9
+ The generic adapter in `lib/s2-native.js` recognizes the common shape of a
10
+ native DSH plugin:
11
+
12
+ 1. A worker binary under `runtime/<platform>-<arch>/<name>`, a worker passed
13
+ explicitly with `workerPath`, or a Linux asset on the repository's GitHub
14
+ Release (arm64 preferred, then x64).
15
+ 2. A JS module exporting a `PLATFORMS` array and interpolating
16
+ `${platform}-${arch}` into runtime paths.
17
+ 3. Spawn calls of the form `const argv = [file]` or `let argv = [file]`.
18
+ 4. Optional `package.json` `os`/`cpu` gates and a DSH `peerDependencies` range.
19
+
20
+ For each match it:
21
+
22
+ - appends `linux-arm64` (AArch64 worker) or `linux-x64` (x86_64 worker) to
23
+ `PLATFORMS` and injects a `resolveTarget()` helper so `openharmony`/arm64
24
+ resolves to that target in both `resolveWorker` and `tryResolveWorker`;
25
+ - replaces `${platform}-${arch}` **inside template literals** with
26
+ `${resolveTarget(platform, arch)}`;
27
+ - rewrites every standard spawn call to `['bash', file]` and stages a shell
28
+ wrapper at `file` that `exec`s the real worker with `"$@"`. The
29
+ non-`-c` path is required because `dshm-vm-bash` drops positional parameters
30
+ for `bash -c`; the wrapper preserves the worker's argv;
31
+ - removes `os`/`cpu`/`engines.dsh`, drops win32/darwin optional payloads, and
32
+ relaxes `@deepseek-ai/dsh` to `workspace:*`;
33
+ - adds `openharmony`/target/arch to `dsh-plugin.json` component targets;
34
+ - stages the wrapper at `runtime/<target>/<worker>` and the real binary at
35
+ `<worker>-bin` (AArch64) or `<worker>-x64` (x86_64), with a matching
36
+ `provenance.json` (`version`, `target`, `executable`, `sha256`).
37
+
38
+ A source package whose `main` is absent but which has a `build`/`prepack` script
39
+ returns `needs-build` with `npm ci && npm run build && npm pack`; the produced
40
+ tarball is then passed back to `compat_convert`. Before returning that, the
41
+ converter retries the package's latest published npm build and records a
42
+ `fallback` field when it succeeds. This keeps a source checkout from being
43
+ misreported as a broken recipe while still preferring a runnable published build.
44
+
45
+ ### Guest source build
46
+
47
+ `compat_build { spec, backend: "plan" }` returns the exact PC/CI commands. With
48
+ `backend: "guest"` and `confirm: true`, the plugin provisions Node or Rust in
49
+ the Alpine guest and runs the detected command (`cargo build --release`, or
50
+ `npm ci && npm run build && npm pack`). This is experimental: it needs guest
51
+ network, enough guest disk for the toolchain, and a guest-visible source
52
+ directory. A failure returns the exact `apk`/build stderr; fall back to an
53
+ external build and pass the produced artifact to `compat_convert`.
54
+
55
+ An x86_64 worker is staged as a shell wrapper plus `qemu-x86_64`, a minimal
56
+ x86_64 glibc rootfs, and the original `<worker>-x64`; the wrapper runs QEMU in
57
+ the guest. This makes a `linux-x64` runtime usable without an AArch64 rebuild.
58
+
59
+ If no worker is supplied, none is found in the package, and the GitHub Release
60
+ has no Linux asset, the result is `needs-native-build` with the cross-build
61
+ command. The adapter never builds the worker itself.
62
+
63
+ ### Building the worker
64
+
65
+ For a Rust crate under `native/`:
39
66
 
40
67
  ```sh
41
- rustup target add aarch64-unknown-linux-musl
42
- # either
43
- cross build --release --locked --manifest-path native/Cargo.toml --target aarch64-unknown-linux-musl
44
- # or
68
+ cargo build --release --locked --manifest-path native/Cargo.toml --target aarch64-unknown-linux-musl
69
+ # or, with a Zig-based cross linker:
45
70
  cargo zigbuild --release --locked --manifest-path native/Cargo.toml --target aarch64-unknown-linux-musl
46
71
  ```
47
72
 
48
- The Windows/macOS metadata crates are target-gated in `native/Cargo.toml`; the
49
- portable dependencies are `cap-std`, `cap-fs-ext`, `fs2`, `rustix`, `sha2`,
50
- `serde`, `serde_json`, `uuid`, `regex`.
73
+ The result must be a static AArch64 musl ELF: `e_machine == 183`, no
74
+ `PT_INTERP`. Dynamic glibc binaries will not start in the Alpine guest.
51
75
 
52
- Stage the result as `runtime/linux-arm64/dsh-ops-worker` with a matching
53
- `provenance.json` (`version`, `target`, `executable`, `sha256`).
76
+ ### Generic sidecar
54
77
 
55
- ### 3. Spawn through the guest
78
+ A native worker that follows one-JSON-request / one-JSON-response can also be
79
+ staged and called without patching the plugin's own tool layer:
56
80
 
57
- `lib/file/worker.js` and `lib/ptc/worker.js` call:
58
-
59
- ```js
60
- subprocess.spawn({ argv: [file], ... })
61
- ```
62
-
63
- `dshm-vm-bash` only redirects `bash -c`, so patch both call sites to:
64
-
65
- ```js
66
- argv: ['bash', '-c', 'exec "$0"', file]
81
+ ```json
82
+ compat_sidecar { "action": "stage", "tarball": "<converted.tgz>", "workerPath": "runtime/linux-arm64/worker", "id": "my-worker" }
83
+ compat_sidecar { "action": "probe", "id": "my-worker" }
84
+ compat_sidecar { "action": "call", "id": "my-worker", "input": { "ping": true }, "confirm": true }
67
85
  ```
68
86
 
69
- The worker path is under the profile's 9p share, so the guest sees the same
70
- absolute path. If exec from 9p fails on a device, copy the worker to
71
- `/tmp/dsh-ops-worker` once (hash-keyed) and execute that copy.
72
-
73
- ### 4. Shell component
74
-
75
- `lib/shells/resolve.js` treats every non-win32 host as POSIX and probes the
76
- **host** for `/bin/bash`. DSHM's host has no bash. Patch the DSHM branch to
77
- report the guest's `/bin/bash` as available; `ops_bash` then spawns
78
- `bash -c`, which `dshm-vm-bash` routes into the guest.
79
-
80
- Disable the PowerShell rung: the pwsh payload is win32-only.
81
-
82
- ### 5. PTC vs file
83
-
84
- `lib/ptc/worker.js` calls `sandbox.confine()` for non-`danger-full-access`
85
- policies. If DSHM's sandbox provider cannot confine the worker, PTC tools fail
86
- closed. The `file` component uses policy checks but no `sandbox.confine`, so
87
- port `ops_*` file tools first and treat PTC as a later optimization.
88
-
89
- ### 6. Verification
90
-
91
- Run on the device:
92
-
93
- - `ops_stat` on a workspace file;
94
- - `ops_grep` over a small tree;
95
- - `ops_write_file` then `ops_stat` (check the new revision);
96
- - `ops_copy` / `ops_move` (9p rename/atomicity);
97
- - a failure case with a read-only policy.
98
-
99
- Watch for `cap-std`/`rustix` failures on 9p (`openat2`, `statx`, `renameat2`,
100
- `flock`). If those fail, the fallback is a JS reimplementation of the worker
101
- protocol rather than a guest port.
87
+ Staging checks the worker is an AArch64 ELF and puts it under
88
+ `$DSH_HOME/dshm-compat/sidecars/<id>/`. Calls run through
89
+ `bash -c 'exec "$0" "$@"'`, which `dshm-vm-bash` routes into Alpine.
102
90
 
103
91
  ## Common native dependencies
104
92