@vibedeckx/linux-x64 0.3.1 → 0.3.2
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 +181 -66
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -186618,20 +186618,6 @@ var createAgentSessionRepos = (kdb, h) => ({
|
|
|
186618
186618
|
const row = await kdb.selectFrom("agent_sessions").select(kdb.fn.countAll().as("count")).where("project_id", "=", projectId).where("status", "=", "running").executeTakeFirstOrThrow();
|
|
186619
186619
|
return Number(row.count);
|
|
186620
186620
|
},
|
|
186621
|
-
countAttentionByProject: async (projectId) => {
|
|
186622
|
-
const row = await kdb.selectFrom("agent_sessions").select(kdb.fn.countAll().as("count")).where("project_id", "=", projectId).where((eb) => eb.or([
|
|
186623
|
-
eb("status", "=", "error"),
|
|
186624
|
-
eb.and([
|
|
186625
|
-
eb("status", "=", "stopped"),
|
|
186626
|
-
eb("last_user_message_at", "is not", null),
|
|
186627
|
-
eb.or([
|
|
186628
|
-
eb("last_completed_at", "is", null),
|
|
186629
|
-
eb("last_completed_at", "<", eb.ref("last_user_message_at"))
|
|
186630
|
-
])
|
|
186631
|
-
])
|
|
186632
|
-
])).executeTakeFirstOrThrow();
|
|
186633
|
-
return Number(row.count);
|
|
186634
|
-
},
|
|
186635
186621
|
getByBranch: async (projectId, branch) => {
|
|
186636
186622
|
const row = await kdb.selectFrom("agent_sessions").selectAll().where("project_id", "=", projectId).where("branch", "=", branch).orderBy("updated_at", "desc").limit(1).executeTakeFirst();
|
|
186637
186623
|
return row ? mapAgentSession(row) : void 0;
|
|
@@ -187159,14 +187145,9 @@ var createSearchCacheRepos = (kdb, _h) => ({
|
|
|
187159
187145
|
},
|
|
187160
187146
|
countRemoteSessionActivityByProject: async (projectId) => {
|
|
187161
187147
|
const row = await remoteSessionScope(kdb, projectId).select([
|
|
187162
|
-
sql`coalesce(sum(case when c.status = 'running' then 1 else 0 end), 0)`.as("running")
|
|
187163
|
-
sql`coalesce(sum(case
|
|
187164
|
-
when c.status = 'error' then 1
|
|
187165
|
-
when c.status = 'stopped' and c.last_user_message_at is not null
|
|
187166
|
-
and (c.last_completed_at is null or c.last_completed_at < c.last_user_message_at) then 1
|
|
187167
|
-
else 0 end), 0)`.as("failed")
|
|
187148
|
+
sql`coalesce(sum(case when c.status = 'running' then 1 else 0 end), 0)`.as("running")
|
|
187168
187149
|
]).executeTakeFirstOrThrow();
|
|
187169
|
-
return { running: Number(row.running)
|
|
187150
|
+
return { running: Number(row.running) };
|
|
187170
187151
|
},
|
|
187171
187152
|
updateRemoteSessionActivity: async (entry) => {
|
|
187172
187153
|
return kdb.transaction().execute(async (trx) => {
|
|
@@ -234438,7 +234419,8 @@ var ProjectChatManager = class {
|
|
|
234438
234419
|
this.startupReconciliationDeadlineMs = Math.max(10, options.startupReconciliationDeadlineMs ?? 1e3);
|
|
234439
234420
|
this.reconciliationOperationTimeoutMs = Math.max(10, options.reconciliationOperationTimeoutMs ?? 250);
|
|
234440
234421
|
this.recoveryPageSize = Math.max(1, Math.min(100, options.recoveryPageSize ?? 25));
|
|
234441
|
-
this.maxConcurrentTurns = Math.max(1, options.maxConcurrentTurns ??
|
|
234422
|
+
this.maxConcurrentTurns = Math.max(1, options.maxConcurrentTurns ?? 50);
|
|
234423
|
+
this.maxConcurrentTurnsPerUser = Math.max(1, options.maxConcurrentTurnsPerUser ?? 4);
|
|
234442
234424
|
this.reconciliationDelayMs = Math.min(100, this.reconciliationIntervalMs);
|
|
234443
234425
|
this.toolDependencies = options.toolDependencies;
|
|
234444
234426
|
this.unsubscribeEvents = options.eventBus?.subscribe((event) => {
|
|
@@ -234478,6 +234460,7 @@ var ProjectChatManager = class {
|
|
|
234478
234460
|
reconciliationOperationTimeoutMs;
|
|
234479
234461
|
recoveryPageSize;
|
|
234480
234462
|
maxConcurrentTurns;
|
|
234463
|
+
maxConcurrentTurnsPerUser;
|
|
234481
234464
|
toolDependencies;
|
|
234482
234465
|
unsubscribeEvents;
|
|
234483
234466
|
startupReconciliation;
|
|
@@ -234488,6 +234471,7 @@ var ProjectChatManager = class {
|
|
|
234488
234471
|
pendingPumps = [];
|
|
234489
234472
|
pendingPumpIds = /* @__PURE__ */ new Set();
|
|
234490
234473
|
activeTurnCount = 0;
|
|
234474
|
+
activeTurnsByUser = /* @__PURE__ */ new Map();
|
|
234491
234475
|
reconciliationTimer = null;
|
|
234492
234476
|
reconciliationDelayMs = 100;
|
|
234493
234477
|
shuttingDown = false;
|
|
@@ -235361,6 +235345,7 @@ var ProjectChatManager = class {
|
|
|
235361
235345
|
pendingApprovals: /* @__PURE__ */ new Map(),
|
|
235362
235346
|
writeTail: Promise.resolve(),
|
|
235363
235347
|
evictionTimer: null,
|
|
235348
|
+
queuedAt: null,
|
|
235364
235349
|
contextRefreshGeneration: 0,
|
|
235365
235350
|
contextRefreshFlight: null,
|
|
235366
235351
|
contextRefreshBroadcast: false
|
|
@@ -235482,22 +235467,51 @@ var ProjectChatManager = class {
|
|
|
235482
235467
|
pump(live) {
|
|
235483
235468
|
if (live.activeWork || this.shuttingDown || this.closingThreads.has(live.thread.id)) return;
|
|
235484
235469
|
if (live.queue.length === 0) return;
|
|
235485
|
-
if (this.
|
|
235486
|
-
|
|
235487
|
-
this.pendingPumpIds.add(live.thread.id);
|
|
235488
|
-
this.pendingPumps.push(live);
|
|
235489
|
-
}
|
|
235470
|
+
if (!this.canStartTurn(live)) {
|
|
235471
|
+
this.enqueuePump(live);
|
|
235490
235472
|
return;
|
|
235491
235473
|
}
|
|
235492
235474
|
this.startPump(live);
|
|
235493
235475
|
}
|
|
235476
|
+
/**
|
|
235477
|
+
* Park a thread that has work but no slot. Queueing is invisible to the
|
|
235478
|
+
* caller and only shows up as latency, so every entry into this state is
|
|
235479
|
+
* logged once with the cap that caused it.
|
|
235480
|
+
*/
|
|
235481
|
+
enqueuePump(live) {
|
|
235482
|
+
if (!this.pendingPumpIds.has(live.thread.id)) {
|
|
235483
|
+
this.pendingPumpIds.add(live.thread.id);
|
|
235484
|
+
this.pendingPumps.push(live);
|
|
235485
|
+
}
|
|
235486
|
+
if (live.queuedAt === null) {
|
|
235487
|
+
live.queuedAt = Date.now();
|
|
235488
|
+
const reason = this.activeTurnCount >= this.maxConcurrentTurns ? "global" : "per-user";
|
|
235489
|
+
console.warn(`[ProjectChat] turn queued (thread=${live.thread.id} user=${live.thread.user_id} reason=${reason} active=${this.activeTurnCount}/${this.maxConcurrentTurns} userActive=${this.activeTurnsByUser.get(live.thread.user_id) ?? 0}/${this.maxConcurrentTurnsPerUser} waiting=${this.pendingPumps.length})`);
|
|
235490
|
+
}
|
|
235491
|
+
if (live.status !== "queued") {
|
|
235492
|
+
live.status = "queued";
|
|
235493
|
+
this.broadcastStatus(live);
|
|
235494
|
+
}
|
|
235495
|
+
}
|
|
235496
|
+
/** A turn may start only when both the global and the per-user cap allow it. */
|
|
235497
|
+
canStartTurn(live) {
|
|
235498
|
+
if (this.activeTurnCount >= this.maxConcurrentTurns) return false;
|
|
235499
|
+
const active = this.activeTurnsByUser.get(live.thread.user_id) ?? 0;
|
|
235500
|
+
return active < this.maxConcurrentTurnsPerUser;
|
|
235501
|
+
}
|
|
235494
235502
|
startPump(live) {
|
|
235495
235503
|
if (live.activeWork || this.shuttingDown || this.closingThreads.has(live.thread.id)) return;
|
|
235496
235504
|
const queued = live.queue.shift();
|
|
235497
235505
|
if (!queued) return;
|
|
235498
235506
|
this.pendingPumpIds.delete(live.thread.id);
|
|
235499
|
-
const
|
|
235507
|
+
const userId = live.thread.user_id;
|
|
235508
|
+
if (live.queuedAt !== null) {
|
|
235509
|
+
console.warn(`[ProjectChat] queued turn started after ${Date.now() - live.queuedAt}ms (thread=${live.thread.id} user=${userId})`);
|
|
235510
|
+
live.queuedAt = null;
|
|
235511
|
+
}
|
|
235512
|
+
const slot = { released: false, userId };
|
|
235500
235513
|
this.activeTurnCount++;
|
|
235514
|
+
this.activeTurnsByUser.set(userId, (this.activeTurnsByUser.get(userId) ?? 0) + 1);
|
|
235501
235515
|
live.activeSlot = slot;
|
|
235502
235516
|
this.cancelEviction(live);
|
|
235503
235517
|
let work;
|
|
@@ -235510,10 +235524,7 @@ var ProjectChatManager = class {
|
|
|
235510
235524
|
live.abortController = null;
|
|
235511
235525
|
if (live.queue.length > 0 && !this.shuttingDown && !this.closingThreads.has(live.thread.id)) {
|
|
235512
235526
|
if (this.pendingPumps.length > 0) {
|
|
235513
|
-
|
|
235514
|
-
this.pendingPumpIds.add(live.thread.id);
|
|
235515
|
-
this.pendingPumps.push(live);
|
|
235516
|
-
}
|
|
235527
|
+
this.enqueuePump(live);
|
|
235517
235528
|
} else {
|
|
235518
235529
|
this.pump(live);
|
|
235519
235530
|
}
|
|
@@ -235531,14 +235542,23 @@ var ProjectChatManager = class {
|
|
|
235531
235542
|
if (slot.released) return;
|
|
235532
235543
|
slot.released = true;
|
|
235533
235544
|
this.activeTurnCount = Math.max(0, this.activeTurnCount - 1);
|
|
235545
|
+
const remaining = (this.activeTurnsByUser.get(slot.userId) ?? 0) - 1;
|
|
235546
|
+
if (remaining > 0) this.activeTurnsByUser.set(slot.userId, remaining);
|
|
235547
|
+
else this.activeTurnsByUser.delete(slot.userId);
|
|
235534
235548
|
this.drainPendingPumps();
|
|
235535
235549
|
}
|
|
235536
235550
|
drainPendingPumps() {
|
|
235551
|
+
for (let index = this.pendingPumps.length - 1; index >= 0; index -= 1) {
|
|
235552
|
+
const live = this.pendingPumps[index];
|
|
235553
|
+
if (!live.activeWork && live.queue.length > 0 && !this.closingThreads.has(live.thread.id) && this.liveThreads.get(live.thread.id) === live) continue;
|
|
235554
|
+
this.pendingPumps.splice(index, 1);
|
|
235555
|
+
this.pendingPumpIds.delete(live.thread.id);
|
|
235556
|
+
}
|
|
235537
235557
|
while (!this.shuttingDown && this.activeTurnCount < this.maxConcurrentTurns) {
|
|
235538
|
-
const
|
|
235539
|
-
if (
|
|
235558
|
+
const index = this.pendingPumps.findIndex((live2) => this.canStartTurn(live2));
|
|
235559
|
+
if (index < 0) return;
|
|
235560
|
+
const [live] = this.pendingPumps.splice(index, 1);
|
|
235540
235561
|
this.pendingPumpIds.delete(live.thread.id);
|
|
235541
|
-
if (live.activeWork || live.queue.length === 0 || this.closingThreads.has(live.thread.id) || this.liveThreads.get(live.thread.id) !== live) continue;
|
|
235542
235562
|
this.startPump(live);
|
|
235543
235563
|
}
|
|
235544
235564
|
}
|
|
@@ -238823,23 +238843,34 @@ var routes3 = async (fastify2) => {
|
|
|
238823
238843
|
const host = request.headers["x-forwarded-host"] || request.headers.host || "localhost";
|
|
238824
238844
|
return `npx vibedeckx@latest connect --connect-to ${proto}://${host} --token ${token}`;
|
|
238825
238845
|
};
|
|
238826
|
-
|
|
238827
|
-
|
|
238828
|
-
|
|
238829
|
-
|
|
238830
|
-
|
|
238831
|
-
|
|
238832
|
-
|
|
238833
|
-
|
|
238834
|
-
|
|
238835
|
-
|
|
238836
|
-
|
|
238837
|
-
|
|
238838
|
-
|
|
238839
|
-
|
|
238840
|
-
|
|
238841
|
-
|
|
238842
|
-
|
|
238846
|
+
const sendConnectToken = async (request, reply, op, failure) => {
|
|
238847
|
+
const userId = requireUserFacingUserId(request, reply);
|
|
238848
|
+
if (userId === null) return;
|
|
238849
|
+
const { id } = request.params;
|
|
238850
|
+
const server = await fastify2.storage.remoteServers.getById(id, userId);
|
|
238851
|
+
if (!server) return reply.code(404).send({ error: "Server not found" });
|
|
238852
|
+
const token = await op(id, userId);
|
|
238853
|
+
if (!token) return reply.code(500).send({ error: failure });
|
|
238854
|
+
return reply.send({ token, connectCommand: connectCommandFor(request, token) });
|
|
238855
|
+
};
|
|
238856
|
+
fastify2.post(
|
|
238857
|
+
"/api/remote-servers/:id/connect-token",
|
|
238858
|
+
(request, reply) => sendConnectToken(
|
|
238859
|
+
request,
|
|
238860
|
+
reply,
|
|
238861
|
+
(id, userId) => fastify2.storage.remoteServers.generateToken(id, userId),
|
|
238862
|
+
"Failed to read token"
|
|
238863
|
+
)
|
|
238864
|
+
);
|
|
238865
|
+
fastify2.post(
|
|
238866
|
+
"/api/remote-servers/:id/connect-token/rotate",
|
|
238867
|
+
(request, reply) => sendConnectToken(
|
|
238868
|
+
request,
|
|
238869
|
+
reply,
|
|
238870
|
+
(id, userId) => fastify2.storage.remoteServers.rotateToken(id, userId),
|
|
238871
|
+
"Failed to rotate token"
|
|
238872
|
+
)
|
|
238873
|
+
);
|
|
238843
238874
|
fastify2.post(
|
|
238844
238875
|
"/api/remote-servers/:id/browse",
|
|
238845
238876
|
async (request, reply) => {
|
|
@@ -238891,8 +238922,8 @@ var routes3 = async (fastify2) => {
|
|
|
238891
238922
|
}
|
|
238892
238923
|
}
|
|
238893
238924
|
);
|
|
238894
|
-
fastify2.
|
|
238895
|
-
"/api/remote-servers/:id/
|
|
238925
|
+
fastify2.delete(
|
|
238926
|
+
"/api/remote-servers/:id/connect-token",
|
|
238896
238927
|
async (request, reply) => {
|
|
238897
238928
|
const userId = requireUserFacingUserId(request, reply);
|
|
238898
238929
|
if (userId === null) return;
|
|
@@ -242059,6 +242090,14 @@ var routes11 = async (fastify2) => {
|
|
|
242059
242090
|
console.error(`[API] remote activity write-through failed for ${req.params.sessionId}:`, error48);
|
|
242060
242091
|
return false;
|
|
242061
242092
|
});
|
|
242093
|
+
ensureRemoteAgentStream(req.params.sessionId, {
|
|
242094
|
+
remoteSessionMap: fastify2.remoteSessionMap,
|
|
242095
|
+
remotePatchCache: fastify2.remotePatchCache,
|
|
242096
|
+
reverseConnectManager: fastify2.reverseConnectManager,
|
|
242097
|
+
eventBus: fastify2.eventBus,
|
|
242098
|
+
agentSessionManager: fastify2.agentSessionManager,
|
|
242099
|
+
storage: fastify2.storage
|
|
242100
|
+
});
|
|
242062
242101
|
if (activityReady === true) {
|
|
242063
242102
|
fastify2.agentSessionManager.emitBranchActivityIfChanged(
|
|
242064
242103
|
projectId,
|
|
@@ -243464,7 +243503,7 @@ var project_chat_routes_default = (0, import_fastify_plugin16.default)(routes15,
|
|
|
243464
243503
|
var import_fastify_plugin17 = __toESM(require_plugin2(), 1);
|
|
243465
243504
|
|
|
243466
243505
|
// src/project-activity.ts
|
|
243467
|
-
var RECENT_THREAD_LIMIT =
|
|
243506
|
+
var RECENT_THREAD_LIMIT = 5;
|
|
243468
243507
|
var RECENT_SESSION_LIMIT = 8;
|
|
243469
243508
|
var RECENT_RUN_LIMIT = 5;
|
|
243470
243509
|
var PRIORITY_TASK_LIMIT = 5;
|
|
@@ -243518,8 +243557,6 @@ async function getProjectActivity(storage, projectId, userId) {
|
|
|
243518
243557
|
attentionRuns,
|
|
243519
243558
|
runningSessions,
|
|
243520
243559
|
runningRuns,
|
|
243521
|
-
failedSessions,
|
|
243522
|
-
failedRuns,
|
|
243523
243560
|
remoteCounts,
|
|
243524
243561
|
nextScheduleAt
|
|
243525
243562
|
] = await Promise.all([
|
|
@@ -243533,8 +243570,6 @@ async function getProjectActivity(storage, projectId, userId) {
|
|
|
243533
243570
|
storage.scheduledTaskRuns.getAttentionByProject(projectId, ATTENTION_LIMIT),
|
|
243534
243571
|
storage.agentSessions.countRunningByProject(projectId),
|
|
243535
243572
|
storage.scheduledTaskRuns.countByProjectStatuses(projectId, ["starting", "running"]),
|
|
243536
|
-
storage.agentSessions.countAttentionByProject(projectId),
|
|
243537
|
-
storage.scheduledTaskRuns.countByProjectStatuses(projectId, ["failed", "timeout"]),
|
|
243538
243573
|
storage.searchCache.countRemoteSessionActivityByProject(projectId),
|
|
243539
243574
|
storage.scheduledTasks.getEarliestNextRunAt(projectId)
|
|
243540
243575
|
]);
|
|
@@ -243577,7 +243612,6 @@ async function getProjectActivity(storage, projectId, userId) {
|
|
|
243577
243612
|
attention,
|
|
243578
243613
|
summary: {
|
|
243579
243614
|
running: runningSessions + runningRuns + remoteCounts.running,
|
|
243580
|
-
failed: failedSessions + failedRuns + remoteCounts.failed,
|
|
243581
243615
|
nextScheduleAt
|
|
243582
243616
|
}
|
|
243583
243617
|
};
|
|
@@ -245347,6 +245381,9 @@ async function authenticateWs(authEnabled, query, socket) {
|
|
|
245347
245381
|
if (!userId) return reject("Invalid authentication token");
|
|
245348
245382
|
return { userId, kind: "user" };
|
|
245349
245383
|
}
|
|
245384
|
+
function processOwnerScope(principal) {
|
|
245385
|
+
return principal.kind === "user" ? principal.userId : null;
|
|
245386
|
+
}
|
|
245350
245387
|
async function localProcessProjectId(fastify2, processId) {
|
|
245351
245388
|
const live = fastify2.processManager.getProcessProjectId(processId);
|
|
245352
245389
|
if (live) return live;
|
|
@@ -245462,7 +245499,7 @@ var routes23 = async (fastify2) => {
|
|
|
245462
245499
|
console.log(`[WebSocket] Auth rejected for process ${processId}`);
|
|
245463
245500
|
return;
|
|
245464
245501
|
}
|
|
245465
|
-
const ownerUserId = principal
|
|
245502
|
+
const ownerUserId = processOwnerScope(principal);
|
|
245466
245503
|
if (ownerUserId !== null && !await userOwnsProcess(fastify2, processId, ownerUserId)) {
|
|
245467
245504
|
console.log(`[WebSocket] Ownership denied for process ${processId} (user=${ownerUserId})`);
|
|
245468
245505
|
try {
|
|
@@ -245531,7 +245568,7 @@ var routes23 = async (fastify2) => {
|
|
|
245531
245568
|
const handleInputMap = /* @__PURE__ */ new Map();
|
|
245532
245569
|
const subscribeProcess = async (processId) => {
|
|
245533
245570
|
if (subs.has(processId)) return;
|
|
245534
|
-
const ownerUserId = principal
|
|
245571
|
+
const ownerUserId = processOwnerScope(principal);
|
|
245535
245572
|
if (ownerUserId !== null && !await userOwnsProcess(fastify2, processId, ownerUserId)) {
|
|
245536
245573
|
console.log(`[ExecutorMux] Ownership denied for process ${processId} (user=${ownerUserId})`);
|
|
245537
245574
|
try {
|
|
@@ -245836,8 +245873,46 @@ var websocket_routes_default = (0, import_fastify_plugin24.default)(routes23, {
|
|
|
245836
245873
|
// src/routes/reverse-connect-routes.ts
|
|
245837
245874
|
var import_fastify_plugin25 = __toESM(require_plugin2(), 1);
|
|
245838
245875
|
import { randomBytes as randomBytes2, createHash as createHash7, verify as cryptoVerify } from "crypto";
|
|
245876
|
+
|
|
245877
|
+
// src/utils/rate-limited-warn.ts
|
|
245878
|
+
function createRateLimitedWarn(windowMs, maxKeys) {
|
|
245879
|
+
const seen = /* @__PURE__ */ new Map();
|
|
245880
|
+
return function warnRateLimited(key, message) {
|
|
245881
|
+
const now3 = Date.now();
|
|
245882
|
+
const entry = seen.get(key);
|
|
245883
|
+
if (entry && now3 - entry.last < windowMs) {
|
|
245884
|
+
entry.suppressed++;
|
|
245885
|
+
return;
|
|
245886
|
+
}
|
|
245887
|
+
if (!entry && seen.size >= maxKeys) {
|
|
245888
|
+
let oldestKey = null;
|
|
245889
|
+
let oldestAt = Infinity;
|
|
245890
|
+
for (const [k2, v2] of seen) {
|
|
245891
|
+
if (now3 - v2.last > windowMs) {
|
|
245892
|
+
seen.delete(k2);
|
|
245893
|
+
continue;
|
|
245894
|
+
}
|
|
245895
|
+
if (v2.last < oldestAt) {
|
|
245896
|
+
oldestAt = v2.last;
|
|
245897
|
+
oldestKey = k2;
|
|
245898
|
+
}
|
|
245899
|
+
}
|
|
245900
|
+
if (seen.size >= maxKeys && oldestKey !== null) seen.delete(oldestKey);
|
|
245901
|
+
}
|
|
245902
|
+
const suppressed = entry?.suppressed ?? 0;
|
|
245903
|
+
seen.set(key, { last: now3, suppressed: 0 });
|
|
245904
|
+
console.warn(
|
|
245905
|
+
message + (suppressed > 0 ? ` (+${suppressed} more in the last ${Math.round(windowMs / 1e3)}s)` : "")
|
|
245906
|
+
);
|
|
245907
|
+
};
|
|
245908
|
+
}
|
|
245909
|
+
|
|
245910
|
+
// src/routes/reverse-connect-routes.ts
|
|
245839
245911
|
var MACHINE_HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
245912
|
+
var REJECT_LOG_WINDOW_MS = 3e4;
|
|
245913
|
+
var REJECT_LOG_MAX_KEYS = 500;
|
|
245840
245914
|
var routes24 = async (fastify2) => {
|
|
245915
|
+
const logRejectedUpgrade = createRateLimitedWarn(REJECT_LOG_WINDOW_MS, REJECT_LOG_MAX_KEYS);
|
|
245841
245916
|
fastify2.get("/api/reverse-connect/identity", async (req, reply) => {
|
|
245842
245917
|
const token = req.headers["x-vibedeckx-connect-token"];
|
|
245843
245918
|
if (typeof token !== "string" || token.length === 0) {
|
|
@@ -245856,12 +245931,17 @@ var routes24 = async (fastify2) => {
|
|
|
245856
245931
|
async (socket, req) => {
|
|
245857
245932
|
const token = req.query.token;
|
|
245858
245933
|
if (!token) {
|
|
245934
|
+
logRejectedUpgrade(`no-token:${req.ip}`, `[ReverseConnect] Rejected upgrade from ${req.ip}: no connect token`);
|
|
245859
245935
|
socket.send(JSON.stringify({ error: "Token required" }));
|
|
245860
245936
|
socket.close(4001, "Token required");
|
|
245861
245937
|
return;
|
|
245862
245938
|
}
|
|
245863
245939
|
const server = await fastify2.storage.remoteServers.getByToken(token);
|
|
245864
245940
|
if (!server) {
|
|
245941
|
+
logRejectedUpgrade(
|
|
245942
|
+
`bad-token:${req.ip}`,
|
|
245943
|
+
`[ReverseConnect] Rejected upgrade from ${req.ip}: connect token not recognized \u2014 the worker needs a current token`
|
|
245944
|
+
);
|
|
245865
245945
|
socket.send(JSON.stringify({ error: "Invalid token" }));
|
|
245866
245946
|
socket.close(4001, "Invalid token");
|
|
245867
245947
|
return;
|
|
@@ -245899,6 +245979,7 @@ var routes24 = async (fastify2) => {
|
|
|
245899
245979
|
const publicKey = frame.publicKey;
|
|
245900
245980
|
const signature = frame.signature;
|
|
245901
245981
|
if (!publicKey || !signature) {
|
|
245982
|
+
logRejectedUpgrade(`malformed-auth:${serverId}`, `[ReverseConnect] Malformed machine auth from ${serverId}`);
|
|
245902
245983
|
socket.close(4003, "Malformed machine auth");
|
|
245903
245984
|
return;
|
|
245904
245985
|
}
|
|
@@ -245909,6 +245990,7 @@ var routes24 = async (fastify2) => {
|
|
|
245909
245990
|
valid = false;
|
|
245910
245991
|
}
|
|
245911
245992
|
if (!valid) {
|
|
245993
|
+
logRejectedUpgrade(`bad-signature:${serverId}`, `[ReverseConnect] Bad machine signature from ${serverId}`);
|
|
245912
245994
|
socket.close(4003, "Bad machine signature");
|
|
245913
245995
|
return;
|
|
245914
245996
|
}
|
|
@@ -246013,6 +246095,23 @@ var event_routes_default = (0, import_fastify_plugin26.default)(routes25, { name
|
|
|
246013
246095
|
// src/routes/terminal-routes.ts
|
|
246014
246096
|
var import_fastify_plugin27 = __toESM(require_plugin2(), 1);
|
|
246015
246097
|
import path13 from "path";
|
|
246098
|
+
|
|
246099
|
+
// src/utils/path-project.ts
|
|
246100
|
+
async function ensurePathProjectId(fastify2, projectPath) {
|
|
246101
|
+
const pseudoProjectId = `path:${projectPath}`;
|
|
246102
|
+
if (await fastify2.storage.projects.getById(pseudoProjectId)) return pseudoProjectId;
|
|
246103
|
+
const existingByPath = await fastify2.storage.projects.getByPath(projectPath);
|
|
246104
|
+
if (existingByPath) return existingByPath.id;
|
|
246105
|
+
const name25 = projectPath.split("/").filter(Boolean).pop() || projectPath;
|
|
246106
|
+
try {
|
|
246107
|
+
await fastify2.storage.projects.create({ id: pseudoProjectId, name: name25, path: projectPath });
|
|
246108
|
+
} catch (err) {
|
|
246109
|
+
if (!(err instanceof Error && err.message.includes("UNIQUE constraint failed"))) throw err;
|
|
246110
|
+
}
|
|
246111
|
+
return pseudoProjectId;
|
|
246112
|
+
}
|
|
246113
|
+
|
|
246114
|
+
// src/routes/terminal-routes.ts
|
|
246016
246115
|
async function getRemoteConfig5(fastify2, project, remoteServerId) {
|
|
246017
246116
|
const remotes = await fastify2.storage.projectRemotes.getByProject(project.id);
|
|
246018
246117
|
const target = remoteServerId ? remotes.find((r) => r.remote_server_id === remoteServerId) : remotes[0];
|
|
@@ -246030,7 +246129,8 @@ var routes26 = async (fastify2) => {
|
|
|
246030
246129
|
}
|
|
246031
246130
|
const resolvedPath = resolveWorktreePath(projectPath, branch ?? null);
|
|
246032
246131
|
try {
|
|
246033
|
-
const
|
|
246132
|
+
const projectId = await ensurePathProjectId(fastify2, projectPath);
|
|
246133
|
+
const terminal = fastify2.processManager.startTerminal(projectId, resolvedPath, branch ?? null);
|
|
246034
246134
|
return reply.code(201).send({ terminal: { id: terminal.id, name: terminal.name, cwd: resolvedPath } });
|
|
246035
246135
|
} catch (error48) {
|
|
246036
246136
|
console.error(`[terminal-routes] Failed to start terminal in ${resolvedPath}:`, error48);
|
|
@@ -249435,6 +249535,8 @@ var MACHINE_KEY_SETTING = "reverse_machine_private_key";
|
|
|
249435
249535
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
249436
249536
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
249437
249537
|
var NO_PING_TIMEOUT_MS = 6e4;
|
|
249538
|
+
var AUTH_REJECT_CODES = /* @__PURE__ */ new Set([4001, 4003]);
|
|
249539
|
+
var HEALTHY_CONNECTION_MS = 5e3;
|
|
249438
249540
|
function isTextualContentType(contentType) {
|
|
249439
249541
|
const t = contentType.toLowerCase();
|
|
249440
249542
|
if (t === "") return true;
|
|
@@ -249448,6 +249550,7 @@ var ReverseConnectClient = class {
|
|
|
249448
249550
|
localPort;
|
|
249449
249551
|
localChannels = /* @__PURE__ */ new Map();
|
|
249450
249552
|
reconnectAttempt = 0;
|
|
249553
|
+
openedAt = null;
|
|
249451
249554
|
reconnectTimer = null;
|
|
249452
249555
|
noPingTimer = null;
|
|
249453
249556
|
shuttingDown = false;
|
|
@@ -249468,8 +249571,8 @@ var ReverseConnectClient = class {
|
|
|
249468
249571
|
maxPayload: 11 * 1024 * 1024
|
|
249469
249572
|
});
|
|
249470
249573
|
this.ws.on("open", () => {
|
|
249471
|
-
console.log("[ReverseClient]
|
|
249472
|
-
this.
|
|
249574
|
+
console.log("[ReverseClient] Socket open, awaiting server handshake");
|
|
249575
|
+
this.openedAt = Date.now();
|
|
249473
249576
|
this.resetNoPingTimer();
|
|
249474
249577
|
const frame = { type: "status", ready: true };
|
|
249475
249578
|
this.ws.send(JSON.stringify(frame));
|
|
@@ -249484,7 +249587,19 @@ var ReverseConnectClient = class {
|
|
|
249484
249587
|
});
|
|
249485
249588
|
this.ws.on("close", (code, reason) => {
|
|
249486
249589
|
const safeReason = redactSecretForms(reason?.toString() || "", this.token);
|
|
249487
|
-
|
|
249590
|
+
const rejected = AUTH_REJECT_CODES.has(code);
|
|
249591
|
+
const uptime = this.openedAt === null ? 0 : Date.now() - this.openedAt;
|
|
249592
|
+
if (rejected) {
|
|
249593
|
+
console.error(
|
|
249594
|
+
`[ReverseClient] Server rejected this connection (code=${code}, reason=${safeReason}). ` + (code === 4001 ? "The connect token is no longer valid \u2014 open Settings \u2192 Remote Servers, read the current token, and re-run `vibedeckx connect` with it." : "This machine's identity was refused \u2014 the remote record may belong to another machine or another account.") + " Retrying with backoff, but it will not recover on its own."
|
|
249595
|
+
);
|
|
249596
|
+
} else {
|
|
249597
|
+
console.log(`[ReverseClient] Disconnected (code=${code}, reason=${safeReason})`);
|
|
249598
|
+
}
|
|
249599
|
+
if (!rejected && this.openedAt !== null && uptime >= HEALTHY_CONNECTION_MS) {
|
|
249600
|
+
this.reconnectAttempt = 0;
|
|
249601
|
+
}
|
|
249602
|
+
this.openedAt = null;
|
|
249488
249603
|
this.clearNoPingTimer();
|
|
249489
249604
|
this.closeAllLocalChannels();
|
|
249490
249605
|
this.ws = null;
|