You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/08/28 13:09:12 UTC

(maka) branch feat/runtime-host-target-aware-dev-package created (now 32316a4ed)

This is an automated email from the ASF dual-hosted git repository.

M4n5ter pushed a change to branch feat/runtime-host-target-aware-dev-package
in repository https://gitbox.apache.org/repos/asf/maka.git


      at 32316a4ed feat(runtime-host): target development host packages

This branch includes the following new commits:

     new 32316a4ed feat(runtime-host): target development host packages

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org


(maka) 01/01: feat(runtime-host): target development host packages

Posted by wy...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

M4n5ter pushed a commit to branch feat/runtime-host-target-aware-dev-package
in repository https://gitbox.apache.org/repos/asf/maka.git

commit 32316a4ed6b5627846d469b22f8d4adbbf443daf
Author: M4n5ter <m4...@gmail.com>
AuthorDate: Fri Aug 28 21:08:57 2026 +0800

    feat(runtime-host): target development host packages
    
    Generated-by: Codex
---
 .../main/__tests__/runtime-host-management.test.ts |   6 ++
 .../main/__tests__/runtime-host-onboarding.test.ts |   9 ++
 .../__tests__/runtime-host-setup-package.test.ts   |  24 +++--
 .../__tests__/runtime-host-ssh-terminal.test.ts    |  32 +++++-
 apps/desktop/src/main/runtime-host-boot.ts         |  14 ++-
 apps/desktop/src/main/runtime-host-management.ts   |  19 +++-
 apps/desktop/src/main/runtime-host-onboarding.ts   |  32 +++++-
 .../desktop/src/main/runtime-host-setup-package.ts | 107 +++++++++++++++------
 apps/desktop/src/main/runtime-host-ssh-terminal.ts |  85 ++++++++++++++++
 native/runtime-host-peer/build.mjs                 |  13 ++-
 scripts/release-cli-package.mjs                    |  85 +++++++++++++---
 11 files changed, 366 insertions(+), 60 deletions(-)

diff --git a/apps/desktop/src/main/__tests__/runtime-host-management.test.ts b/apps/desktop/src/main/__tests__/runtime-host-management.test.ts
index b82385efd..e7d234745 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-management.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-management.test.ts
@@ -425,6 +425,9 @@ test('publishes update progress and waits for the managed profile to reconnect',
     runUpdatePolicy: async () => assert.fail('update policy is not expected'),
     runUpdateReconciliation: async () =>
       assert.fail('update reconciliation is not expected'),
+    setupPackageMode: 'published',
+    resolveSshDevelopmentPeerTarget: async () =>
+      assert.fail('published update must not inspect the development target'),
     resolveUpdatePackage: () => ({ kind: 'npm', specifier: 'maka-agent@1.3.0' }),
     currentHostEpoch: () => 'host-before-update',
     awaitUpdatedConnection: async (...args) => {
@@ -1175,6 +1178,9 @@ function unusedUpdateDependencies() {
     runPeerManagement: async (): Promise<never> =>
       assert.fail('direct peer management is not expected'),
     directPeerClientAvailable: false,
+    setupPackageMode: 'published' as const,
+    resolveSshDevelopmentPeerTarget: async (): Promise<never> =>
+      assert.fail('published update must not inspect the development target'),
     resolveUpdatePackage: () => ({ kind: 'npm', specifier: 'maka-agent@1.2.3' } as const),
     currentHostEpoch: () => undefined,
     awaitUpdatedConnection: async () => undefined,
diff --git a/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts b/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts
index 2eae7b7d4..c40b096d2 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts
@@ -94,6 +94,7 @@ test('persists a verified on-demand SSH profile without endpoint or credential p
 
 test('onboards WSL as a credential-free environment profile', async () => {
   let saved: DesktopRuntimeHostProfileAddInput | undefined;
+  const peerTargets: string[] = [];
   const harness = createHarness({
     profiles: {
       addAndEnable: async (input) => {
@@ -108,6 +109,10 @@ test('onboards WSL as a credential-free environment profile', async () => {
         operatorPath: '/home/operator/.local/share/maka/operator',
       };
     },
+    resolveSetupPackage: (peerTarget) => {
+      peerTargets.push(peerTarget);
+      return { kind: 'npm', specifier: 'maka-agent@0.2.0' };
+    },
   });
 
   const result = await harness.invoke('runtime-host-onboarding:start', {
@@ -125,6 +130,7 @@ test('onboards WSL as a credential-free environment profile', async () => {
     rootId: 'a'.repeat(64),
     operatorPath: '/home/operator/.local/share/maka/operator',
   });
+  assert.deepEqual(peerTargets, ['none']);
   await harness.onboarding.close();
 });
 
@@ -294,6 +300,9 @@ function createHarness(overrides: HarnessOverrides = {}) {
       addAndEnableVerified: async () => assert.fail('profile must not be saved'),
       ...profiles,
     },
+    setupPackageMode: 'published',
+    resolveSshDevelopmentPeerTarget: async () =>
+      assert.fail('published setup must not inspect the development target'),
     resolveSetupPackage: () => ({ kind: 'npm', specifier: 'maka-agent@0.2.0' }),
     runSetup: async () => assert.fail('SSH must not start'),
     runWslSetup: async () => assert.fail('WSL must not start'),
diff --git a/apps/desktop/src/main/__tests__/runtime-host-setup-package.test.ts b/apps/desktop/src/main/__tests__/runtime-host-setup-package.test.ts
index c5e9ecec4..6827082f2 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-setup-package.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-setup-package.test.ts
@@ -23,17 +23,19 @@ import { join, resolve } from 'node:path';
 import { test } from 'node:test';
 import { createRuntimeHostSetupPackageResolver } from '../runtime-host-setup-package.js';
 
-test('development setup lazily builds one local CLI archive unless explicitly overridden', async () => {
+test('development setup lazily caches CLI archives by peer target unless overridden', async () => {
   const repoRoot = resolve('/workspace');
   const archive = join(repoRoot, 'packages', 'cli', 'release', 'maka-agent-dev.tgz');
   let builds = 0;
   let closes = 0;
+  const targets: string[] = [];
   const resolvePackage = createRuntimeHostSetupPackageResolver({
     isPackaged: false,
     appPath: join(repoRoot, 'apps', 'desktop'),
     environment: {},
-    startDevelopmentArchiveBuild: (resolvedRoot) => {
+    startDevelopmentArchiveBuild: (resolvedRoot, target) => {
       builds += 1;
+      targets.push(target);
       assert.equal(resolvedRoot, repoRoot);
       return {
         result: Promise.resolve(archive),
@@ -44,7 +46,11 @@ test('development setup lazily builds one local CLI archive unless explicitly ov
     },
   });
 
-  assert.deepEqual(await Promise.all([resolvePackage.resolve(), resolvePackage.resolve()]), [
+  assert.equal(resolvePackage.mode, 'development');
+  assert.deepEqual(await Promise.all([
+    resolvePackage.resolve('linux-x64'),
+    resolvePackage.resolve('linux-x64'),
+  ]), [
     {
       kind: 'development_archive',
       path: archive,
@@ -54,7 +60,9 @@ test('development setup lazily builds one local CLI archive unless explicitly ov
       path: archive,
     },
   ]);
-  assert.equal(builds, 1);
+  await resolvePackage.resolve('none');
+  assert.equal(builds, 2);
+  assert.deepEqual(targets, ['linux-x64', 'none']);
 
   const override = join(tmpdir(), 'explicit.tgz');
   const resolveOverride = createRuntimeHostSetupPackageResolver({
@@ -63,12 +71,12 @@ test('development setup lazily builds one local CLI archive unless explicitly ov
     environment: { MAKA_RUNTIME_HOST_SETUP_ARCHIVE: override },
     startDevelopmentArchiveBuild: () => assert.fail('override must bypass the local build'),
   });
-  assert.deepEqual(await resolveOverride.resolve(), {
+  assert.deepEqual(await resolveOverride.resolve('none'), {
     kind: 'development_archive',
     path: override,
   });
   await Promise.all([resolvePackage.close(), resolveOverride.close()]);
-  assert.equal(closes, 1);
+  assert.equal(closes, 2);
 });
 
 test('cancelling the last waiter closes its build before a new setup starts', async () => {
@@ -107,10 +115,10 @@ test('cancelling the last waiter closes its build before a new setup starts', as
     },
   });
 
-  const first = resolver.resolve(cancelled.signal);
+  const first = resolver.resolve('linux-x64', cancelled.signal);
   cancelled.abort(new Error('setup cancelled'));
   await closeStarted;
-  const second = resolver.resolve();
+  const second = resolver.resolve('linux-x64');
   await Promise.resolve();
   assert.equal(builds, 1);
 
diff --git a/apps/desktop/src/main/__tests__/runtime-host-ssh-terminal.test.ts b/apps/desktop/src/main/__tests__/runtime-host-ssh-terminal.test.ts
index 3bbc6c564..c5a918c24 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-ssh-terminal.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-ssh-terminal.test.ts
@@ -33,7 +33,37 @@ import {
   RUNTIME_HOST_OPERATOR_PEER_MANAGEMENT_CAPABILITY,
   RUNTIME_HOST_SETUP_FRAME_PREFIX,
 } from '@maka/runtime-host/operator';
-import { createDesktopRuntimeHostSshTerminal } from '../runtime-host-ssh-terminal.js';
+import {
+  createDesktopRuntimeHostSshTerminal,
+  runtimeHostDevelopmentPeerTargetFromUname,
+} from '../runtime-host-ssh-terminal.js';
+
+test('maps supported SSH uname identities to development peer targets', () => {
+  assert.equal(runtimeHostDevelopmentPeerTargetFromUname('Linux', 'x86_64'), 'linux-x64');
+  assert.equal(runtimeHostDevelopmentPeerTargetFromUname('Linux', 'aarch64'), 'linux-arm64');
+  assert.equal(runtimeHostDevelopmentPeerTargetFromUname('Darwin', 'arm64'), 'darwin-arm64');
+  assert.throws(
+    () => runtimeHostDevelopmentPeerTargetFromUname('Linux', 'riscv64'),
+    /not available/u,
+  );
+});
+
+test('detects the development peer target through the bounded SSH preflight', async () => {
+  const harness = createHarness('pending');
+  const detection = harness.terminal.resolveDevelopmentPeerTarget({
+    destination: 'operator@example.com',
+  });
+  await waitFor(() => harness.pty.hasDataListener());
+  const command = harness.launchArgs[0]?.at(-1) ?? '';
+  const marker = command.match(/__MAKA_RUNTIME_HOST_TARGET_[0-9a-f]+__/u)?.[0];
+  assert.ok(marker);
+  harness.pty.emitData(`${marker}Linux:x86_64\r\n`);
+  harness.pty.exit(0);
+
+  assert.equal(await detection, 'linux-x64');
+  assert.doesNotMatch(JSON.stringify(harness.events), /MAKA_RUNTIME_HOST_TARGET/u);
+  await harness.terminal.close();
+});
 
 test('keeps a connecting SSH prompt observable across renderer presentation changes', async () => {
   const harness = createHarness('pending');
diff --git a/apps/desktop/src/main/runtime-host-boot.ts b/apps/desktop/src/main/runtime-host-boot.ts
index 0bbce55db..5476d7e02 100644
--- a/apps/desktop/src/main/runtime-host-boot.ts
+++ b/apps/desktop/src/main/runtime-host-boot.ts
@@ -172,7 +172,10 @@ import {
   createDesktopRuntimeHostSshTerminal,
 } from "./runtime-host-ssh-terminal.js";
 import { runDesktopRuntimeHostWslSetup } from './runtime-host-wsl-controller.js';
-import { createRuntimeHostSetupPackageResolver } from "./runtime-host-setup-package.js";
+import {
+  createRuntimeHostSetupPackageResolver,
+  desktopRuntimeHostDevelopmentPeerTarget,
+} from "./runtime-host-setup-package.js";
 import { configureDesktopRuntimeHostPeerClient } from './runtime-host-peer-client.js';
 import { createDesktopRuntimeHostLocalOperator } from './runtime-host-local-operator.js';
 import { createDesktopLocalRuntimeHostRemoteAccess } from './runtime-host-local-remote-access.js';
@@ -399,7 +402,10 @@ const localRuntimeHostRemoteAccess = createDesktopLocalRuntimeHostRemoteAccess({
   rootId: startupLocalStorageRoot.rootId,
   directPeerAvailable: runtimeHostDirectPeerAvailable,
   manager: () => runtimeHostManager,
-  resolveSetupPackage: runtimeHostSetupPackage.resolve,
+  resolveSetupPackage: (signal) => runtimeHostSetupPackage.resolve(
+    desktopRuntimeHostDevelopmentPeerTarget(),
+    signal,
+  ),
   operator: localRuntimeHostOperator,
 });
 const native = assembleDesktopNativeCapabilities({
@@ -474,6 +480,8 @@ const runtimeHostOnboarding = createDesktopRuntimeHostOnboarding({
   runSetup: runtimeHostSshTerminal.runSetup,
   runWslSetup: runDesktopRuntimeHostWslSetup,
   listWslDistributions: listRuntimeHostWslDistributions,
+  setupPackageMode: runtimeHostSetupPackage.mode,
+  resolveSshDevelopmentPeerTarget: runtimeHostSshTerminal.resolveDevelopmentPeerTarget,
   resolveSetupPackage: runtimeHostSetupPackage.resolve,
   send: (snapshot) =>
     mainWindowController.send("runtime-host-onboarding:changed", snapshot),
@@ -487,6 +495,8 @@ const runtimeHostManagement = createDesktopRuntimeHostManagement({
   runUpdate: runtimeHostSshTerminal.runUpdate,
   runUpdatePolicy: runtimeHostSshTerminal.runUpdatePolicy,
   runUpdateReconciliation: runtimeHostSshTerminal.runUpdateReconciliation,
+  setupPackageMode: runtimeHostSetupPackage.mode,
+  resolveSshDevelopmentPeerTarget: runtimeHostSshTerminal.resolveDevelopmentPeerTarget,
   resolveUpdatePackage: runtimeHostSetupPackage.resolve,
   currentHostEpoch: (profileId) =>
     runtimeHostManager?.current(profileId)?.candidate?.client.hostEpoch,
diff --git a/apps/desktop/src/main/runtime-host-management.ts b/apps/desktop/src/main/runtime-host-management.ts
index 3b82eb0b9..c1e82c737 100644
--- a/apps/desktop/src/main/runtime-host-management.ts
+++ b/apps/desktop/src/main/runtime-host-management.ts
@@ -54,6 +54,7 @@ import type {
   RuntimeHostServiceUpdateReconciliationTerminalFrame,
   RuntimeHostServiceUpdateTerminalFrame,
 } from './runtime-host-ssh-terminal.js';
+import type { DesktopRuntimeHostDevelopmentPeerTarget } from './runtime-host-setup-package.js';
 
 const MANAGEMENT_ACTIONS = new Set<DesktopRuntimeHostManagementAction>([
   'status',
@@ -104,7 +105,15 @@ export function createDesktopRuntimeHostManagement(input: {
     input: DesktopRuntimeHostSshUpdateReconciliationInput,
     onProgress: (phase: DesktopRuntimeHostManagementProgress['phase']) => void,
   ) => Promise<RuntimeHostServiceUpdateReconciliationTerminalFrame>;
-  readonly resolveUpdatePackage: () =>
+  readonly setupPackageMode: 'published' | 'development';
+  readonly resolveSshDevelopmentPeerTarget: (input: {
+    readonly destination: string;
+    readonly sshPort?: number;
+    readonly signal?: AbortSignal;
+  }) => Promise<Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'>>;
+  readonly resolveUpdatePackage: (
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+  ) =>
     | DesktopRuntimeHostSetupPackage
     | Promise<DesktopRuntimeHostSetupPackage>;
   readonly currentHostEpoch: (profileId: string) => string | undefined;
@@ -466,7 +475,13 @@ export function createDesktopRuntimeHostManagement(input: {
       await managedMutationTarget(profileIdValue);
     const previousHostEpoch = input.currentHostEpoch(profileId);
     input.sendProgress({ profileId, phase: 'preparing_cli' });
-    const setupPackage = await input.resolveUpdatePackage();
+    const peerTarget = input.setupPackageMode === 'development'
+      ? await input.resolveSshDevelopmentPeerTarget({
+          destination: transport.destination,
+          ...(transport.sshPort === undefined ? {} : { sshPort: transport.sshPort }),
+        })
+      : 'none';
+    const setupPackage = await input.resolveUpdatePackage(peerTarget);
     const response = await input.runUpdate(
       {
         destination: transport.destination,
diff --git a/apps/desktop/src/main/runtime-host-onboarding.ts b/apps/desktop/src/main/runtime-host-onboarding.ts
index 1c9674b8e..fd39a4276 100644
--- a/apps/desktop/src/main/runtime-host-onboarding.ts
+++ b/apps/desktop/src/main/runtime-host-onboarding.ts
@@ -33,6 +33,7 @@ import type {
   DesktopRuntimeHostSshSetupInput,
 } from './runtime-host-ssh-terminal.js';
 import type { DesktopRuntimeHostWslSetupInput } from './runtime-host-wsl-controller.js';
+import type { DesktopRuntimeHostDevelopmentPeerTarget } from './runtime-host-setup-package.js';
 import { requireProjectDirectoryRoots } from '../shared/runtime-host-project-directory-policy.js';
 
 type OnboardingState = DesktopRuntimeHostOnboardingSnapshot extends infer Snapshot
@@ -65,7 +66,14 @@ export function createDesktopRuntimeHostOnboarding(input: {
   ) => Promise<{ readonly rootId: string; readonly operatorPath: string }>;
   readonly listWslDistributions: () => Promise<readonly string[]>;
   readonly send: (snapshot: DesktopRuntimeHostOnboardingSnapshot) => void;
+  readonly setupPackageMode: 'published' | 'development';
+  readonly resolveSshDevelopmentPeerTarget: (input: {
+    readonly destination: string;
+    readonly sshPort?: number;
+    readonly signal?: AbortSignal;
+  }) => Promise<Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'>>;
   readonly resolveSetupPackage: (
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
     signal?: AbortSignal,
   ) => DesktopRuntimeHostSetupPackage | Promise<DesktopRuntimeHostSetupPackage>;
 }): { close(): Promise<void> } {
@@ -113,8 +121,14 @@ export function createDesktopRuntimeHostOnboarding(input: {
     signal: AbortSignal,
   ): Promise<DesktopRuntimeHostOnboardingSnapshot> => {
     try {
-      const setupPackage = await input.resolveSetupPackage(signal);
-      if (request.kind === 'wsl') return await runWsl(request, setupPackage, signal);
+      if (request.kind === 'wsl') {
+        const setupPackage = await input.resolveSetupPackage('none', signal);
+        return await runWsl(request, setupPackage, signal);
+      }
+      const peerTarget = input.setupPackageMode === 'development'
+        ? await resolveSshDevelopmentPeerTarget(request, signal)
+        : 'none';
+      const setupPackage = await input.resolveSetupPackage(peerTarget, signal);
       const lifecycle = setupPackage.kind === 'npm' ? 'on_demand' : 'supervised';
       signal.throwIfAborted();
       publish({ kind: 'running', phase: 'connecting_ssh' });
@@ -203,6 +217,20 @@ export function createDesktopRuntimeHostOnboarding(input: {
     }
   };
 
+  const resolveSshDevelopmentPeerTarget = async (
+    request: Extract<DesktopRuntimeHostOnboardingInput, { readonly kind: 'ssh' }>,
+    signal: AbortSignal,
+  ): Promise<Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'>> => {
+    publish({ kind: 'running', phase: 'connecting_ssh' });
+    const target = await input.resolveSshDevelopmentPeerTarget({
+      destination: request.destination,
+      ...(request.sshPort === undefined ? {} : { sshPort: request.sshPort }),
+      signal,
+    });
+    publish({ kind: 'running', phase: 'preparing_cli' });
+    return target;
+  };
+
   const runWsl = async (
     request: Extract<DesktopRuntimeHostOnboardingInput, { readonly kind: 'wsl' }>,
     setupPackage: DesktopRuntimeHostSetupPackage,
diff --git a/apps/desktop/src/main/runtime-host-setup-package.ts b/apps/desktop/src/main/runtime-host-setup-package.ts
index 7a9ca7f10..5a182ace5 100644
--- a/apps/desktop/src/main/runtime-host-setup-package.ts
+++ b/apps/desktop/src/main/runtime-host-setup-package.ts
@@ -36,33 +36,68 @@ interface DevelopmentArchiveBuild {
   close(): Promise<void>;
 }
 
+interface DevelopmentBuildState {
+  readonly task: DevelopmentArchiveBuild;
+  readonly result: Promise<DesktopRuntimeHostSetupPackage>;
+  waiters: number;
+  settled: boolean;
+  closing?: Promise<void>;
+}
+
+export type DesktopRuntimeHostDevelopmentPeerTarget =
+  | 'none'
+  | 'darwin-arm64'
+  | 'linux-arm64'
+  | 'linux-x64'
+  | 'win32-x64';
+
 export interface RuntimeHostSetupPackageResolver {
-  resolve(signal?: AbortSignal): Promise<DesktopRuntimeHostSetupPackage>;
+  readonly mode: 'published' | 'development';
+  resolve(
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+    signal?: AbortSignal,
+  ): Promise<DesktopRuntimeHostSetupPackage>;
   close(): Promise<void>;
 }
 
+export function desktopRuntimeHostDevelopmentPeerTarget(
+  platform: NodeJS.Platform = process.platform,
+  arch: string = process.arch,
+): Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'> {
+  const target = `${platform}-${arch}`;
+  if (
+    target !== 'darwin-arm64' &&
+    target !== 'linux-arm64' &&
+    target !== 'linux-x64' &&
+    target !== 'win32-x64'
+  ) {
+    throw new Error(`Direct peer is not available on ${target}`);
+  }
+  return target;
+}
+
 export function createRuntimeHostSetupPackageResolver(input: {
   readonly isPackaged: boolean;
   readonly appPath: string;
   readonly environment: NodeJS.ProcessEnv;
-  readonly startDevelopmentArchiveBuild?: (repoRoot: string) => DevelopmentArchiveBuild;
+  readonly startDevelopmentArchiveBuild?: (
+    repoRoot: string,
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+  ) => DevelopmentArchiveBuild;
 }): RuntimeHostSetupPackageResolver {
   let closed = false;
-  let developmentBuild:
-    | {
-        readonly task: DevelopmentArchiveBuild;
-        readonly result: Promise<DesktopRuntimeHostSetupPackage>;
-        waiters: number;
-        settled: boolean;
-        closing?: Promise<void>;
-      }
-    | undefined;
+  const developmentBuilds = new Map<
+    DesktopRuntimeHostDevelopmentPeerTarget,
+    DevelopmentBuildState
+  >();
 
-  const startBuild = () => {
+  const startBuild = (
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+  ): DevelopmentBuildState => {
     const repoRoot = resolve(input.appPath, '..', '..');
-    const task = input.startDevelopmentArchiveBuild?.(repoRoot) ??
-      startDevelopmentArchiveBuild(repoRoot, input.environment);
-    const build = {
+    const task = input.startDevelopmentArchiveBuild?.(repoRoot, peerTarget) ??
+      startDevelopmentArchiveBuild(repoRoot, input.environment, peerTarget);
+    const build: DevelopmentBuildState = {
       task,
       result: task.result.then((path) => ({
         kind: 'development_archive' as const,
@@ -71,62 +106,71 @@ export function createRuntimeHostSetupPackageResolver(input: {
       waiters: 0,
       settled: false,
     };
-    developmentBuild = build;
+    developmentBuilds.set(peerTarget, build);
     void build.result.then(
       () => {
         build.settled = true;
       },
       () => {
         build.settled = true;
-        if (developmentBuild === build) developmentBuild = undefined;
+        if (developmentBuilds.get(peerTarget) === build) {
+          developmentBuilds.delete(peerTarget);
+        }
       },
     );
     return build;
   };
 
-  const stopBuild = async (build: NonNullable<typeof developmentBuild>) => {
+  const stopBuild = async (
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+    build: DevelopmentBuildState,
+  ) => {
     build.closing ??= build.task.close().finally(() => {
-      if (developmentBuild === build) developmentBuild = undefined;
+      if (developmentBuilds.get(peerTarget) === build) developmentBuilds.delete(peerTarget);
     });
     await build.closing;
     await build.result.catch(() => undefined);
   };
 
-  const acquireBuild = async (signal?: AbortSignal) => {
+  const acquireBuild = async (
+    peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
+    signal?: AbortSignal,
+  ) => {
     while (true) {
       if (closed) throw new Error('Runtime Host setup package resolver is closed');
-      const build = developmentBuild;
-      if (!build) return startBuild();
+      const build = developmentBuilds.get(peerTarget);
+      if (!build) return startBuild(peerTarget);
       if (!build.closing) return build;
       await waitForPackage(build.closing, signal);
     }
   };
 
   return {
-    async resolve(signal) {
+    mode: input.isPackaged ? 'published' : 'development',
+    async resolve(peerTarget, signal) {
       if (closed) throw new Error('Runtime Host setup package resolver is closed');
       if (input.isPackaged) return packagedSetupPackage(input.appPath);
 
       const override = input.environment[DEVELOPMENT_ARCHIVE_ENV];
       if (override) return { kind: 'development_archive', path: override };
 
-      const build = await acquireBuild(signal);
+      const build = await acquireBuild(peerTarget, signal);
       build.waiters += 1;
       try {
         return await waitForPackage(build.result, signal);
       } finally {
         build.waiters -= 1;
         if (signal?.aborted && build.waiters === 0 && !build.settled) {
-          await stopBuild(build);
+          await stopBuild(peerTarget, build);
         }
       }
     },
     async close() {
       if (closed) return;
       closed = true;
-      const build = developmentBuild;
-      developmentBuild = undefined;
-      if (build) await stopBuild(build);
+      const builds = [...developmentBuilds.entries()];
+      developmentBuilds.clear();
+      await Promise.all(builds.map(([peerTarget, build]) => stopBuild(peerTarget, build)));
     },
   };
 }
@@ -144,6 +188,7 @@ function packagedSetupPackage(appPath: string): DesktopRuntimeHostSetupPackage {
 function startDevelopmentArchiveBuild(
   repoRoot: string,
   environment: NodeJS.ProcessEnv,
+  peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
 ): DevelopmentArchiveBuild {
   const script = join(repoRoot, 'scripts', 'release-cli-package.mjs');
   const nodeExecutable = environment.npm_node_execpath?.trim() || 'node';
@@ -153,7 +198,11 @@ function startDevelopmentArchiveBuild(
   const child = spawn(nodeExecutable, [script, '--development'], {
     cwd: repoRoot,
     detached: process.platform !== 'win32',
-    env: { ...environment, MAKA_CLI_DEVELOPMENT_OUTPUT_ROOT: outputRoot },
+    env: {
+      ...environment,
+      MAKA_CLI_DEVELOPMENT_OUTPUT_ROOT: outputRoot,
+      MAKA_CLI_DEVELOPMENT_PEER_TARGET: peerTarget,
+    },
     stdio: ['ignore', 'pipe', 'pipe'],
     windowsHide: true,
   });
diff --git a/apps/desktop/src/main/runtime-host-ssh-terminal.ts b/apps/desktop/src/main/runtime-host-ssh-terminal.ts
index 1b2d82e21..97b133c39 100644
--- a/apps/desktop/src/main/runtime-host-ssh-terminal.ts
+++ b/apps/desktop/src/main/runtime-host-ssh-terminal.ts
@@ -67,6 +67,7 @@ import type {
   DesktopRuntimeHostSshTerminalSnapshot,
 } from '../preload/bridge-contract.js';
 import { createRuntimeHostFramedOutputFilter } from './runtime-host-framed-output.js';
+import type { DesktopRuntimeHostDevelopmentPeerTarget } from './runtime-host-setup-package.js';
 
 interface ActiveTerminal {
   readonly sessionId: string;
@@ -105,6 +106,12 @@ export interface DesktopRuntimeHostSshSetupInput {
   readonly signal?: AbortSignal;
 }
 
+export interface DesktopRuntimeHostSshTargetInput {
+  readonly destination: string;
+  readonly sshPort?: number;
+  readonly signal?: AbortSignal;
+}
+
 export interface DesktopRuntimeHostSshManagementInput {
   readonly destination: string;
   readonly sshPort?: number;
@@ -236,6 +243,9 @@ export function createDesktopRuntimeHostSshTerminal(input: {
     input: RuntimeHostSshOperatorActivationInput,
   ): Promise<RuntimeHostActivationResult>;
   openSshTunnel(input: RuntimeHostSshTunnelInput): Promise<RuntimeHostSshTunnel>;
+  resolveDevelopmentPeerTarget(
+    input: DesktopRuntimeHostSshTargetInput,
+  ): Promise<Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'>>;
   runSetup(
     input: DesktopRuntimeHostSshSetupInput,
     onProgress: (frame: Extract<RuntimeHostSetupFrame, { kind: 'progress' }>) => void,
@@ -603,6 +613,66 @@ export function createDesktopRuntimeHostSshTerminal(input: {
       }
       return tunnel;
     },
+    resolveDevelopmentPeerTarget: async (targetInput) => {
+      if (closed) throw new Error('Runtime Host SSH terminal is closed');
+      targetInput.signal?.throwIfAborted();
+      const destination = normalizeRuntimeHostSshDestination(targetInput.destination);
+      const sshPort = targetInput.sshPort === undefined
+        ? undefined
+        : requireSetupPort(targetInput.sshPort);
+      const marker = `__MAKA_RUNTIME_HOST_TARGET_${randomUUID().replaceAll('-', '')}__`;
+      const remoteCommand = `printf '${marker}%s:%s\\n' "$(uname -s)" "$(uname -m)"`;
+      let target: Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'> | undefined;
+      let failure: Error | undefined;
+      const filter = createRuntimeHostFramedOutputFilter({
+        prefix: marker,
+        pendingMaxBytes: 256,
+        decode: (line) => line.slice(marker.length).replaceAll('\r', '').trimEnd(),
+        label: 'Remote Runtime Host target detection',
+        onFrame: (identity) => {
+          if (target) {
+            failure = new Error('Remote Runtime Host target detection returned multiple results');
+            return;
+          }
+          const [system, machine, ...extra] = identity.split(':');
+          if (!system || !machine || extra.length > 0) {
+            failure = new Error('Remote Runtime Host target detection returned an invalid result');
+            return;
+          }
+          try {
+            target = runtimeHostDevelopmentPeerTargetFromUname(system, machine);
+          } catch (error) {
+            failure = error instanceof Error ? error : new Error(String(error));
+          }
+        },
+        onError: (error) => {
+          failure = error;
+        },
+      });
+      const { process, terminal } = startTerminalProcess(
+        'ssh',
+        sshRemoteCommandArgs(destination, sshPort, remoteCommand),
+        filter.push,
+        true,
+      );
+      const wait = await waitForTerminalProcess(process, {
+        signal: targetInput.signal,
+        timeoutMs: input.managementTimeoutMs ?? MANAGEMENT_TIMEOUT_MS,
+        stopGraceMs: input.processStopGraceMs,
+        onAbort: () => dismissPresentation(terminal),
+      }, input.terminateProcessTree);
+      if (wait.timedOut) throw new Error('Remote Runtime Host target detection timed out');
+      if (wait.exit.code !== 0) {
+        throw new Error(
+          `Remote Runtime Host target detection exited with code ${String(wait.exit.code)}`,
+        );
+      }
+      filter.finish();
+      if (failure) throw failure;
+      completePresentation(terminal);
+      if (!target) throw new Error('Remote Runtime Host target detection returned no result');
+      return target;
+    },
     runSetup: async (setupInput, onProgress, onComplete) => {
       if (closed) throw new Error('Runtime Host SSH terminal is closed');
       setupInput.signal?.throwIfAborted();
@@ -842,6 +912,21 @@ export function createDesktopRuntimeHostSshTerminal(input: {
   };
 }
 
+export function runtimeHostDevelopmentPeerTargetFromUname(
+  system: string,
+  machine: string,
+): Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'> {
+  const normalizedMachine = machine.toLowerCase();
+  if (system === 'Darwin' && normalizedMachine === 'arm64') return 'darwin-arm64';
+  if (system === 'Linux') {
+    if (normalizedMachine === 'x86_64') return 'linux-x64';
+    if (normalizedMachine === 'aarch64' || normalizedMachine === 'arm64') {
+      return 'linux-arm64';
+    }
+  }
+  throw new Error(`Direct peer is not available on ${system}/${machine}`);
+}
+
 function cancellableUntilComplete(signal: AbortSignal | undefined): {
   readonly signal: AbortSignal;
   commit(): boolean;
diff --git a/native/runtime-host-peer/build.mjs b/native/runtime-host-peer/build.mjs
index b103cfe15..9c9d77ffe 100644
--- a/native/runtime-host-peer/build.mjs
+++ b/native/runtime-host-peer/build.mjs
@@ -53,6 +53,13 @@ const targetPlatform = cargoTarget
       ? 'darwin'
       : 'linux'
   : process.platform;
+const targetArch = cargoTarget
+  ? cargoTarget.startsWith('aarch64-')
+    ? 'arm64'
+    : cargoTarget.startsWith('x86_64-')
+      ? 'x64'
+      : undefined
+  : process.arch;
 
 const library =
   targetPlatform === 'win32'
@@ -67,7 +74,11 @@ if (targetPlatform === 'darwin' && process.platform === 'darwin') {
     ...process.env,
   });
   await run('strip', ['-x', destination], root, { ...process.env });
-} else if (targetPlatform === 'linux' && process.platform === 'linux') {
+} else if (
+  targetPlatform === 'linux' &&
+  process.platform === 'linux' &&
+  targetArch === process.arch
+) {
   await run('strip', ['--strip-unneeded', destination], root, { ...process.env });
 }
 if ((await readFile(destination)).includes(Buffer.from(root))) {
diff --git a/scripts/release-cli-package.mjs b/scripts/release-cli-package.mjs
index 2a6a12a22..da67bac7e 100644
--- a/scripts/release-cli-package.mjs
+++ b/scripts/release-cli-package.mjs
@@ -58,6 +58,9 @@ const releaseRoot = join(cliSource, 'release');
 const artifactRoot = developmentBuild ? createDevelopmentArtifactRoot() : releaseRoot;
 const stageRoot = join(artifactRoot, 'package');
 const peerPrebuildTargets = ['darwin-arm64', 'linux-arm64', 'linux-x64', 'win32-x64'];
+const privatePeerTarget = developmentBuild
+  ? resolveDevelopmentPeerTarget()
+  : `${process.platform}-${process.arch}`;
 const unsupportedArguments = process.argv
   .slice(2)
   .filter((argument) => !['--allow-dirty', '--development'].includes(argument));
@@ -157,7 +160,7 @@ function packageCli(publishable) {
     entryCount: pack.entryCount,
   });
   const tarballPath = join(artifactRoot, pack.filename);
-  validatePackedFiles(pack.files, expectedDependencyManifests);
+  validatePackedFiles(pack.files, expectedDependencyManifests, publishable);
   const sha256 = digestFile(tarballPath);
   writeFileSync(`${tarballPath}.sha256`, `${sha256}  ${pack.filename}\n`, 'utf8');
   writeFileSync(
@@ -542,16 +545,15 @@ function copyReleaseDocuments() {
 function copyRuntimeHostPeerPrebuilds(publishable) {
   let sourceRoot = process.env.MAKA_RUNTIME_HOST_PEER_PREBUILDS?.trim();
   let generatedRoot;
-  let targets = peerPrebuildTargets;
+  const targets = publishable
+    ? peerPrebuildTargets
+    : privatePeerTarget === 'none'
+      ? []
+      : [privatePeerTarget];
+  if (targets.length === 0) return;
   if (!sourceRoot && !publishable) {
-    execFileSync(process.execPath, [join(repoRoot, 'native/runtime-host-peer/build.mjs')], {
-      cwd: repoRoot,
-      stdio: 'inherit',
-    });
-    const target = `${process.platform}-${process.arch}`;
-    if (!peerPrebuildTargets.includes(target)) {
-      throw new Error(`Direct peer is not supported on ${target}`);
-    }
+    const [target] = targets;
+    buildDevelopmentPeerAddon(target);
     sourceRoot = generatedRoot = mkdtempSync(join(tmpdir(), 'maka-runtime-host-peer-prebuilds-'));
     const targetRoot = join(sourceRoot, target);
     mkdirSync(targetRoot, { recursive: true, mode: 0o755 });
@@ -559,7 +561,6 @@ function copyRuntimeHostPeerPrebuilds(publishable) {
       join(repoRoot, 'native/runtime-host-peer/target/release/maka_runtime_host_peer.node'),
       join(targetRoot, 'maka_runtime_host_peer.node'),
     );
-    targets = [target];
   }
   if (!sourceRoot) {
     throw new Error('MAKA_RUNTIME_HOST_PEER_PREBUILDS must contain all release platform addons');
@@ -580,6 +581,60 @@ function copyRuntimeHostPeerPrebuilds(publishable) {
   }
 }
 
+function resolveDevelopmentPeerTarget() {
+  const configured = process.env.MAKA_CLI_DEVELOPMENT_PEER_TARGET?.trim();
+  const target = configured || `${process.platform}-${process.arch}`;
+  if (target !== 'none' && !peerPrebuildTargets.includes(target)) {
+    throw new Error(
+      `MAKA_CLI_DEVELOPMENT_PEER_TARGET must be none or a supported target; found ${target}`,
+    );
+  }
+  return target;
+}
+
+function buildDevelopmentPeerAddon(target) {
+  const hostTarget = `${process.platform}-${process.arch}`;
+  const buildScript = join(repoRoot, 'native/runtime-host-peer/build.mjs');
+  if (target === hostTarget) {
+    execFileSync(process.execPath, [buildScript], { cwd: repoRoot, stdio: 'inherit' });
+    return;
+  }
+  const rustTarget = {
+    'linux-arm64': 'aarch64-unknown-linux-gnu.2.28',
+    'linux-x64': 'x86_64-unknown-linux-gnu.2.28',
+  }[target];
+  if (!rustTarget) {
+    throw new Error(
+      `Cannot build the ${target} direct-peer addon from ${hostTarget}; run Desktop on that target or provide MAKA_RUNTIME_HOST_PEER_PREBUILDS`,
+    );
+  }
+  requireDevelopmentCommand(
+    'zig',
+    ['version'],
+    `Cross-compiling the ${target} direct-peer addon requires Zig on PATH (CI uses 0.16.x)`,
+  );
+  requireDevelopmentCommand(
+    'cargo-zigbuild',
+    ['--version'],
+    `Cross-compiling the ${target} direct-peer addon requires cargo-zigbuild (cargo install cargo-zigbuild --version 0.23.2 --locked)`,
+  );
+  execFileSync(process.execPath, [buildScript], {
+    cwd: repoRoot,
+    env: {
+      ...process.env,
+      MAKA_RUNTIME_HOST_PEER_CARGO_SUBCOMMAND: 'zigbuild',
+      MAKA_RUNTIME_HOST_PEER_CARGO_TARGET: rustTarget,
+    },
+    stdio: 'inherit',
+  });
+}
+
+function requireDevelopmentCommand(command, args, message) {
+  const result = spawnSync(command, args, { cwd: repoRoot, encoding: 'utf8' });
+  if (result.status === 0) return;
+  throw new Error(`${message}; install it before setting up this development Runtime Host`);
+}
+
 function writeReleaseManifest(cli, publishable) {
   const source = readJson(join(cliSource, 'package.json'));
   const root = readJson(join(repoRoot, 'package.json'));
@@ -690,9 +745,9 @@ function validateStaging(publishable) {
         (target) => `native/runtime-host-peer/prebuilds/${target}/maka_runtime_host_peer.node`,
       ),
     );
-  } else {
+  } else if (privatePeerTarget !== 'none') {
     required.push(
-      `native/runtime-host-peer/prebuilds/${process.platform}-${process.arch}/maka_runtime_host_peer.node`,
+      `native/runtime-host-peer/prebuilds/${privatePeerTarget}/maka_runtime_host_peer.node`,
     );
   }
   for (const path of required) {
@@ -745,7 +800,7 @@ function validateStaging(publishable) {
   }
 }
 
-function validatePackedFiles(files, expectedDependencyManifests) {
+function validatePackedFiles(files, expectedDependencyManifests, publishable) {
   const paths = files.map((file) => file.path);
   for (const file of files) {
     const { path } = file;
@@ -780,7 +835,7 @@ function validatePackedFiles(files, expectedDependencyManifests) {
     'node_modules/@maka/runtime/dist/workers/filesystem-worker.js',
     'node_modules/@maka/runtime-host/dist/execution-candidate-main.js',
     'packages/eval/harbor/relay_agent.py',
-    'native/runtime-host-peer/prebuilds/',
+    ...(publishable || privatePeerTarget !== 'none' ? ['native/runtime-host-peer/prebuilds/'] : []),
   ];
   for (const suffix of requiredPacked) {
     if (


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org