From 6cfc78a27ed23a5d2af41aa4c2f07b487e7ebdee Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Fri, 23 Oct 2026 08:13:56 -0400 Subject: [PATCH 1/2] =?UTF-8?q?fix(release):=20the=20two=20publish=20block?= =?UTF-8?q?ers=20=E2=80=94=20the=20sentinel=20rename=20+=20the=20robust=20?= =?UTF-8?q?npx=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open VSX's secret scanner blocked the v0.3.8 publish: the hashicorp-tf-password rule false-positives on 'UNARMED_PASSWORD = "__hub_unarmed__"' (a placeholder sentinel from the unarmed-hub work, entropy 3.17). Renamed to UNARMED_SENTINEL — the scanner's rule keys on *PASSWORD* assignments with quoted literals; the constant's meaning is unchanged (a sentinel, never a credential). The Marketplace step broke earlier: 'sh: 1: vsce: not found' — the runner's npx no longer resolves @vscode/vsce's bin via the bare package form (worked at v0.3.7, broke since; the runner's npm moved). The robust explicit form: npx --yes --package @vscode/vsce vsce publish. --- .github/workflows/release.yml | 2 +- packages/extension/src/amicode_service_runner.ts | 2 +- packages/extension/src/extension.ts | 6 +++--- packages/extension/src/server_handshake.ts | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 335eec597..8ff3fa8dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/packages/extension/src/amicode_service_runner.ts b/packages/extension/src/amicode_service_runner.ts index 1319e2cf2..ad81ec62e 100644 --- a/packages/extension/src/amicode_service_runner.ts +++ b/packages/extension/src/amicode_service_runner.ts @@ -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; diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 353e801cd..e3a26aebe 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -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"; @@ -1553,7 +1553,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { 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( @@ -1577,7 +1577,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { 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; diff --git a/packages/extension/src/server_handshake.ts b/packages/extension/src/server_handshake.ts index 61e5d9222..a6fa17d3b 100644 --- a/packages/extension/src/server_handshake.ts +++ b/packages/extension/src/server_handshake.ts @@ -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). @@ -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. */ @@ -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, From 43a740c7d3846f2002f6a937d41e8befcb0f2de9 Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Fri, 23 Oct 2026 08:44:47 -0400 Subject: [PATCH 2/2] test(extension): follow the UNARMED_SENTINEL rename in the handshake tests (#1847) --- packages/extension/test/adopt_live_server.test.ts | 8 ++++---- packages/extension/test/amicode_service_runner.test.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/extension/test/adopt_live_server.test.ts b/packages/extension/test/adopt_live_server.test.ts index a68ff5437..b52cc5079 100644 --- a/packages/extension/test/adopt_live_server.test.ts +++ b/packages/extension/test/adopt_live_server.test.ts @@ -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"; @@ -291,7 +291,7 @@ async function startUnarmedHub(): Promise<{ port: number; close: () => Promise new Promise((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). */ @@ -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, @@ -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); }); diff --git a/packages/extension/test/amicode_service_runner.test.ts b/packages/extension/test/amicode_service_runner.test.ts index 77c93e091..f34893897 100644 --- a/packages/extension/test/amicode_service_runner.test.ts +++ b/packages/extension/test/amicode_service_runner.test.ts @@ -36,7 +36,7 @@ import { serverAuthHeader, serverAuthToken } from "../src/server_auth"; import { readHandshake, classifyGate, - UNARMED_PASSWORD, + UNARMED_SENTINEL, PROTOCOL_VERSION, } from "../src/server_handshake"; @@ -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"; @@ -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);