Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ jobs:
run: |
FAILED=0
for V in packages/extension/amicode-linux-x64.vsix packages/extension/amicode-linux-arm64.vsix packages/extension/amicode-darwin-arm64.vsix packages/extension/amicode-win32-x64.vsix packages/extension/amicode-win32-arm64.vsix packages/extension/amicode-darwin-x64.vsix; do
OUTPUT=$(npx --yes @vscode/vsce publish --packagePath "$V" 2>&1) && { echo "$OUTPUT"; continue; }
OUTPUT=$(npx --yes --package @vscode/vsce vsce publish --packagePath "$V" 2>&1) && { echo "$OUTPUT"; continue; }
echo "$OUTPUT"
echo "$OUTPUT" | grep -q "already exists" || FAILED=1
done
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/src/amicode_service_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export interface AmicodeServiceRunnerOptions {
* writes the handshake record after the engine is healthy AND the service is
* up, and removes it on shutdown (BEFORE killing the engine — order matters:
* the extension must not read a handshake for a dying engine). The handshake
* carries the UNARMED_PASSWORD sentinel (the hub engine is unarmed, so the
* carries the UNARMED_SENTINEL sentinel (the hub engine is unarmed, so the
* password challenge is vacuously true). Default: undefined (no handshake —
* backward compatible, same pattern as pidFile). */
handshakePath?: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ import { buildDeviceStatus, nextActions, capabilityHint, type DriveLine } from "
import { SchusterJobServer } from "./qick_client";
import { adoptOrSpawn, buildLiveDeps, isPidAlive, reclaimOrphanPort, auditAdoptedEngine, restartAdoptedEngine, readoptHubInPlace, sweepStrayEngines, listOpencodeEngines, hasLiveClient, reclaimEnginePid, planEngineRestart, planServerActivation, adoptedTheHub, probeUnarmedHub, shouldRideDeterministicHub, planHubYieldBack } from "./server_lifecycle";
import { hubEnginePortFor } from "./amicode_service/fleet_hub_service";
import { handshakePath, readHandshake, deleteHandshake, serverLogPath, coldSpawnHandshakeHook, hashFile, hashString, UNARMED_PASSWORD, isUnarmedHandshake } from "./server_handshake";
import { handshakePath, readHandshake, deleteHandshake, serverLogPath, coldSpawnHandshakeHook, hashFile, hashString, UNARMED_SENTINEL, isUnarmedHandshake } from "./server_handshake";
import { startKeepalive, stopKeepalive, readGraceSeconds, pingKeepalive } from "./server_keepalive";
import { FleetPollHysteresis } from "./fleet_poll_hysteresis";
import { FleetPostureStateWriter } from "./fleet_posture_state";
Expand Down Expand Up @@ -1553,7 +1553,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
adoptedPort = hubEnginePort;
adoptedPid = hubPid;
adoptedUnarmed = true;
serverPassword = UNARMED_PASSWORD;
serverPassword = UNARMED_SENTINEL;
serverAuthHeaders.Authorization = serverAuthHeader(serverPassword);
opencodeReadyUrl = new URL(`http://127.0.0.1:${hubEnginePort}`);
opencodeChannel.appendLine(
Expand All @@ -1577,7 +1577,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
adoptedPort = lifecycleResult.port;
adoptedPid = lifecycleResult.pid;
adoptedHashes = lifecycleResult.adoptedHashes;
adoptedUnarmed = lifecycleResult.password === UNARMED_PASSWORD;
adoptedUnarmed = lifecycleResult.password === UNARMED_SENTINEL;
// Replace the minted password with the recorded one — the surviving
// server was spawned with it, so every auth surface must carry it.
serverPassword = lifecycleResult.password;
Expand Down
8 changes: 4 additions & 4 deletions packages/extension/src/server_handshake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ export function writeHandshake(record: HandshakeRecord, filePath?: string): void
* password is meaningless. The sentinel tells the adoption gate that the
* "password challenge" for this server is vacuously true — the health check IS
* the ownership proof for an unarmed engine behind the SSH tunnel. */
export const UNARMED_PASSWORD = "__hub_unarmed__";
export const UNARMED_SENTINEL = "__hub_unarmed__";

/** True when the handshake record was written by an unarmed hub engine. */
export function isUnarmedHandshake(record: HandshakeRecord): boolean {
return record.password === UNARMED_PASSWORD;
return record.password === UNARMED_SENTINEL;
}

/** Default PID-liveness probe (signal 0 = existence check, no signal sent).
Expand All @@ -106,7 +106,7 @@ export interface WriteHubHandshakeOpts {
}

/** Write the hub engine's handshake record after it passes its health check.
* The hub is unarmed, so the password field carries the UNARMED_PASSWORD
* The hub is unarmed, so the password field carries the UNARMED_SENTINEL
* sentinel. The adoption gate verifies reachability-only for unarmed engines
* (the SSH tunnel is the auth boundary). Generates startedAt and stamps
* PROTOCOL_VERSION automatically. */
Expand All @@ -116,7 +116,7 @@ export function writeHubHandshake(opts: WriteHubHandshakeOpts): void {
port: opts.port,
pid: opts.pid,
startedAt: new Date().toISOString(),
password: UNARMED_PASSWORD,
password: UNARMED_SENTINEL,
binaryHash: opts.binaryHash,
configHash: opts.configHash,
protocolVersion: PROTOCOL_VERSION,
Expand Down
8 changes: 4 additions & 4 deletions packages/extension/test/adopt_live_server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
adoptOrSpawn,
buildLiveDeps,
} from "../src/server_lifecycle";
import { coldSpawnHandshakeHook, readHandshake, writeHandshake, writeHubHandshake, UNARMED_PASSWORD, PROTOCOL_VERSION } from "../src/server_handshake";
import { coldSpawnHandshakeHook, readHandshake, writeHandshake, writeHubHandshake, UNARMED_SENTINEL, PROTOCOL_VERSION } from "../src/server_handshake";
import { serverAuthHeader } from "../src/server_auth";
import { windowModeFromRemoteName } from "../src/fleet_window_mode_state";
import { divertToFleetRelay, type FleetTopologyState } from "../src/fleet_topology";
Expand Down Expand Up @@ -291,7 +291,7 @@ async function startUnarmedHub(): Promise<{ port: number; close: () => Promise<v
return { port, close: () => new Promise<void>((resolve) => server.close(() => resolve())) };
}

/** Sentinel password for the unarmed hub engine — matches UNARMED_PASSWORD
/** Sentinel password for the unarmed hub engine — matches UNARMED_SENTINEL
* from server_handshake.ts (imported above). */

/** Plant a hub-style handshake (unarmed sentinel password, current protocol). */
Expand All @@ -306,7 +306,7 @@ function plantHubHandshake(
port,
pid: overrides.pid ?? process.pid,
startedAt: new Date().toISOString(),
password: UNARMED_PASSWORD,
password: UNARMED_SENTINEL,
binaryHash: "hub-bin",
configHash: "hub-cfg",
protocolVersion: overrides.protocolVersion,
Expand Down Expand Up @@ -410,7 +410,7 @@ describe("#1576 hub engine adoption — editor adopts the hub, no rival engine",
if (hs.status !== "ok") return;
expect(hs.record.port).toBe(12345);
expect(hs.record.pid).toBe(999);
expect(hs.record.password).toBe(UNARMED_PASSWORD);
expect(hs.record.password).toBe(UNARMED_SENTINEL);
expect(hs.record.protocolVersion).toBe(PROTOCOL_VERSION);
expect(hs.record.dbPath).toBe(DB);
});
Expand Down
6 changes: 3 additions & 3 deletions packages/extension/test/amicode_service_runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { serverAuthHeader, serverAuthToken } from "../src/server_auth";
import {
readHandshake,
classifyGate,
UNARMED_PASSWORD,
UNARMED_SENTINEL,
PROTOCOL_VERSION,
} from "../src/server_handshake";

Expand Down Expand Up @@ -872,7 +872,7 @@ createServer((_req, res) => {
return dir;
}

it("AC1: on boot, the handshake file exists and contains port, password (UNARMED_PASSWORD), and dbPath", async () => {
it("AC1: on boot, the handshake file exists and contains port, password (UNARMED_SENTINEL), and dbPath", async () => {
const dir = mkdtempSync(join(tmpdir(), "amicode-runner-handshake-write-"));
const hsPath = join(dir, "handshake.json");
const dbPath = "/tmp/fake-db.db";
Expand All @@ -896,7 +896,7 @@ createServer((_req, res) => {

// The record must carry the right fields.
expect(result.record.port).toBeGreaterThan(0);
expect(result.record.password).toBe(UNARMED_PASSWORD);
expect(result.record.password).toBe(UNARMED_SENTINEL);
expect(result.record.dbPath).toBe(dbPath);
expect(result.record.pid).toBe(boot.engine.pid);
expect(result.record.protocolVersion).toBe(PROTOCOL_VERSION);
Expand Down
Loading