@vibedeckx/linux-x64 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +194 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -238010,6 +238010,20 @@ var import_fastify_plugin23 = __toESM(require_plugin2(), 1);
|
|
|
238010
238010
|
import { randomBytes as randomBytes2, createHash as createHash3, verify as cryptoVerify } from "crypto";
|
|
238011
238011
|
var MACHINE_HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
238012
238012
|
var routes22 = async (fastify2) => {
|
|
238013
|
+
fastify2.get("/api/reverse-connect/identity", async (req, reply) => {
|
|
238014
|
+
const token = req.headers["x-vibedeckx-connect-token"];
|
|
238015
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
238016
|
+
return reply.code(401).send({ error: "Connect token required" });
|
|
238017
|
+
}
|
|
238018
|
+
const server = await fastify2.storage.remoteServers.getByToken(token);
|
|
238019
|
+
if (!server) {
|
|
238020
|
+
return reply.code(401).send({ error: "Invalid token" });
|
|
238021
|
+
}
|
|
238022
|
+
if (server.connection_mode !== "inbound") {
|
|
238023
|
+
return reply.code(403).send({ error: "Server is not configured for inbound connections" });
|
|
238024
|
+
}
|
|
238025
|
+
return { serverId: server.id, name: server.name };
|
|
238026
|
+
});
|
|
238013
238027
|
fastify2.after(() => {
|
|
238014
238028
|
fastify2.get(
|
|
238015
238029
|
"/api/reverse-connect",
|
|
@@ -241163,6 +241177,10 @@ var createServer = async (opts) => {
|
|
|
241163
241177
|
if (!API_KEY) return done();
|
|
241164
241178
|
if (!req.url.startsWith("/api/")) return done();
|
|
241165
241179
|
if (req.method === "OPTIONS") return done();
|
|
241180
|
+
const pathname = req.url.split("?")[0];
|
|
241181
|
+
if (pathname === "/api/config" || pathname === "/api/reverse-connect/identity") {
|
|
241182
|
+
return done();
|
|
241183
|
+
}
|
|
241166
241184
|
const providedKey = req.headers["x-vibedeckx-api-key"] || req.query?.apiKey;
|
|
241167
241185
|
if (!providedKey && authEnabled) {
|
|
241168
241186
|
return done();
|
|
@@ -241204,7 +241222,12 @@ var createServer = async (opts) => {
|
|
|
241204
241222
|
server.get("/api/config", async () => ({
|
|
241205
241223
|
authEnabled,
|
|
241206
241224
|
clerkPublishableKey: authEnabled ? process.env.CLERK_PUBLISHABLE_KEY : void 0,
|
|
241207
|
-
localProjectsEnabled: !noLocalProjects
|
|
241225
|
+
localProjectsEnabled: !noLocalProjects,
|
|
241226
|
+
// Capability flag for the reverse-connect identity preflight. Workers
|
|
241227
|
+
// check this before calling /api/reverse-connect/identity so an auth
|
|
241228
|
+
// middleware 401 from an older hub (which lacks the endpoint AND its
|
|
241229
|
+
// exemptions) is never confused with a genuinely rejected token.
|
|
241230
|
+
reverseConnectIdentity: true
|
|
241208
241231
|
}));
|
|
241209
241232
|
server.register(shared_services_default, { storage: opts.storage, authEnabled });
|
|
241210
241233
|
server.register(import_websocket2.default);
|
|
@@ -241215,6 +241238,7 @@ var createServer = async (opts) => {
|
|
|
241215
241238
|
server.decorateRequest("auth", null);
|
|
241216
241239
|
server.addHook("preHandler", async (req, reply) => {
|
|
241217
241240
|
if (req.headers.upgrade?.toLowerCase() === "websocket") return;
|
|
241241
|
+
if (req.url.split("?")[0] === "/api/reverse-connect/identity") return;
|
|
241218
241242
|
const headers = new Headers();
|
|
241219
241243
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
241220
241244
|
if (value) headers.set(key, Array.isArray(value) ? value.join(",") : String(value));
|
|
@@ -241760,6 +241784,69 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
241760
241784
|
import { randomUUID as randomUUID22 } from "node:crypto";
|
|
241761
241785
|
import fs7 from "node:fs";
|
|
241762
241786
|
import path19 from "node:path";
|
|
241787
|
+
|
|
241788
|
+
// src/update-check.ts
|
|
241789
|
+
var SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/;
|
|
241790
|
+
async function fetchLatestPublishedVersion(pkg = "vibedeckx", fetchImpl = fetch) {
|
|
241791
|
+
try {
|
|
241792
|
+
const response = await fetchImpl(
|
|
241793
|
+
`https://registry.npmjs.org/${pkg}/latest`,
|
|
241794
|
+
{ signal: AbortSignal.timeout(4e3) }
|
|
241795
|
+
);
|
|
241796
|
+
if (!response.ok) return void 0;
|
|
241797
|
+
const body = await response.json();
|
|
241798
|
+
return typeof body.version === "string" && parseSemver(body.version) ? body.version : void 0;
|
|
241799
|
+
} catch {
|
|
241800
|
+
return void 0;
|
|
241801
|
+
}
|
|
241802
|
+
}
|
|
241803
|
+
function parseSemver(version2) {
|
|
241804
|
+
const match2 = SEMVER_RE.exec(version2);
|
|
241805
|
+
if (!match2) return void 0;
|
|
241806
|
+
return {
|
|
241807
|
+
core: [match2[1], match2[2], match2[3]],
|
|
241808
|
+
prerelease: match2[4]?.split(".")
|
|
241809
|
+
};
|
|
241810
|
+
}
|
|
241811
|
+
function compareNumericStrings(a, b2) {
|
|
241812
|
+
if (a.length !== b2.length) return a.length - b2.length;
|
|
241813
|
+
return a < b2 ? -1 : a > b2 ? 1 : 0;
|
|
241814
|
+
}
|
|
241815
|
+
function comparePrereleaseIdentifiers(a, b2) {
|
|
241816
|
+
const aNumeric = /^\d+$/.test(a);
|
|
241817
|
+
const bNumeric = /^\d+$/.test(b2);
|
|
241818
|
+
if (aNumeric && bNumeric) return compareNumericStrings(a, b2);
|
|
241819
|
+
if (aNumeric !== bNumeric) return aNumeric ? -1 : 1;
|
|
241820
|
+
return a < b2 ? -1 : a > b2 ? 1 : 0;
|
|
241821
|
+
}
|
|
241822
|
+
function compareSemver(a, b2) {
|
|
241823
|
+
for (let i = 0; i < 3; i += 1) {
|
|
241824
|
+
const compared = compareNumericStrings(a.core[i], b2.core[i]);
|
|
241825
|
+
if (compared !== 0) return compared;
|
|
241826
|
+
}
|
|
241827
|
+
if (!a.prerelease && !b2.prerelease) return 0;
|
|
241828
|
+
if (!a.prerelease) return 1;
|
|
241829
|
+
if (!b2.prerelease) return -1;
|
|
241830
|
+
const length = Math.max(a.prerelease.length, b2.prerelease.length);
|
|
241831
|
+
for (let i = 0; i < length; i += 1) {
|
|
241832
|
+
const aId = a.prerelease[i];
|
|
241833
|
+
const bId = b2.prerelease[i];
|
|
241834
|
+
if (aId === void 0) return -1;
|
|
241835
|
+
if (bId === void 0) return 1;
|
|
241836
|
+
const compared = comparePrereleaseIdentifiers(aId, bId);
|
|
241837
|
+
if (compared !== 0) return compared;
|
|
241838
|
+
}
|
|
241839
|
+
return 0;
|
|
241840
|
+
}
|
|
241841
|
+
function compareUpdateStatus(current, latest) {
|
|
241842
|
+
if (latest === void 0) return "unknown";
|
|
241843
|
+
const currentParsed = parseSemver(current);
|
|
241844
|
+
const latestParsed = parseSemver(latest);
|
|
241845
|
+
if (!currentParsed || !latestParsed) return "unknown";
|
|
241846
|
+
return compareSemver(latestParsed, currentParsed) > 0 ? "update-available" : "up-to-date";
|
|
241847
|
+
}
|
|
241848
|
+
|
|
241849
|
+
// src/connect-daemon.ts
|
|
241763
241850
|
var CONNECT_DAEMON_CHILD_ENV = "VIBEDECKX_INTERNAL_CONNECT_DAEMON";
|
|
241764
241851
|
var CONNECT_DAEMON_TOKEN_ENV = "VIBEDECKX_INTERNAL_CONNECT_TOKEN";
|
|
241765
241852
|
function buildDaemonChildArgs(argv) {
|
|
@@ -242307,18 +242394,31 @@ function inspectDaemonState(dataDir) {
|
|
|
242307
242394
|
const actualTicks = readLinuxProcessStartTicks(parsed.pid);
|
|
242308
242395
|
return actualTicks === parsed.processStartTicks ? { kind: "running", state: parsed } : { kind: "stale", state: parsed };
|
|
242309
242396
|
}
|
|
242310
|
-
function
|
|
242397
|
+
function describeUpdateStatus(current, latest) {
|
|
242398
|
+
switch (compareUpdateStatus(current, latest)) {
|
|
242399
|
+
case "update-available":
|
|
242400
|
+
return `(update available: ${latest})`;
|
|
242401
|
+
case "up-to-date":
|
|
242402
|
+
return "(up to date)";
|
|
242403
|
+
case "unknown":
|
|
242404
|
+
return "(update check failed)";
|
|
242405
|
+
}
|
|
242406
|
+
}
|
|
242407
|
+
async function describeConnectDaemon(dataDir, fetchLatestVersion = fetchLatestPublishedVersion) {
|
|
242311
242408
|
const inspection = inspectDaemonState(dataDir);
|
|
242312
242409
|
switch (inspection.kind) {
|
|
242313
|
-
case "running":
|
|
242410
|
+
case "running": {
|
|
242411
|
+
const latest = await fetchLatestVersion();
|
|
242314
242412
|
return {
|
|
242315
242413
|
exitCode: 0,
|
|
242316
242414
|
message: [
|
|
242317
242415
|
`Running (PID ${inspection.state.pid}, since ${inspection.state.startedAt})`,
|
|
242416
|
+
`Version: ${inspection.state.version} ${describeUpdateStatus(inspection.state.version, latest)}`,
|
|
242318
242417
|
`Target: ${inspection.state.connectTo}`,
|
|
242319
242418
|
`Logs: ${path19.join(dataDir, "logs", "vibedeckx.log")}`
|
|
242320
242419
|
].join("\n")
|
|
242321
242420
|
};
|
|
242421
|
+
}
|
|
242322
242422
|
case "missing":
|
|
242323
242423
|
return { exitCode: 1, message: "Vibedeckx connect is not running" };
|
|
242324
242424
|
case "stale":
|
|
@@ -242541,6 +242641,78 @@ function removeVerifiedStaleStateUnlocked(dataDir) {
|
|
|
242541
242641
|
}
|
|
242542
242642
|
}
|
|
242543
242643
|
|
|
242644
|
+
// src/connect-preflight.ts
|
|
242645
|
+
var CONNECT_IDENTITY_HEADER = "x-vibedeckx-connect-token";
|
|
242646
|
+
var PINNED_IDENTITY_SETTING_PREFIX = "reverse_pinned_identity:";
|
|
242647
|
+
function canonicalizeHubUrl(raw) {
|
|
242648
|
+
let url2;
|
|
242649
|
+
try {
|
|
242650
|
+
url2 = new URL(raw);
|
|
242651
|
+
} catch {
|
|
242652
|
+
throw new Error(`Invalid --connect-to URL: ${raw}`);
|
|
242653
|
+
}
|
|
242654
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
242655
|
+
throw new Error(`--connect-to must be an http(s) URL, got: ${raw}`);
|
|
242656
|
+
}
|
|
242657
|
+
const pathname = url2.pathname.replace(/\/+$/, "");
|
|
242658
|
+
return `${url2.origin}${pathname}`;
|
|
242659
|
+
}
|
|
242660
|
+
function parsePin(raw) {
|
|
242661
|
+
try {
|
|
242662
|
+
const parsed = JSON.parse(raw);
|
|
242663
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
242664
|
+
} catch {
|
|
242665
|
+
}
|
|
242666
|
+
return {};
|
|
242667
|
+
}
|
|
242668
|
+
async function preflightIdentityCheck(opts) {
|
|
242669
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
242670
|
+
const hubUrl = canonicalizeHubUrl(opts.connectTo);
|
|
242671
|
+
let config2;
|
|
242672
|
+
try {
|
|
242673
|
+
const res2 = await fetchImpl(`${hubUrl}/api/config`);
|
|
242674
|
+
if (res2.ok) config2 = await res2.json();
|
|
242675
|
+
} catch {
|
|
242676
|
+
}
|
|
242677
|
+
if (config2?.reverseConnectIdentity !== true) return { checked: false };
|
|
242678
|
+
const res = await fetchImpl(`${hubUrl}/api/reverse-connect/identity`, {
|
|
242679
|
+
headers: { [CONNECT_IDENTITY_HEADER]: opts.token }
|
|
242680
|
+
});
|
|
242681
|
+
if (res.status === 401) {
|
|
242682
|
+
throw new Error("Connect token was rejected by the server (invalid or revoked)");
|
|
242683
|
+
}
|
|
242684
|
+
if (res.status === 403) {
|
|
242685
|
+
throw new Error("This token's remote is not configured for inbound connections");
|
|
242686
|
+
}
|
|
242687
|
+
if (!res.ok) {
|
|
242688
|
+
throw new Error(`Identity preflight failed with status ${res.status}`);
|
|
242689
|
+
}
|
|
242690
|
+
const body = await res.json();
|
|
242691
|
+
if (!body || typeof body.serverId !== "string" || body.serverId.length === 0) {
|
|
242692
|
+
throw new Error("Malformed identity response from server");
|
|
242693
|
+
}
|
|
242694
|
+
const identity = { serverId: body.serverId, name: body.name ?? "" };
|
|
242695
|
+
const key = `${PINNED_IDENTITY_SETTING_PREFIX}${hubUrl}`;
|
|
242696
|
+
const desired = JSON.stringify(identity);
|
|
242697
|
+
if (opts.force) {
|
|
242698
|
+
await opts.settings.set(key, desired);
|
|
242699
|
+
return { checked: true, identity };
|
|
242700
|
+
}
|
|
242701
|
+
const persisted = await opts.settings.getOrCreate(key, () => desired);
|
|
242702
|
+
const pinned = parsePin(persisted);
|
|
242703
|
+
if (pinned.serverId && pinned.serverId !== identity.serverId) {
|
|
242704
|
+
const pinnedLabel = pinned.name || pinned.serverId;
|
|
242705
|
+
const tokenLabel = identity.name || identity.serverId;
|
|
242706
|
+
throw new Error(
|
|
242707
|
+
`This machine previously served remote "${pinnedLabel}" on ${hubUrl}, but the provided token belongs to "${tokenLabel}". If this is intentional, re-run with --force to re-pin.`
|
|
242708
|
+
);
|
|
242709
|
+
}
|
|
242710
|
+
if (!pinned.serverId || pinned.name !== identity.name) {
|
|
242711
|
+
await opts.settings.set(key, desired);
|
|
242712
|
+
}
|
|
242713
|
+
return { checked: true, identity };
|
|
242714
|
+
}
|
|
242715
|
+
|
|
242544
242716
|
// ../../node_modules/.pnpm/open@10.2.0/node_modules/open/index.js
|
|
242545
242717
|
import process8 from "node:process";
|
|
242546
242718
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -243237,6 +243409,11 @@ var connectCommand = buildCommand({
|
|
|
243237
243409
|
brief: "Run in the background after initialization (Linux only)",
|
|
243238
243410
|
optional: true
|
|
243239
243411
|
},
|
|
243412
|
+
force: {
|
|
243413
|
+
kind: "boolean",
|
|
243414
|
+
brief: "Re-pin this machine to the token's remote, overriding the pinned-identity mismatch guard",
|
|
243415
|
+
optional: true
|
|
243416
|
+
},
|
|
243240
243417
|
port: {
|
|
243241
243418
|
kind: "parsed",
|
|
243242
243419
|
parse: parseInt,
|
|
@@ -243336,6 +243513,17 @@ var connectCommand = buildCommand({
|
|
|
243336
243513
|
console.log("Starting vibedeckx in reverse-connect mode...");
|
|
243337
243514
|
const dbPath = path21.join(dataDir, "data.sqlite");
|
|
243338
243515
|
storage = await createSqliteStorage(dbPath);
|
|
243516
|
+
const preflight = await preflightIdentityCheck({
|
|
243517
|
+
connectTo: flags["connect-to"],
|
|
243518
|
+
token,
|
|
243519
|
+
settings: storage.settings,
|
|
243520
|
+
force: flags.force ?? false
|
|
243521
|
+
});
|
|
243522
|
+
if (preflight.checked && preflight.identity) {
|
|
243523
|
+
console.log(
|
|
243524
|
+
`Remote identity verified: ${preflight.identity.name || preflight.identity.serverId}`
|
|
243525
|
+
);
|
|
243526
|
+
}
|
|
243339
243527
|
server = await createServer({
|
|
243340
243528
|
storage,
|
|
243341
243529
|
acceptRemote: true,
|
|
@@ -243408,7 +243596,9 @@ var connectStatusCommand = buildCommand({
|
|
|
243408
243596
|
parameters: { flags: connectManagementFlags },
|
|
243409
243597
|
func: async (flags) => {
|
|
243410
243598
|
assertConnectDaemonPlatform();
|
|
243411
|
-
const result = describeConnectDaemon(
|
|
243599
|
+
const result = await describeConnectDaemon(
|
|
243600
|
+
flags["data-dir"] ?? VIBEDECKX_HOME
|
|
243601
|
+
);
|
|
243412
243602
|
return reportDaemonCommandResult(result);
|
|
243413
243603
|
},
|
|
243414
243604
|
docs: { brief: "Show the connect daemon status" }
|