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/09/16 05:13:14 UTC

(maka) branch feat/runtime-host-rust updated: feat(desktop): detect remote native targets without Node

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

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


The following commit(s) were added to refs/heads/feat/runtime-host-rust by this push:
     new 79b760cf85 feat(desktop): detect remote native targets without Node
79b760cf85 is described below

commit 79b760cf85047d1f51f9dcb5ed344486f1912412
Author: Wang <m4...@gmail.com>
AuthorDate: Wed Sep 16 13:13:08 2026 +0800

    feat(desktop): detect remote native targets without Node
    
    Generated-by: OpenAI Codex
---
 .../main/__tests__/runtime-host-management.test.ts |  4 +-
 .../main/__tests__/runtime-host-onboarding.test.ts |  9 +--
 .../__tests__/runtime-host-ssh-terminal.test.ts    | 61 +++++++++++++------
 .../__tests__/runtime-host-wsl-controller.test.ts  | 40 +++++++++++++
 apps/desktop/src/main/runtime-host-boot.ts         |  6 +-
 .../desktop/src/main/runtime-host-framed-output.ts | 10 +++-
 apps/desktop/src/main/runtime-host-management.ts   | 12 ++--
 apps/desktop/src/main/runtime-host-onboarding.ts   | 31 ++++++----
 apps/desktop/src/main/runtime-host-ssh-terminal.ts | 51 ++++++++--------
 apps/desktop/src/main/runtime-host-target.ts       | 69 ++++++++++++++++++++++
 .../src/main/runtime-host-wsl-controller.ts        | 26 ++++++++
 11 files changed, 246 insertions(+), 73 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 a72a25fd0c..bc8bf1eac7 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-management.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-management.test.ts
@@ -732,7 +732,7 @@ test('publishes update progress and waits for the managed profile to reconnect',
     runUpdateReconciliation: async () =>
       assert.fail('update reconciliation is not expected'),
     setupPackageMode: 'published',
-    resolveSshNodeIdentity: async () =>
+    resolveSshTargetIdentity: async () =>
       assert.fail('published update must not inspect the development target'),
     resolveUpdatePackage: () => ({ kind: 'npm', specifier: 'maka-agent@1.3.0' }),
     currentHostEpoch: () => 'host-before-update',
@@ -1499,7 +1499,7 @@ function unusedUpdateDependencies() {
       assert.fail('direct peer management is not expected'),
     directPeerClientAvailable: false,
     setupPackageMode: 'published' as const,
-    resolveSshNodeIdentity: async (): Promise<never> =>
+    resolveSshTargetIdentity: 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,
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 7f21b797eb..848c46f1b3 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-onboarding.test.ts
@@ -164,8 +164,8 @@ test('onboards WSL as a credential-free environment profile', async () => {
 
 test('projects WSL setup failures as recoverable onboarding state', async () => {
   const harness = createHarness({
-    runWslSetup: async () => {
-      throw new Error('WSL requires Linux Node.js');
+    resolveWslTargetIdentity: async () => {
+      throw new Error('Native Linux Runtime Host requires GNU libc');
     },
   });
 
@@ -176,7 +176,7 @@ test('projects WSL setup failures as recoverable onboarding state', async () =>
     }),
     {
       kind: 'failed',
-      message: 'WSL requires Linux Node.js',
+      message: 'Native Linux Runtime Host requires GNU libc',
       revision: 3,
     },
   );
@@ -329,7 +329,8 @@ function createHarness(overrides: HarnessOverrides = {}) {
       ...profiles,
     },
     setupPackageMode: 'published',
-    resolveSshNodeIdentity: async () => ({ platform: 'darwin', architecture: 'x64' }),
+    resolveSshTargetIdentity: async () => ({ platform: 'darwin', architecture: 'x64' }),
+    resolveWslTargetIdentity: async () => ({ platform: 'linux', architecture: 'x64', glibcVersion: '2.39' }),
     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-ssh-terminal.test.ts b/apps/desktop/src/main/__tests__/runtime-host-ssh-terminal.test.ts
index aa1a9a6d97..92a95b25c4 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
@@ -38,7 +38,7 @@ import {
 } from '@maka/runtime-host/operator';
 import {
   createDesktopRuntimeHostSshTerminal,
-  runtimeHostPeerTargetFromNode,
+  runtimeHostPeerTargetFromPlatform,
 } from '../runtime-host-ssh-terminal.js';
 import { waitFor as pollFor } from '@maka/core/test-only/async-primitives';
 
@@ -56,35 +56,58 @@ const WINDOWS_OPERATOR = {
   modulePath: 'C:\\Users\\operator\\AppData\\Local\\Maka\\operator.mjs',
 };
 
-test('maps supported SSH Node identities to peer targets', () => {
-  assert.equal(runtimeHostPeerTargetFromNode('linux', 'x64'), 'linux-x64');
-  assert.equal(runtimeHostPeerTargetFromNode('linux', 'arm64'), 'linux-arm64');
-  assert.equal(runtimeHostPeerTargetFromNode('darwin', 'arm64'), 'darwin-arm64');
-  assert.equal(runtimeHostPeerTargetFromNode('win32', 'x64'), 'win32-x64');
+test('maps supported SSH platform identities to peer targets', () => {
+  assert.equal(runtimeHostPeerTargetFromPlatform('linux', 'x64'), 'linux-x64');
+  assert.equal(runtimeHostPeerTargetFromPlatform('linux', 'arm64'), 'linux-arm64');
+  assert.equal(runtimeHostPeerTargetFromPlatform('darwin', 'arm64'), 'darwin-arm64');
+  assert.equal(runtimeHostPeerTargetFromPlatform('win32', 'x64'), 'win32-x64');
   assert.throws(
-    () => runtimeHostPeerTargetFromNode('linux', 'riscv64'),
+    () => runtimeHostPeerTargetFromPlatform('linux', 'riscv64'),
     /not available/u,
   );
 });
 
 test('detects the peer target through the bounded SSH preflight', async () => {
   const harness = createHarness('pending');
-  const detection = harness.terminal.resolveNodeIdentity({
+  const detection = harness.terminal.resolveTargetIdentity({
     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:x64\r\n`);
+  const suffix = command.match(/[0-9a-f]{32}__/u)?.[0];
+  assert.ok(suffix);
+  const marker = `__MAKA_RUNTIME_HOST_TARGET_${suffix}`;
+  assert.doesNotMatch(command, /\bnode\b/u);
+  harness.pty.emitData(`${marker}Linux:x86_64:glibc 2.39\r\n`);
   harness.pty.exit(0);
 
-  assert.deepEqual(await detection, { platform: 'linux', architecture: 'x64' });
+  assert.deepEqual(await detection, { platform: 'linux', architecture: 'x64', glibcVersion: '2.39' });
   assert.doesNotMatch(JSON.stringify(harness.events), /MAKA_RUNTIME_HOST_TARGET/u);
   await harness.terminal.close();
 });
 
-test('retries target detection through the POSIX login shell when Node is not on the default PATH', async (t) => {
+test('rejects unsupported SSH targets without crashing the output listener or retrying another OS', async () => {
+  for (const [value, expected] of [
+    ['Linux:x86_64:unknown', /requires GNU libc/u],
+    ['Linux:riscv64:glibc 2.39', /Unsupported.*architecture/u],
+    ['Linux:x86_64:glibc 2.39:extra', /Invalid.*result/u],
+  ] as const) {
+    const harness = createHarness('pending');
+    const detection = harness.terminal.resolveTargetIdentity({ destination: 'operator@example.com' });
+    const rejected = assert.rejects(detection, expected);
+    await waitFor(() => harness.pty.hasDataListener());
+    const suffix = harness.launchArgs[0]?.at(-1)?.match(/[0-9a-f]{32}__/u)?.[0];
+    assert.ok(suffix);
+    const marker = `__MAKA_RUNTIME_HOST_TARGET_${suffix}`;
+    harness.pty.emitData(`${marker}${value}\r\n`);
+    harness.pty.exit(0);
+    await rejected;
+    assert.equal(harness.launchArgs.length, 1);
+    await harness.terminal.close();
+  }
+});
+
+test('detects a Windows SSH target through PowerShell when no POSIX shell exists', async (t) => {
   const handlers = new Map<string, (...args: unknown[]) => unknown>();
   const launches: Array<{ args: string[]; pty: FakePty }> = [];
   const terminal = createDesktopRuntimeHostSshTerminal({
@@ -101,18 +124,20 @@ test('retries target detection through the POSIX login shell when Node is not on
   });
   t.after(() => terminal.close());
 
-  const detection = terminal.resolveNodeIdentity({ destination: 'operator@example.com' });
+  const detection = terminal.resolveTargetIdentity({ destination: 'operator@example.com' });
   await waitFor(() => launches.length === 1);
   launches[0]?.pty.exit(127);
   await waitFor(() => launches.length === 2);
   const command = launches[1]?.args.at(-1) ?? '';
-  const marker = command.match(/__MAKA_RUNTIME_HOST_TARGET_[0-9a-f]+__/u)?.[0];
+  const script = Buffer.from(command.split(' ').at(-1) ?? '', 'base64').toString('utf16le');
+  const marker = script.match(/__MAKA_RUNTIME_HOST_TARGET_[0-9a-f]+__/u)?.[0];
   assert.ok(marker);
-  assert.match(command, /\$\{SHELL:-\/bin\/sh\}.*-lic/u);
-  launches[1]?.pty.emitData(`${marker}linux:x64\r\n`);
+  assert.match(command, /powershell.exe .* -EncodedCommand/u);
+  assert.match(script, /OSArchitecture/u);
+  launches[1]?.pty.emitData(`${marker}Windows:X64:\r\n`);
   launches[1]?.pty.exit(0);
 
-  assert.deepEqual(await detection, { platform: 'linux', architecture: 'x64' });
+  assert.deepEqual(await detection, { platform: 'win32', architecture: 'x64' });
 });
 
 test('keeps a connecting SSH prompt observable across renderer presentation changes', async () => {
diff --git a/apps/desktop/src/main/__tests__/runtime-host-wsl-controller.test.ts b/apps/desktop/src/main/__tests__/runtime-host-wsl-controller.test.ts
index 78688e36cf..c73bf7344c 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-wsl-controller.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-wsl-controller.test.ts
@@ -31,6 +31,7 @@ import {
   RUNTIME_HOST_SETUP_SOURCE_PACKAGE_INTEGRITY_ENV,
 } from '@maka/runtime-host/operator';
 import {
+  resolveDesktopRuntimeHostWslTarget,
   runDesktopRuntimeHostWslManagement,
   runDesktopRuntimeHostWslSetup,
   runDesktopRuntimeHostWslUpdate,
@@ -43,6 +44,45 @@ const OPERATOR = {
   modulePath: '/home/operator/.local/share/maka/operator.mjs',
 };
 
+test('WSL target detection uses the selected Linux environment without Node and can be cancelled', async () => {
+  for (const cancel of [false, true]) {
+    const abort = new AbortController();
+    let killed = false;
+    const outcome = resolveDesktopRuntimeHostWslTarget({ distribution: 'Ubuntu Arm', signal: abort.signal }, {
+      wslExecutable: 'wsl.exe',
+      processFactory: (executable, args) => {
+        assert.equal(executable, 'wsl.exe');
+        assert.deepEqual(args.slice(0, 5), ['--distribution', 'Ubuntu Arm', '--exec', '/bin/sh', '-c']);
+        const script = args.at(-1) ?? '';
+        assert.doesNotMatch(script, /\bnode\b/u);
+        const suffix = script.match(/[0-9a-f]{32}__/u)?.[0];
+        assert.ok(suffix);
+        const marker = `__MAKA_RUNTIME_HOST_TARGET_${suffix}`;
+        const child = new EventEmitter() as ChildProcessWithoutNullStreams;
+        const stdout = new PassThrough();
+        const stderr = new PassThrough();
+        const finish = () => { stdout.end(); stderr.end(); child.emit('close', cancel ? 1 : 0, null); };
+        Object.assign(child, { stdin: new PassThrough(), stdout, stderr, kill: () => {
+          killed = true; process.nextTick(finish); return true;
+        } });
+        process.nextTick(() => {
+          if (cancel) { abort.abort(); return; }
+          stdout.write(`${marker}Linux:aarch64:glibc 2.39\n`);
+          finish();
+        });
+        return child;
+      },
+    });
+    if (cancel) {
+      await assert.rejects(outcome, /abort/iu);
+      assert.equal(killed, true);
+    } else {
+      assert.deepEqual(await outcome, { platform: 'linux', architecture: 'arm64', glibcVersion: '2.39' });
+      assert.equal(killed, false);
+    }
+  }
+});
+
 test('WSL management invokes the stable operator directly with the exact deployment target', async () => {
   let launch:
     | { readonly executable: string; readonly args: readonly string[]; readonly environment: NodeJS.ProcessEnv }
diff --git a/apps/desktop/src/main/runtime-host-boot.ts b/apps/desktop/src/main/runtime-host-boot.ts
index 68dd53e301..a3b5cc8b61 100644
--- a/apps/desktop/src/main/runtime-host-boot.ts
+++ b/apps/desktop/src/main/runtime-host-boot.ts
@@ -228,6 +228,7 @@ import {
 import {
   runDesktopRuntimeHostWslManagement,
   runDesktopRuntimeHostWslSetup,
+  resolveDesktopRuntimeHostWslTarget,
 } from './runtime-host-wsl-controller.js';
 import {
   createRuntimeHostSetupPackageResolver,
@@ -755,9 +756,10 @@ const runtimeHostOnboarding = createDesktopRuntimeHostOnboarding({
   profiles: runtimeHostProfileService,
   runSetup: runtimeHostSshTerminal.runSetup,
   runWslSetup: runDesktopRuntimeHostWslSetup,
+  resolveWslTargetIdentity: resolveDesktopRuntimeHostWslTarget,
   listWslDistributions: listRuntimeHostWslDistributions,
   setupPackageMode: runtimeHostSetupPackage.mode,
-  resolveSshNodeIdentity: runtimeHostSshTerminal.resolveNodeIdentity,
+  resolveSshTargetIdentity: runtimeHostSshTerminal.resolveTargetIdentity,
   resolveSetupPackage: runtimeHostSetupPackage.resolve,
   send: (snapshot) =>
     mainWindowController.send("runtime-host-onboarding:changed", snapshot),
@@ -828,7 +830,7 @@ const runtimeHostManagement = createDesktopRuntimeHostManagement({
   runUpdatePolicy: runtimeHostSshTerminal.runUpdatePolicy,
   runUpdateReconciliation: runtimeHostSshTerminal.runUpdateReconciliation,
   setupPackageMode: runtimeHostSetupPackage.mode,
-  resolveSshNodeIdentity: runtimeHostSshTerminal.resolveNodeIdentity,
+  resolveSshTargetIdentity: runtimeHostSshTerminal.resolveTargetIdentity,
   resolveUpdatePackage: runtimeHostSetupPackage.resolve,
   currentHostEpoch: (profileId) =>
     runtimeHostManager?.current(profileId)?.candidate?.client.hostEpoch,
diff --git a/apps/desktop/src/main/runtime-host-framed-output.ts b/apps/desktop/src/main/runtime-host-framed-output.ts
index 5a24062815..f843b4bb2d 100644
--- a/apps/desktop/src/main/runtime-host-framed-output.ts
+++ b/apps/desktop/src/main/runtime-host-framed-output.ts
@@ -58,9 +58,13 @@ export function createRuntimeHostFramedOutputFilter<Frame>(input: {
         }
         const line = pending.slice(0, newline + 1);
         pending = pending.slice(newline + 1);
-        const frame = input.decode(line);
-        if (frame) input.onFrame(frame);
-        else input.onError(new Error(`${input.label} returned an invalid result`));
+        try {
+          const frame = input.decode(line);
+          if (frame) input.onFrame(frame);
+          else input.onError(new Error(`${input.label} returned an invalid result`));
+        } catch (error) {
+          input.onError(error instanceof Error ? error : new Error(`${input.label} returned an invalid result`));
+        }
         continue;
       }
       if (finished) {
diff --git a/apps/desktop/src/main/runtime-host-management.ts b/apps/desktop/src/main/runtime-host-management.ts
index a7fa881c88..7579613490 100644
--- a/apps/desktop/src/main/runtime-host-management.ts
+++ b/apps/desktop/src/main/runtime-host-management.ts
@@ -53,11 +53,11 @@ import {
 } from './runtime-host-managed-services.js';
 import { requireProjectDirectoryRoots } from '../shared/runtime-host-project-directory-policy.js';
 import {
-  runtimeHostPeerTargetFromNode,
+  runtimeHostPeerTargetFromPlatform,
   type DesktopRuntimeHostSshCleanupInput,
   type DesktopRuntimeHostSshAccessInput,
   type DesktopRuntimeHostSshManagementInput,
-  type DesktopRuntimeHostSshNodeIdentity,
+  type RuntimeHostTargetIdentity,
   type DesktopRuntimeHostSshPeerManagementInput,
   type DesktopRuntimeHostSshUpdateInput,
   type DesktopRuntimeHostSshUpdatePolicyInput,
@@ -130,11 +130,11 @@ export function createDesktopRuntimeHostManagement(input: {
     onProgress: (phase: DesktopRuntimeHostManagementProgress['phase']) => void,
   ) => Promise<RuntimeHostServiceUpdateReconciliationTerminalFrame>;
   readonly setupPackageMode: 'published' | 'development';
-  readonly resolveSshNodeIdentity: (input: {
+  readonly resolveSshTargetIdentity: (input: {
     readonly destination: string;
     readonly sshPort?: number;
     readonly signal?: AbortSignal;
-  }) => Promise<DesktopRuntimeHostSshNodeIdentity>;
+  }) => Promise<RuntimeHostTargetIdentity>;
   readonly resolveUpdatePackage: (
     peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
   ) =>
@@ -657,11 +657,11 @@ export function createDesktopRuntimeHostManagement(input: {
       input.sendProgress({ profileId, phase: 'preparing_cli' });
       let peerTarget: DesktopRuntimeHostDevelopmentPeerTarget = 'none';
       if (input.setupPackageMode === 'development') {
-        const identity = await input.resolveSshNodeIdentity({
+        const identity = await input.resolveSshTargetIdentity({
           destination: transport.destination,
           ...(transport.sshPort === undefined ? {} : { sshPort: transport.sshPort }),
         });
-        peerTarget = runtimeHostPeerTargetFromNode(identity.platform, identity.architecture);
+        peerTarget = runtimeHostPeerTargetFromPlatform(identity.platform, identity.architecture);
       }
       const setupPackage = await input.resolveUpdatePackage(peerTarget);
       execute = () => input.runUpdate(
diff --git a/apps/desktop/src/main/runtime-host-onboarding.ts b/apps/desktop/src/main/runtime-host-onboarding.ts
index 54e55b0b92..8d792fe1f1 100644
--- a/apps/desktop/src/main/runtime-host-onboarding.ts
+++ b/apps/desktop/src/main/runtime-host-onboarding.ts
@@ -32,8 +32,8 @@ import type {
 } from '../preload/bridge-contract.js';
 import type { DesktopRuntimeHostProfileService } from './runtime-host-profile-service.js';
 import {
-  runtimeHostPeerTargetFromNode,
-  type DesktopRuntimeHostSshNodeIdentity,
+  runtimeHostPeerTargetFromPlatform,
+  type RuntimeHostTargetIdentity,
   type DesktopRuntimeHostSshSetupInput,
 } from './runtime-host-ssh-terminal.js';
 import type { DesktopRuntimeHostWslSetupInput } from './runtime-host-wsl-controller.js';
@@ -81,13 +81,17 @@ export function createDesktopRuntimeHostOnboarding(input: {
     readonly operator: RuntimeHostNodeOperatorCommand<'posix'> | RuntimeHostNativeOperatorCommand<'posix'>;
   }>;
   readonly listWslDistributions: () => Promise<readonly string[]>;
+  readonly resolveWslTargetIdentity: (input: {
+    readonly distribution: string;
+    readonly signal?: AbortSignal;
+  }) => Promise<Extract<RuntimeHostTargetIdentity, { platform: 'linux' }>>;
   readonly send: (snapshot: DesktopRuntimeHostOnboardingSnapshot) => void;
   readonly setupPackageMode: 'published' | 'development';
-  readonly resolveSshNodeIdentity: (input: {
+  readonly resolveSshTargetIdentity: (input: {
     readonly destination: string;
     readonly sshPort?: number;
     readonly signal?: AbortSignal;
-  }) => Promise<DesktopRuntimeHostSshNodeIdentity>;
+  }) => Promise<RuntimeHostTargetIdentity>;
   readonly resolveSetupPackage: (
     peerTarget: DesktopRuntimeHostDevelopmentPeerTarget,
     signal?: AbortSignal,
@@ -138,12 +142,17 @@ export function createDesktopRuntimeHostOnboarding(input: {
   ): Promise<DesktopRuntimeHostOnboardingSnapshot> => {
     try {
       if (request.kind === 'wsl') {
-        const setupPackage = await input.resolveSetupPackage('none', signal);
+        publish({ kind: 'running', phase: 'connecting_wsl' });
+        const target = await input.resolveWslTargetIdentity({ distribution: request.distribution, signal });
+        publish({ kind: 'running', phase: 'preparing_cli' });
+        const peerTarget = input.setupPackageMode === 'development'
+          ? runtimeHostPeerTargetFromPlatform(target.platform, target.architecture) : 'none';
+        const setupPackage = await input.resolveSetupPackage(peerTarget, signal);
         return await runWsl(request, setupPackage, signal);
       }
-      const nodeIdentity = await resolveSshNodeIdentity(request, signal);
+      const targetIdentity = await resolveSshTargetIdentity(request, signal);
       const peerTarget = input.setupPackageMode === 'development'
-        ? runtimeHostPeerTargetFromNode(nodeIdentity.platform, nodeIdentity.architecture)
+        ? runtimeHostPeerTargetFromPlatform(targetIdentity.platform, targetIdentity.architecture)
         : 'none';
       const setupPackage = await input.resolveSetupPackage(
         peerTarget,
@@ -167,7 +176,7 @@ export function createDesktopRuntimeHostOnboarding(input: {
           destination: request.destination,
           ...(request.sshPort === undefined ? {} : { sshPort: request.sshPort }),
           setupPackage,
-          remotePlatform: nodeIdentity.platform === 'win32' ? 'win32' : 'posix',
+          remotePlatform: targetIdentity.platform === 'win32' ? 'win32' : 'posix',
           lifecycle,
           principalId: `desktop:${input.clientInstanceId}`,
           ...(request.projectDirectoryRoots
@@ -238,12 +247,12 @@ export function createDesktopRuntimeHostOnboarding(input: {
     }
   };
 
-  const resolveSshNodeIdentity = async (
+  const resolveSshTargetIdentity = async (
     request: Extract<DesktopRuntimeHostOnboardingInput, { readonly kind: 'ssh' }>,
     signal: AbortSignal,
-  ): Promise<DesktopRuntimeHostSshNodeIdentity> => {
+  ): Promise<RuntimeHostTargetIdentity> => {
     publish({ kind: 'running', phase: 'connecting_ssh' });
-    const identity = await input.resolveSshNodeIdentity({
+    const identity = await input.resolveSshTargetIdentity({
       destination: request.destination,
       ...(request.sshPort === undefined ? {} : { sshPort: request.sshPort }),
       signal,
diff --git a/apps/desktop/src/main/runtime-host-ssh-terminal.ts b/apps/desktop/src/main/runtime-host-ssh-terminal.ts
index 9354cc1606..a6c1ed2b06 100644
--- a/apps/desktop/src/main/runtime-host-ssh-terminal.ts
+++ b/apps/desktop/src/main/runtime-host-ssh-terminal.ts
@@ -75,6 +75,13 @@ import type {
   DesktopRuntimeHostSshTerminalSnapshot,
 } from '../preload/bridge-contract.js';
 import { createRuntimeHostFramedOutputFilter } from './runtime-host-framed-output.js';
+import {
+  decodeRuntimeHostTarget,
+  posixRuntimeHostTargetProbe,
+  windowsRuntimeHostTargetProbe,
+  type RuntimeHostTargetIdentity,
+} from './runtime-host-target.js';
+export type { RuntimeHostTargetIdentity } from './runtime-host-target.js';
 import {
   runtimeHostSetupPackageVersion,
   type DesktopRuntimeHostDevelopmentPeerTarget,
@@ -125,11 +132,6 @@ export interface DesktopRuntimeHostSshTargetInput {
   readonly signal?: AbortSignal;
 }
 
-export interface DesktopRuntimeHostSshNodeIdentity {
-  readonly platform: string;
-  readonly architecture: string;
-}
-
 export interface DesktopRuntimeHostSshManagementInput {
   readonly destination: string;
   readonly sshPort?: number;
@@ -271,9 +273,9 @@ export function createDesktopRuntimeHostSshTerminal(input: {
     input: RuntimeHostSshOperatorActivationInput,
   ): Promise<RuntimeHostActivationResult>;
   openSshTunnel(input: RuntimeHostSshTunnelInput): Promise<RuntimeHostSshTunnel>;
-  resolveNodeIdentity(
+  resolveTargetIdentity(
     input: DesktopRuntimeHostSshTargetInput,
-  ): Promise<DesktopRuntimeHostSshNodeIdentity>;
+  ): Promise<RuntimeHostTargetIdentity>;
   runSetup(
     input: DesktopRuntimeHostSshSetupInput,
     onProgress: (frame: Extract<RuntimeHostSetupFrame, { kind: 'progress' }>) => void,
@@ -653,7 +655,7 @@ export function createDesktopRuntimeHostSshTerminal(input: {
       }
       return tunnel;
     },
-    resolveNodeIdentity: async (targetInput) => {
+    resolveTargetIdentity: async (targetInput) => {
       if (closed) throw new Error('Runtime Host SSH terminal is closed');
       targetInput.signal?.throwIfAborted();
       const destination = normalizeRuntimeHostSshDestination(targetInput.destination);
@@ -661,26 +663,21 @@ export function createDesktopRuntimeHostSshTerminal(input: {
         ? undefined
         : requireSetupPort(targetInput.sshPort);
       const marker = `__MAKA_RUNTIME_HOST_TARGET_${randomUUID().replaceAll('-', '')}__`;
-      const nodeProbe = `node -e "process.stdout.write('${marker}'+process.platform+':'+process.arch+'\\n')"`;
+      const deadline = Date.now() + 60_000;
       const detect = async (remoteCommand: string) => {
-        let identity: DesktopRuntimeHostSshNodeIdentity | undefined;
+        let identity: RuntimeHostTargetIdentity | undefined;
         let failure: Error | undefined;
         const filter = createRuntimeHostFramedOutputFilter({
           prefix: marker,
           pendingMaxBytes: 256,
-          decode: (line) => line.slice(marker.length).replaceAll('\r', '').trimEnd(),
+          decode: (line) => decodeRuntimeHostTarget(line.slice(marker.length).replaceAll('\r', '').trimEnd()),
           label: 'Remote Runtime Host target detection',
           onFrame: (value) => {
             if (identity) {
               failure = new Error('Remote Runtime Host target detection returned multiple results');
               return;
             }
-            const [platform, architecture, ...extra] = value.split(':');
-            if (!platform || !architecture || extra.length > 0) {
-              failure = new Error('Remote Runtime Host target detection returned an invalid result');
-              return;
-            }
-            identity = { platform, architecture };
+            identity = value;
           },
           onError: (error) => {
             failure = error;
@@ -694,7 +691,7 @@ export function createDesktopRuntimeHostSshTerminal(input: {
         );
         const wait = await waitForTerminalProcess(process, {
           signal: targetInput.signal,
-          timeoutMs: input.managementTimeoutMs ?? MANAGEMENT_TIMEOUT_MS,
+          timeoutMs: Math.max(1, Math.min(input.managementTimeoutMs ?? 60_000, deadline - Date.now())),
           stopGraceMs: input.processStopGraceMs,
           onAbort: () => dismissPresentation(terminal),
         }, input.terminateProcessTree);
@@ -704,27 +701,27 @@ export function createDesktopRuntimeHostSshTerminal(input: {
         completePresentation(terminal);
         return { identity, exitCode: wait.exit.code };
       };
-      const direct = await detect(nodeProbe);
+      const direct = await detect(`sh -c ${quotePosix(posixRuntimeHostTargetProbe(marker))}`);
       if (direct.exitCode === 0) {
         if (!direct.identity)
           throw new Error('Remote Runtime Host target detection returned no result');
         return direct.identity;
       }
-      if (direct.exitCode !== 127) {
+      if (direct.identity || ![1, 127, 9009].includes(direct.exitCode ?? -1)) {
         throw new Error(
           `Remote Runtime Host target detection exited with code ${String(direct.exitCode)}`,
         );
       }
-      const loginProbe = `exec "\${SHELL:-/bin/sh}" -lic ${quotePosix(`exec ${nodeProbe}`)}`;
-      const login = await detect(loginProbe);
-      if (login.exitCode !== 0) {
+      targetInput.signal?.throwIfAborted();
+      const windows = await detect(windowsRuntimeHostTargetProbe(marker));
+      if (windows.exitCode !== 0) {
         throw new Error(
-          `Remote Runtime Host target detection exited with code ${String(login.exitCode)}`,
+          `Remote Runtime Host target detection exited with code ${String(windows.exitCode)}`,
         );
       }
-      if (!login.identity)
+      if (!windows.identity)
         throw new Error('Remote Runtime Host target detection returned no result');
-      return login.identity;
+      return windows.identity;
     },
     runSetup: async (setupInput, onProgress, onComplete) => {
       if (closed) throw new Error('Runtime Host SSH terminal is closed');
@@ -983,7 +980,7 @@ export function createDesktopRuntimeHostSshTerminal(input: {
   };
 }
 
-export function runtimeHostPeerTargetFromNode(
+export function runtimeHostPeerTargetFromPlatform(
   platform: string,
   arch: string,
 ): Exclude<DesktopRuntimeHostDevelopmentPeerTarget, 'none'> {
diff --git a/apps/desktop/src/main/runtime-host-target.ts b/apps/desktop/src/main/runtime-host-target.ts
new file mode 100644
index 0000000000..02cf51a1f9
--- /dev/null
+++ b/apps/desktop/src/main/runtime-host-target.ts
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type RuntimeHostTargetIdentity =
+  | { readonly platform: 'linux'; readonly architecture: 'x64' | 'arm64'; readonly glibcVersion: string }
+  | { readonly platform: 'darwin' | 'win32'; readonly architecture: 'x64' | 'arm64' };
+
+/** OS tools only; shared by SSH preflight and WSL's actual Linux environment. */
+export function posixRuntimeHostTargetProbe(marker: string): string {
+  requireMarker(marker);
+  return [
+    'maka_os=$(uname -s) || exit 1',
+    'maka_arch=$(uname -m) || exit 1',
+    'maka_libc=',
+    'case "$maka_os" in Linux) maka_libc=$(getconf GNU_LIBC_VERSION 2>/dev/null) || maka_libc=unknown ;; Darwin) if [ "$(sysctl -in sysctl.proc_translated 2>/dev/null)" = 1 ]; then maka_arch=arm64; fi ;; *) exit 127 ;; esac',
+    // Split the marker so shell diagnostics echoing this command cannot be
+    // mistaken for a result. Keep the command single-line for csh/tcsh SSH shells.
+    `printf '%s%s%s:%s:%s\\n' '__MAKA_RUNTIME_HOST_TARGET_' '${marker.slice('__MAKA_RUNTIME_HOST_TARGET_'.length)}' "$maka_os" "$maka_arch" "$maka_libc"`,
+  ].join('; ');
+}
+
+export function windowsRuntimeHostTargetProbe(marker: string): string {
+  requireMarker(marker);
+  const script = [
+    "$ErrorActionPreference = 'Stop'",
+    "$architecture = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()",
+    `[Console]::WriteLine('${marker}Windows:' + $architecture + ':')`,
+  ].join('; ');
+  return `powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script, 'utf16le').toString('base64')}`;
+}
+
+export function decodeRuntimeHostTarget(value: string): RuntimeHostTargetIdentity {
+  const [os, cpu, libc, ...extra] = value.split(':');
+  if (extra.length || libc === undefined) throw new Error('Invalid Runtime Host target result');
+  const architecture = cpu === 'x86_64' || cpu === 'X64' ? 'x64'
+    : cpu === 'aarch64' || cpu === 'arm64' || cpu === 'Arm64' ? 'arm64' : undefined;
+  if (!architecture) throw new Error(`Unsupported Runtime Host architecture: ${cpu}`);
+  if (os === 'Linux') {
+    const version = /^glibc ([0-9]+\.[0-9]+)$/u.exec(libc)?.[1];
+    if (!version) throw new Error('Native Linux Runtime Host requires GNU libc; target libc could not be verified');
+    return { platform: 'linux', architecture, glibcVersion: version };
+  }
+  if ((os === 'Darwin' || os === 'Windows') && libc === '') {
+    return { platform: os === 'Darwin' ? 'darwin' : 'win32', architecture };
+  }
+  throw new Error(`Unsupported Runtime Host operating system: ${os}`);
+}
+
+function requireMarker(marker: string): void {
+  if (!/^__MAKA_RUNTIME_HOST_TARGET_[0-9a-f]+__$/u.test(marker)) {
+    throw new Error('Invalid Runtime Host target marker');
+  }
+}
diff --git a/apps/desktop/src/main/runtime-host-wsl-controller.ts b/apps/desktop/src/main/runtime-host-wsl-controller.ts
index 59ef1a7af1..2ae7b7b0d7 100644
--- a/apps/desktop/src/main/runtime-host-wsl-controller.ts
+++ b/apps/desktop/src/main/runtime-host-wsl-controller.ts
@@ -18,6 +18,7 @@
  */
 
 import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';
+import { randomUUID } from 'node:crypto';
 import type { Readable } from 'node:stream';
 import {
   normalizeRuntimeHostWslDistribution,
@@ -43,11 +44,36 @@ import {
 } from '@maka/runtime-host/operator';
 import { createRuntimeHostFramedOutputFilter } from './runtime-host-framed-output.js';
 import type { DesktopRuntimeHostSetupPackage } from './runtime-host-setup-package.js';
+import { decodeRuntimeHostTarget, posixRuntimeHostTargetProbe, type RuntimeHostTargetIdentity } from './runtime-host-target.js';
 
 const WSL_SETUP_TIMEOUT_MS = 10 * 60_000;
 const WSL_SETUP_OUTPUT_MAX_BYTES = 64 * 1024;
 const WSL_SETUP_STDERR_MAX_BYTES = 8 * 1024;
 
+export async function resolveDesktopRuntimeHostWslTarget(
+  input: { readonly distribution: string; readonly signal?: AbortSignal },
+  overrides: { readonly processFactory?: RuntimeHostWslProcessFactory; readonly wslExecutable?: string } = {},
+): Promise<Extract<RuntimeHostTargetIdentity, { platform: 'linux' }>> {
+  input.signal?.throwIfAborted();
+  const marker = `__MAKA_RUNTIME_HOST_TARGET_${randomUUID().replaceAll('-', '')}__`;
+  const child = (overrides.processFactory ?? spawnWsl)(
+    overrides.wslExecutable ?? resolveSystemRuntimeHostWslExecutable(),
+    ['--distribution', normalizeRuntimeHostWslDistribution(input.distribution), '--exec',
+      '/bin/sh', '-c', posixRuntimeHostTargetProbe(marker)],
+  );
+  const deadline = AbortSignal.timeout(30_000);
+  return runWslFramedProcess({
+    child, signal: input.signal ? AbortSignal.any([input.signal, deadline]) : deadline,
+    prefix: marker,
+    decode: (line) => decodeRuntimeHostTarget(line.slice(marker.length).replaceAll('\r', '').trimEnd()),
+    label: 'WSL Runtime Host target detection',
+    onFrame: (identity) => {
+      if (identity.platform !== 'linux') throw new Error('WSL did not report a Linux target');
+      return identity;
+    },
+  });
+}
+
 type RuntimeHostSetupCompleteFrame = Extract<RuntimeHostSetupFrame, { kind: 'complete' | 'existing_environment' }>;
 type RuntimeHostWslSetupCompleteFrame = Omit<RuntimeHostSetupCompleteFrame, 'operator'> & {
   readonly operator: RuntimeHostNodeOperatorCommand<'posix'> | RuntimeHostNativeOperatorCommand<'posix'>;


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