remote-codex 0.11.17 → 0.11.19
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/apps/supervisor-api/dist/{chunk-ZYCD54EZ.js → chunk-IZBNFCMP.js} +460 -560
- package/apps/supervisor-api/dist/index.js +1 -1
- package/apps/supervisor-api/dist/worker-index.js +1 -1
- package/apps/supervisor-web/dist/assets/index-CUnlRID-.js +6 -0
- package/apps/supervisor-web/dist/assets/thread-ui-TBhog-RK.js +3614 -0
- package/apps/supervisor-web/dist/index.html +2 -2
- package/package.json +1 -1
- package/packages/db/migrations/0028_thread_turn_display_prompt.sql +1 -0
- package/packages/db/src/repositories.ts +6 -0
- package/packages/db/src/schema.ts +1 -0
- package/packages/shared/src/index.ts +3 -0
- package/apps/supervisor-web/dist/assets/index-DKyefuy8.js +0 -6
- package/apps/supervisor-web/dist/assets/thread-ui-q6mjcjXn.js +0 -3614
|
@@ -9,8 +9,8 @@ import Fastify from "fastify";
|
|
|
9
9
|
import multipart from "@fastify/multipart";
|
|
10
10
|
import websocket from "@fastify/websocket";
|
|
11
11
|
import { spawn as spawn6 } from "child_process";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import fs28 from "fs";
|
|
13
|
+
import path28 from "path";
|
|
14
14
|
import { ZodError } from "zod";
|
|
15
15
|
|
|
16
16
|
// ../../packages/config/src/index.ts
|
|
@@ -2318,7 +2318,7 @@ Subquery.prototype.getSQL = function() {
|
|
|
2318
2318
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
2319
2319
|
const nullifyMap = {};
|
|
2320
2320
|
const result = columns.reduce(
|
|
2321
|
-
(result2, { path:
|
|
2321
|
+
(result2, { path: path29, field }, columnIndex) => {
|
|
2322
2322
|
let decoder;
|
|
2323
2323
|
if (is(field, Column)) {
|
|
2324
2324
|
decoder = field;
|
|
@@ -2328,8 +2328,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
2328
2328
|
decoder = field.sql.decoder;
|
|
2329
2329
|
}
|
|
2330
2330
|
let node = result2;
|
|
2331
|
-
for (const [pathChunkIndex, pathChunk] of
|
|
2332
|
-
if (pathChunkIndex <
|
|
2331
|
+
for (const [pathChunkIndex, pathChunk] of path29.entries()) {
|
|
2332
|
+
if (pathChunkIndex < path29.length - 1) {
|
|
2333
2333
|
if (!(pathChunk in node)) {
|
|
2334
2334
|
node[pathChunk] = {};
|
|
2335
2335
|
}
|
|
@@ -2337,8 +2337,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
2337
2337
|
} else {
|
|
2338
2338
|
const rawValue = row[columnIndex];
|
|
2339
2339
|
const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
|
|
2340
|
-
if (joinsNotNullableMap && is(field, Column) &&
|
|
2341
|
-
const objectName =
|
|
2340
|
+
if (joinsNotNullableMap && is(field, Column) && path29.length === 2) {
|
|
2341
|
+
const objectName = path29[0];
|
|
2342
2342
|
if (!(objectName in nullifyMap)) {
|
|
2343
2343
|
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
2344
2344
|
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
@@ -6496,6 +6496,7 @@ var threadTurnMetadata = sqliteTable(
|
|
|
6496
6496
|
pricingModelKey: text("pricing_model_key"),
|
|
6497
6497
|
pricingTierKey: text("pricing_tier_key"),
|
|
6498
6498
|
tokenUsageJson: text("token_usage_json"),
|
|
6499
|
+
displayPrompt: text("display_prompt"),
|
|
6499
6500
|
createdAt: text("created_at").notNull(),
|
|
6500
6501
|
updatedAt: text("updated_at").notNull()
|
|
6501
6502
|
},
|
|
@@ -7119,6 +7120,7 @@ function upsertThreadTurnMetadata(db, input) {
|
|
|
7119
7120
|
pricingModelKey: input.pricingModelKey !== void 0 ? input.pricingModelKey : existing.pricingModelKey,
|
|
7120
7121
|
pricingTierKey: input.pricingTierKey !== void 0 ? input.pricingTierKey : existing.pricingTierKey,
|
|
7121
7122
|
tokenUsageJson: input.tokenUsageJson !== void 0 ? input.tokenUsageJson : existing.tokenUsageJson,
|
|
7123
|
+
displayPrompt: input.displayPrompt !== void 0 ? input.displayPrompt : existing.displayPrompt,
|
|
7122
7124
|
updatedAt: now
|
|
7123
7125
|
}).where(eq(threadTurnMetadata.id, existing.id)).run();
|
|
7124
7126
|
return;
|
|
@@ -7133,6 +7135,7 @@ function upsertThreadTurnMetadata(db, input) {
|
|
|
7133
7135
|
pricingModelKey: input.pricingModelKey ?? null,
|
|
7134
7136
|
pricingTierKey: input.pricingTierKey ?? null,
|
|
7135
7137
|
tokenUsageJson: input.tokenUsageJson ?? null,
|
|
7138
|
+
displayPrompt: input.displayPrompt ?? null,
|
|
7136
7139
|
createdAt: now,
|
|
7137
7140
|
updatedAt: now
|
|
7138
7141
|
}).run();
|
|
@@ -9248,7 +9251,7 @@ function extractFileChangeEntries(item) {
|
|
|
9248
9251
|
isRecord4(entry.summary) ? entry.summary : null,
|
|
9249
9252
|
isRecord4(entry.diff) ? entry.diff : null
|
|
9250
9253
|
].filter((candidate) => Boolean(candidate));
|
|
9251
|
-
const
|
|
9254
|
+
const path29 = uniqueStrings([
|
|
9252
9255
|
stringOrNull(valueFromRecords(nestedRecords, ["path", "filePath", "targetPath"])),
|
|
9253
9256
|
stringOrNull(
|
|
9254
9257
|
valueFromRecords(nestedRecords, [
|
|
@@ -9295,7 +9298,7 @@ function extractFileChangeEntries(item) {
|
|
|
9295
9298
|
const diffStats = explicitAdditions === 0 && explicitDeletions === 0 && diffText ? countUnifiedDiffStats(diffText) : null;
|
|
9296
9299
|
const additions = explicitAdditions || diffStats?.additions || 0;
|
|
9297
9300
|
const deletions = explicitDeletions || diffStats?.deletions || 0;
|
|
9298
|
-
const normalizedPath =
|
|
9301
|
+
const normalizedPath = path29 ?? (diffText ? projectRelativePathLabel(extractPathFromDiffText(diffText)) : null);
|
|
9299
9302
|
if (!normalizedPath && additions === 0 && deletions === 0) {
|
|
9300
9303
|
return null;
|
|
9301
9304
|
}
|
|
@@ -13605,14 +13608,14 @@ function displayPath(pathValue, options) {
|
|
|
13605
13608
|
}
|
|
13606
13609
|
return relativePath;
|
|
13607
13610
|
}
|
|
13608
|
-
function toolIsLowInformationPatch(normalized, state, input, patchText,
|
|
13611
|
+
function toolIsLowInformationPatch(normalized, state, input, patchText, path29, metadataStats) {
|
|
13609
13612
|
if (normalized !== "applypatch" && normalized !== "patch") {
|
|
13610
13613
|
return false;
|
|
13611
13614
|
}
|
|
13612
13615
|
if (toolStateStatus(state) !== "running") {
|
|
13613
13616
|
return false;
|
|
13614
13617
|
}
|
|
13615
|
-
if (
|
|
13618
|
+
if (path29 || patchText || metadataStats || stringValue2(state.output)) {
|
|
13616
13619
|
return false;
|
|
13617
13620
|
}
|
|
13618
13621
|
return !isRecord9(input) || Object.keys(input).length === 0;
|
|
@@ -13655,7 +13658,7 @@ function fileChangeStatsFromMetadata(metadata) {
|
|
|
13655
13658
|
if (files.length === 0) {
|
|
13656
13659
|
return null;
|
|
13657
13660
|
}
|
|
13658
|
-
const paths = files.map((file) => stringValue2(file.filePath) ?? stringValue2(file.path) ?? stringValue2(file.relativePath)).filter((
|
|
13661
|
+
const paths = files.map((file) => stringValue2(file.filePath) ?? stringValue2(file.path) ?? stringValue2(file.relativePath)).filter((path29) => Boolean(path29));
|
|
13659
13662
|
const addedLines = files.reduce((total, file) => total + (numberValue(file.additions) ?? numberValue(file.addedLines) ?? numberValue(file.added) ?? 0), 0);
|
|
13660
13663
|
const removedLines = files.reduce((total, file) => total + (numberValue(file.deletions) ?? numberValue(file.removedLines) ?? numberValue(file.removed) ?? 0), 0);
|
|
13661
13664
|
return {
|
|
@@ -13803,28 +13806,28 @@ function mapAssistantTool(messageId2, tool, options) {
|
|
|
13803
13806
|
].includes(normalized)) {
|
|
13804
13807
|
const metadataStats = fileChangeStatsFromMetadata(state.metadata);
|
|
13805
13808
|
const patchText = isRecord9(input) ? stringValue2(input.patchText) ?? stringValue2(input.patch) ?? stringValue2(input.diff) : null;
|
|
13806
|
-
const
|
|
13807
|
-
if (toolIsLowInformationPatch(normalized, state, input, patchText,
|
|
13809
|
+
const path29 = metadataStats?.path ?? filePathFromInput(input) ?? extractPathFromPatchText(patchText);
|
|
13810
|
+
if (toolIsLowInformationPatch(normalized, state, input, patchText, path29, metadataStats)) {
|
|
13808
13811
|
return null;
|
|
13809
13812
|
}
|
|
13810
13813
|
const output = stringValue2(state.output);
|
|
13811
13814
|
const diffStats = countUnifiedDiffStats2(patchText);
|
|
13812
|
-
const displayFilePath = displayPath(
|
|
13815
|
+
const displayFilePath = displayPath(path29, options);
|
|
13813
13816
|
return {
|
|
13814
13817
|
id,
|
|
13815
13818
|
kind: "fileChange",
|
|
13816
13819
|
text: metadataStats ? metadataStats.changedFiles > 1 ? `${metadataStats.changedFiles} changed files` : displayFilePath ?? metadataStats.previewText : displayFilePath ?? output ?? summary ?? name,
|
|
13817
13820
|
previewText: metadataStats ? metadataStats.changedFiles > 1 ? `${metadataStats.changedFiles} changed files` : displayFilePath ?? metadataStats.previewText : displayFilePath ? `${name}: ${displayFilePath}` : output ?? summary ?? name,
|
|
13818
13821
|
detailText,
|
|
13819
|
-
changedFiles: metadataStats?.changedFiles ?? (
|
|
13822
|
+
changedFiles: metadataStats?.changedFiles ?? (path29 ? 1 : null),
|
|
13820
13823
|
addedLines: metadataStats?.addedLines ?? diffStats?.addedLines ?? null,
|
|
13821
13824
|
removedLines: metadataStats?.removedLines ?? diffStats?.removedLines ?? null,
|
|
13822
13825
|
status: toolStateStatus(state)
|
|
13823
13826
|
};
|
|
13824
13827
|
}
|
|
13825
13828
|
if (["read", "grep", "glob", "list", "ls", "bashoutput"].includes(normalized)) {
|
|
13826
|
-
const
|
|
13827
|
-
const text2 = displayPath(
|
|
13829
|
+
const path29 = filePathFromInput(input);
|
|
13830
|
+
const text2 = displayPath(path29, options) ?? summary ?? name;
|
|
13828
13831
|
return {
|
|
13829
13832
|
id,
|
|
13830
13833
|
kind: "fileRead",
|
|
@@ -18313,8 +18316,13 @@ function applyLiveAgentMessageOrderingHints(turns, localThreadId, liveState) {
|
|
|
18313
18316
|
}
|
|
18314
18317
|
function buildTurnDto(turn, metadata) {
|
|
18315
18318
|
const tokenUsage = parseThreadTurnTokenUsageJson(metadata?.tokenUsageJson);
|
|
18319
|
+
const displayPrompt = metadata?.displayPrompt?.trim();
|
|
18320
|
+
const items = displayPrompt && turn.items.some((item) => /\[localImage\]/.test(item.text)) ? turn.items.map(
|
|
18321
|
+
(item) => item.kind === "userMessage" && /\[localImage\]/.test(item.text) ? { ...item, text: displayPrompt } : item
|
|
18322
|
+
) : turn.items;
|
|
18316
18323
|
return {
|
|
18317
18324
|
...turn,
|
|
18325
|
+
items,
|
|
18318
18326
|
startedAt: turn.startedAt ?? metadata?.createdAt ?? null,
|
|
18319
18327
|
model: metadata?.model ?? null,
|
|
18320
18328
|
reasoningEffort: normalizeReasoningEffort(metadata?.reasoningEffort),
|
|
@@ -18824,7 +18832,8 @@ var ThreadPromptTurnCoordinator = class {
|
|
|
18824
18832
|
input.effectiveModel
|
|
18825
18833
|
),
|
|
18826
18834
|
pricingModelKey: pricingSnapshot?.pricingModelKey ?? null,
|
|
18827
|
-
pricingTierKey: pricingSnapshot?.pricingTierKey ?? null
|
|
18835
|
+
pricingTierKey: pricingSnapshot?.pricingTierKey ?? null,
|
|
18836
|
+
displayPrompt
|
|
18828
18837
|
});
|
|
18829
18838
|
const patch = {
|
|
18830
18839
|
providerTurnId: turn.providerTurnId,
|
|
@@ -19443,7 +19452,8 @@ var ThreadHistoryPersistenceCoordinator = class {
|
|
|
19443
19452
|
reasoningEffortAvailable: runtimeMetadata.reasoningEffortAvailable ?? null,
|
|
19444
19453
|
pricingModelKey: runtimeMetadata.pricingModelKey ?? null,
|
|
19445
19454
|
pricingTierKey: runtimeMetadata.pricingTierKey ?? null,
|
|
19446
|
-
tokenUsageJson: runtimeMetadata.tokenUsageJson ?? null
|
|
19455
|
+
tokenUsageJson: runtimeMetadata.tokenUsageJson ?? null,
|
|
19456
|
+
displayPrompt: runtimeMetadata.displayPrompt ?? null
|
|
19447
19457
|
});
|
|
19448
19458
|
deleteThreadTurnMetadataByThreadAndTurnId(this.db, localThreadId, runtimeTurnId);
|
|
19449
19459
|
}
|
|
@@ -20650,6 +20660,24 @@ function truncateChromeStderr(value) {
|
|
|
20650
20660
|
return value.slice(value.length - MAX_CHROME_STDERR_CHARS);
|
|
20651
20661
|
}
|
|
20652
20662
|
|
|
20663
|
+
// src/thread-turn-metadata.ts
|
|
20664
|
+
function listThreadTurnMetadataMap(db, localThreadId) {
|
|
20665
|
+
return new Map(
|
|
20666
|
+
listThreadTurnMetadataByThreadId(db, localThreadId).map((entry) => [
|
|
20667
|
+
entry.turnId,
|
|
20668
|
+
{
|
|
20669
|
+
model: entry.model ?? null,
|
|
20670
|
+
reasoningEffort: entry.reasoningEffort ?? null,
|
|
20671
|
+
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
20672
|
+
pricingModelKey: entry.pricingModelKey ?? null,
|
|
20673
|
+
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
20674
|
+
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
20675
|
+
createdAt: entry.createdAt ?? null
|
|
20676
|
+
}
|
|
20677
|
+
])
|
|
20678
|
+
);
|
|
20679
|
+
}
|
|
20680
|
+
|
|
20653
20681
|
// src/thread-export-coordinator.ts
|
|
20654
20682
|
function userPromptPreviewFromTurn(turn) {
|
|
20655
20683
|
const prompt = turn.items.find((item) => item.kind === "userMessage")?.text.trim();
|
|
@@ -20743,20 +20771,7 @@ var ThreadExportCoordinator = class {
|
|
|
20743
20771
|
});
|
|
20744
20772
|
}
|
|
20745
20773
|
this.callbacks.requireProviderSessionId(record);
|
|
20746
|
-
const turnMetadataById =
|
|
20747
|
-
listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
|
|
20748
|
-
entry.turnId,
|
|
20749
|
-
{
|
|
20750
|
-
model: entry.model ?? null,
|
|
20751
|
-
reasoningEffort: entry.reasoningEffort ?? null,
|
|
20752
|
-
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
20753
|
-
pricingModelKey: entry.pricingModelKey ?? null,
|
|
20754
|
-
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
20755
|
-
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
20756
|
-
createdAt: entry.createdAt ?? null
|
|
20757
|
-
}
|
|
20758
|
-
])
|
|
20759
|
-
);
|
|
20774
|
+
const turnMetadataById = listThreadTurnMetadataMap(this.db, localThreadId);
|
|
20760
20775
|
const cachedDetail = await this.detailAssembler.buildCacheEntry({
|
|
20761
20776
|
localThreadId,
|
|
20762
20777
|
record,
|
|
@@ -20823,20 +20838,7 @@ var ThreadForkCoordinator = class {
|
|
|
20823
20838
|
});
|
|
20824
20839
|
}
|
|
20825
20840
|
this.callbacks.requireProviderSessionId(record);
|
|
20826
|
-
const turnMetadataById =
|
|
20827
|
-
listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
|
|
20828
|
-
entry.turnId,
|
|
20829
|
-
{
|
|
20830
|
-
model: entry.model ?? null,
|
|
20831
|
-
reasoningEffort: entry.reasoningEffort ?? null,
|
|
20832
|
-
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
20833
|
-
pricingModelKey: entry.pricingModelKey ?? null,
|
|
20834
|
-
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
20835
|
-
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
20836
|
-
createdAt: entry.createdAt ?? null
|
|
20837
|
-
}
|
|
20838
|
-
])
|
|
20839
|
-
);
|
|
20841
|
+
const turnMetadataById = listThreadTurnMetadataMap(this.db, localThreadId);
|
|
20840
20842
|
const cachedDetail = await this.detailAssembler.buildCacheEntry({
|
|
20841
20843
|
localThreadId,
|
|
20842
20844
|
record,
|
|
@@ -21441,6 +21443,26 @@ var ThreadService = class {
|
|
|
21441
21443
|
}
|
|
21442
21444
|
return record.providerSessionId;
|
|
21443
21445
|
}
|
|
21446
|
+
requireThreadRecord(localThreadId) {
|
|
21447
|
+
const record = getThreadRecordById(this.db, localThreadId);
|
|
21448
|
+
if (!record) {
|
|
21449
|
+
throw new HttpError(404, {
|
|
21450
|
+
code: "not_found",
|
|
21451
|
+
message: "Thread was not found."
|
|
21452
|
+
});
|
|
21453
|
+
}
|
|
21454
|
+
return record;
|
|
21455
|
+
}
|
|
21456
|
+
requireWorkspaceForThread(record) {
|
|
21457
|
+
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
21458
|
+
if (!workspace) {
|
|
21459
|
+
throw new HttpError(404, {
|
|
21460
|
+
code: "not_found",
|
|
21461
|
+
message: "Workspace was not found for this thread."
|
|
21462
|
+
});
|
|
21463
|
+
}
|
|
21464
|
+
return workspace;
|
|
21465
|
+
}
|
|
21444
21466
|
materializeHiddenRuntimeTurns(localThreadId, turns) {
|
|
21445
21467
|
for (const turn of this.liveState.hiddenRemoteTurns(localThreadId, turns)) {
|
|
21446
21468
|
const displayTurnId = this.liveState.displayTurnIdForRuntimeTurn(localThreadId, turn.providerTurnId) ?? turn.providerTurnId;
|
|
@@ -21575,37 +21597,12 @@ var ThreadService = class {
|
|
|
21575
21597
|
return this.getThreadDetail(localThreadId);
|
|
21576
21598
|
}
|
|
21577
21599
|
async getThreadDetail(localThreadId, options = {}) {
|
|
21578
|
-
const record =
|
|
21579
|
-
|
|
21580
|
-
throw new HttpError(404, {
|
|
21581
|
-
code: "not_found",
|
|
21582
|
-
message: "Thread was not found."
|
|
21583
|
-
});
|
|
21584
|
-
}
|
|
21585
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
21586
|
-
if (!workspace) {
|
|
21587
|
-
throw new HttpError(404, {
|
|
21588
|
-
code: "not_found",
|
|
21589
|
-
message: "Workspace was not found for this thread."
|
|
21590
|
-
});
|
|
21591
|
-
}
|
|
21600
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21601
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
21592
21602
|
this.requireProviderSessionId(record);
|
|
21593
21603
|
const loadedIds = await this.listLoadedProviderSessionIds(record.provider);
|
|
21594
21604
|
const workspacePathStatus = await pathExists3(workspace.absPath) ? "present" : "missing";
|
|
21595
|
-
const turnMetadataById =
|
|
21596
|
-
listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
|
|
21597
|
-
entry.turnId,
|
|
21598
|
-
{
|
|
21599
|
-
model: entry.model ?? null,
|
|
21600
|
-
reasoningEffort: entry.reasoningEffort ?? null,
|
|
21601
|
-
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
21602
|
-
pricingModelKey: entry.pricingModelKey ?? null,
|
|
21603
|
-
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
21604
|
-
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
21605
|
-
createdAt: entry.createdAt ?? null
|
|
21606
|
-
}
|
|
21607
|
-
])
|
|
21608
|
-
);
|
|
21605
|
+
const turnMetadataById = listThreadTurnMetadataMap(this.db, localThreadId);
|
|
21609
21606
|
const cachedDetail = await this.detailAssembler.buildCacheEntry({
|
|
21610
21607
|
localThreadId,
|
|
21611
21608
|
record,
|
|
@@ -21663,33 +21660,15 @@ var ThreadService = class {
|
|
|
21663
21660
|
return this.exportCoordinator.exportThreadTranscript(localThreadId, input);
|
|
21664
21661
|
}
|
|
21665
21662
|
async getThreadGoal(localThreadId) {
|
|
21666
|
-
const record =
|
|
21667
|
-
if (!record) {
|
|
21668
|
-
throw new HttpError(404, {
|
|
21669
|
-
code: "not_found",
|
|
21670
|
-
message: "Thread was not found."
|
|
21671
|
-
});
|
|
21672
|
-
}
|
|
21663
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21673
21664
|
return this.goalCoordinator.getThreadGoal(record);
|
|
21674
21665
|
}
|
|
21675
21666
|
async updateThreadGoal(localThreadId, input) {
|
|
21676
|
-
const record =
|
|
21677
|
-
if (!record) {
|
|
21678
|
-
throw new HttpError(404, {
|
|
21679
|
-
code: "not_found",
|
|
21680
|
-
message: "Thread was not found."
|
|
21681
|
-
});
|
|
21682
|
-
}
|
|
21667
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21683
21668
|
return this.goalCoordinator.updateThreadGoal(record, input);
|
|
21684
21669
|
}
|
|
21685
21670
|
async clearThreadGoal(localThreadId) {
|
|
21686
|
-
const record =
|
|
21687
|
-
if (!record) {
|
|
21688
|
-
throw new HttpError(404, {
|
|
21689
|
-
code: "not_found",
|
|
21690
|
-
message: "Thread was not found."
|
|
21691
|
-
});
|
|
21692
|
-
}
|
|
21671
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21693
21672
|
return this.goalCoordinator.clearThreadGoal(record);
|
|
21694
21673
|
}
|
|
21695
21674
|
async ensureThreadLoadedForProviderOperation(record) {
|
|
@@ -21711,28 +21690,9 @@ var ThreadService = class {
|
|
|
21711
21690
|
await this.resumeThread(record.id, resumeInput);
|
|
21712
21691
|
}
|
|
21713
21692
|
async getThreadHistoryItemDetail(localThreadId, itemId) {
|
|
21714
|
-
const record =
|
|
21715
|
-
if (!record) {
|
|
21716
|
-
throw new HttpError(404, {
|
|
21717
|
-
code: "not_found",
|
|
21718
|
-
message: "Thread was not found."
|
|
21719
|
-
});
|
|
21720
|
-
}
|
|
21693
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21721
21694
|
this.requireProviderSessionId(record);
|
|
21722
|
-
const turnMetadataById =
|
|
21723
|
-
listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
|
|
21724
|
-
entry.turnId,
|
|
21725
|
-
{
|
|
21726
|
-
model: entry.model ?? null,
|
|
21727
|
-
reasoningEffort: entry.reasoningEffort ?? null,
|
|
21728
|
-
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
21729
|
-
pricingModelKey: entry.pricingModelKey ?? null,
|
|
21730
|
-
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
21731
|
-
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
21732
|
-
createdAt: entry.createdAt ?? null
|
|
21733
|
-
}
|
|
21734
|
-
])
|
|
21735
|
-
);
|
|
21695
|
+
const turnMetadataById = listThreadTurnMetadataMap(this.db, localThreadId);
|
|
21736
21696
|
const cachedDetail = await this.detailAssembler.buildCacheEntry({
|
|
21737
21697
|
localThreadId,
|
|
21738
21698
|
record,
|
|
@@ -21763,13 +21723,7 @@ var ThreadService = class {
|
|
|
21763
21723
|
return this.getThreadDetail(localThreadId);
|
|
21764
21724
|
}
|
|
21765
21725
|
async sendPrompt(localThreadId, input, options = {}) {
|
|
21766
|
-
let record =
|
|
21767
|
-
if (!record) {
|
|
21768
|
-
throw new HttpError(404, {
|
|
21769
|
-
code: "not_found",
|
|
21770
|
-
message: "Thread was not found."
|
|
21771
|
-
});
|
|
21772
|
-
}
|
|
21726
|
+
let record = this.requireThreadRecord(localThreadId);
|
|
21773
21727
|
await this.importCoordinator.assertImportedThreadReadyForPrompt({
|
|
21774
21728
|
source: record.source,
|
|
21775
21729
|
provider: record.provider,
|
|
@@ -21869,13 +21823,7 @@ var ThreadService = class {
|
|
|
21869
21823
|
});
|
|
21870
21824
|
}
|
|
21871
21825
|
async updateThreadSettings(localThreadId, input) {
|
|
21872
|
-
const record =
|
|
21873
|
-
if (!record) {
|
|
21874
|
-
throw new HttpError(404, {
|
|
21875
|
-
code: "not_found",
|
|
21876
|
-
message: "Thread was not found."
|
|
21877
|
-
});
|
|
21878
|
-
}
|
|
21826
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21879
21827
|
const nextSettings = await this.sessionCoordinator.resolveThreadSettings({
|
|
21880
21828
|
provider: record.provider,
|
|
21881
21829
|
currentModel: record.model,
|
|
@@ -21916,13 +21864,7 @@ var ThreadService = class {
|
|
|
21916
21864
|
return this.toThreadDto(updated, loadedIds);
|
|
21917
21865
|
}
|
|
21918
21866
|
async updateThreadTitle(localThreadId, title) {
|
|
21919
|
-
const record =
|
|
21920
|
-
if (!record) {
|
|
21921
|
-
throw new HttpError(404, {
|
|
21922
|
-
code: "not_found",
|
|
21923
|
-
message: "Thread was not found."
|
|
21924
|
-
});
|
|
21925
|
-
}
|
|
21867
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21926
21868
|
const normalizedTitle = title.trim();
|
|
21927
21869
|
if (!normalizedTitle) {
|
|
21928
21870
|
throw new HttpError(400, {
|
|
@@ -21941,13 +21883,7 @@ var ThreadService = class {
|
|
|
21941
21883
|
return this.toThreadDto(updated, loadedIds);
|
|
21942
21884
|
}
|
|
21943
21885
|
async compactThread(localThreadId) {
|
|
21944
|
-
const record =
|
|
21945
|
-
if (!record) {
|
|
21946
|
-
throw new HttpError(404, {
|
|
21947
|
-
code: "not_found",
|
|
21948
|
-
message: "Thread was not found."
|
|
21949
|
-
});
|
|
21950
|
-
}
|
|
21886
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21951
21887
|
const providerSessionId = this.requireProviderSessionId(record);
|
|
21952
21888
|
if (record.isConnected === false) {
|
|
21953
21889
|
throw new HttpError(409, {
|
|
@@ -21982,72 +21918,30 @@ var ThreadService = class {
|
|
|
21982
21918
|
return this.forkCoordinator.forkThread(localThreadId, input);
|
|
21983
21919
|
}
|
|
21984
21920
|
async listThreadSkills(localThreadId) {
|
|
21985
|
-
const record =
|
|
21986
|
-
|
|
21987
|
-
throw new HttpError(404, {
|
|
21988
|
-
code: "not_found",
|
|
21989
|
-
message: "Thread was not found."
|
|
21990
|
-
});
|
|
21991
|
-
}
|
|
21992
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
21993
|
-
if (!workspace) {
|
|
21994
|
-
throw new HttpError(404, {
|
|
21995
|
-
code: "not_found",
|
|
21996
|
-
message: "Workspace was not found for this thread."
|
|
21997
|
-
});
|
|
21998
|
-
}
|
|
21921
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21922
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
21999
21923
|
return this.managementCoordinator.listThreadSkills({
|
|
22000
21924
|
provider: record.provider,
|
|
22001
21925
|
workspacePath: workspace.absPath
|
|
22002
21926
|
});
|
|
22003
21927
|
}
|
|
22004
21928
|
async listThreadMcpServers(localThreadId) {
|
|
22005
|
-
const record =
|
|
22006
|
-
if (!record) {
|
|
22007
|
-
throw new HttpError(404, {
|
|
22008
|
-
code: "not_found",
|
|
22009
|
-
message: "Thread was not found."
|
|
22010
|
-
});
|
|
22011
|
-
}
|
|
21929
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
22012
21930
|
return this.managementCoordinator.listThreadMcpServers({
|
|
22013
21931
|
provider: record.provider
|
|
22014
21932
|
});
|
|
22015
21933
|
}
|
|
22016
21934
|
async listThreadHooks(localThreadId) {
|
|
22017
|
-
const record =
|
|
22018
|
-
|
|
22019
|
-
throw new HttpError(404, {
|
|
22020
|
-
code: "not_found",
|
|
22021
|
-
message: "Thread was not found."
|
|
22022
|
-
});
|
|
22023
|
-
}
|
|
22024
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
22025
|
-
if (!workspace) {
|
|
22026
|
-
throw new HttpError(404, {
|
|
22027
|
-
code: "not_found",
|
|
22028
|
-
message: "Workspace was not found for this thread."
|
|
22029
|
-
});
|
|
22030
|
-
}
|
|
21935
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21936
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
22031
21937
|
return this.managementCoordinator.listThreadHooks({
|
|
22032
21938
|
provider: record.provider,
|
|
22033
21939
|
workspacePath: workspace.absPath
|
|
22034
21940
|
});
|
|
22035
21941
|
}
|
|
22036
21942
|
async createThreadHook(localThreadId, input) {
|
|
22037
|
-
const record =
|
|
22038
|
-
|
|
22039
|
-
throw new HttpError(404, {
|
|
22040
|
-
code: "not_found",
|
|
22041
|
-
message: "Thread was not found."
|
|
22042
|
-
});
|
|
22043
|
-
}
|
|
22044
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
22045
|
-
if (!workspace) {
|
|
22046
|
-
throw new HttpError(404, {
|
|
22047
|
-
code: "not_found",
|
|
22048
|
-
message: "Workspace was not found for this thread."
|
|
22049
|
-
});
|
|
22050
|
-
}
|
|
21943
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21944
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
22051
21945
|
return this.managementCoordinator.createThreadHook({
|
|
22052
21946
|
provider: record.provider,
|
|
22053
21947
|
workspacePath: workspace.absPath,
|
|
@@ -22055,20 +21949,8 @@ var ThreadService = class {
|
|
|
22055
21949
|
});
|
|
22056
21950
|
}
|
|
22057
21951
|
async updateThreadHook(localThreadId, input) {
|
|
22058
|
-
const record =
|
|
22059
|
-
|
|
22060
|
-
throw new HttpError(404, {
|
|
22061
|
-
code: "not_found",
|
|
22062
|
-
message: "Thread was not found."
|
|
22063
|
-
});
|
|
22064
|
-
}
|
|
22065
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
22066
|
-
if (!workspace) {
|
|
22067
|
-
throw new HttpError(404, {
|
|
22068
|
-
code: "not_found",
|
|
22069
|
-
message: "Workspace was not found for this thread."
|
|
22070
|
-
});
|
|
22071
|
-
}
|
|
21952
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21953
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
22072
21954
|
return this.managementCoordinator.updateThreadHook({
|
|
22073
21955
|
provider: record.provider,
|
|
22074
21956
|
workspacePath: workspace.absPath,
|
|
@@ -22076,20 +21958,8 @@ var ThreadService = class {
|
|
|
22076
21958
|
});
|
|
22077
21959
|
}
|
|
22078
21960
|
async trustThreadHook(localThreadId, input) {
|
|
22079
|
-
const record =
|
|
22080
|
-
|
|
22081
|
-
throw new HttpError(404, {
|
|
22082
|
-
code: "not_found",
|
|
22083
|
-
message: "Thread was not found."
|
|
22084
|
-
});
|
|
22085
|
-
}
|
|
22086
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
22087
|
-
if (!workspace) {
|
|
22088
|
-
throw new HttpError(404, {
|
|
22089
|
-
code: "not_found",
|
|
22090
|
-
message: "Workspace was not found for this thread."
|
|
22091
|
-
});
|
|
22092
|
-
}
|
|
21961
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21962
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
22093
21963
|
return this.managementCoordinator.trustThreadHook({
|
|
22094
21964
|
provider: record.provider,
|
|
22095
21965
|
workspacePath: workspace.absPath,
|
|
@@ -22097,20 +21967,8 @@ var ThreadService = class {
|
|
|
22097
21967
|
});
|
|
22098
21968
|
}
|
|
22099
21969
|
async untrustThreadHook(localThreadId, input) {
|
|
22100
|
-
const record =
|
|
22101
|
-
|
|
22102
|
-
throw new HttpError(404, {
|
|
22103
|
-
code: "not_found",
|
|
22104
|
-
message: "Thread was not found."
|
|
22105
|
-
});
|
|
22106
|
-
}
|
|
22107
|
-
const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
|
|
22108
|
-
if (!workspace) {
|
|
22109
|
-
throw new HttpError(404, {
|
|
22110
|
-
code: "not_found",
|
|
22111
|
-
message: "Workspace was not found for this thread."
|
|
22112
|
-
});
|
|
22113
|
-
}
|
|
21970
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
21971
|
+
const workspace = this.requireWorkspaceForThread(record);
|
|
22114
21972
|
return this.managementCoordinator.untrustThreadHook({
|
|
22115
21973
|
provider: record.provider,
|
|
22116
21974
|
workspacePath: workspace.absPath,
|
|
@@ -22118,13 +21976,7 @@ var ThreadService = class {
|
|
|
22118
21976
|
});
|
|
22119
21977
|
}
|
|
22120
21978
|
async interruptThread(localThreadId, requestedTurnId) {
|
|
22121
|
-
const record =
|
|
22122
|
-
if (!record) {
|
|
22123
|
-
throw new HttpError(404, {
|
|
22124
|
-
code: "not_found",
|
|
22125
|
-
message: "Thread was not found."
|
|
22126
|
-
});
|
|
22127
|
-
}
|
|
21979
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
22128
21980
|
const providerSessionId = this.requireProviderSessionId(record);
|
|
22129
21981
|
const interruptInput = {
|
|
22130
21982
|
provider: record.provider,
|
|
@@ -22152,13 +22004,7 @@ var ThreadService = class {
|
|
|
22152
22004
|
return this.deletionCoordinator.deleteThread(localThreadId);
|
|
22153
22005
|
}
|
|
22154
22006
|
async respondToRequest(localThreadId, requestId, input) {
|
|
22155
|
-
const record =
|
|
22156
|
-
if (!record) {
|
|
22157
|
-
throw new HttpError(404, {
|
|
22158
|
-
code: "not_found",
|
|
22159
|
-
message: "Thread was not found."
|
|
22160
|
-
});
|
|
22161
|
-
}
|
|
22007
|
+
const record = this.requireThreadRecord(localThreadId);
|
|
22162
22008
|
const requestResponse = this.requestCoordinator.respondToRequest(
|
|
22163
22009
|
localThreadId,
|
|
22164
22010
|
requestId,
|
|
@@ -22313,20 +22159,7 @@ var ThreadService = class {
|
|
|
22313
22159
|
if (!record) {
|
|
22314
22160
|
return;
|
|
22315
22161
|
}
|
|
22316
|
-
const turnMetadataById =
|
|
22317
|
-
listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
|
|
22318
|
-
entry.turnId,
|
|
22319
|
-
{
|
|
22320
|
-
model: entry.model ?? null,
|
|
22321
|
-
reasoningEffort: entry.reasoningEffort ?? null,
|
|
22322
|
-
reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
|
|
22323
|
-
pricingModelKey: entry.pricingModelKey ?? null,
|
|
22324
|
-
pricingTierKey: normalizePricingTier(entry.pricingTierKey),
|
|
22325
|
-
tokenUsageJson: entry.tokenUsageJson ?? null,
|
|
22326
|
-
createdAt: entry.createdAt ?? null
|
|
22327
|
-
}
|
|
22328
|
-
])
|
|
22329
|
-
);
|
|
22162
|
+
const turnMetadataById = listThreadTurnMetadataMap(this.db, localThreadId);
|
|
22330
22163
|
const cachedDetail = await this.detailAssembler.buildCacheEntry({
|
|
22331
22164
|
localThreadId,
|
|
22332
22165
|
record,
|
|
@@ -24081,68 +23914,143 @@ async function registerThreadRoutes(app) {
|
|
|
24081
23914
|
}
|
|
24082
23915
|
|
|
24083
23916
|
// src/routes/workspaces.ts
|
|
24084
|
-
import
|
|
23917
|
+
import fs20 from "fs/promises";
|
|
24085
23918
|
import { createReadStream } from "fs";
|
|
24086
|
-
import
|
|
24087
|
-
import path19 from "path";
|
|
24088
|
-
import { spawn as spawn4 } from "child_process";
|
|
23919
|
+
import path21 from "path";
|
|
24089
23920
|
import { Readable } from "stream";
|
|
24090
23921
|
import { z as z6 } from "zod";
|
|
24091
|
-
|
|
24092
|
-
|
|
24093
|
-
|
|
24094
|
-
|
|
24095
|
-
|
|
24096
|
-
|
|
24097
|
-
|
|
24098
|
-
|
|
24099
|
-
|
|
24100
|
-
|
|
24101
|
-
|
|
24102
|
-
|
|
24103
|
-
}
|
|
24104
|
-
|
|
24105
|
-
|
|
24106
|
-
}
|
|
24107
|
-
|
|
24108
|
-
|
|
24109
|
-
|
|
24110
|
-
|
|
24111
|
-
|
|
24112
|
-
|
|
24113
|
-
|
|
24114
|
-
|
|
24115
|
-
|
|
24116
|
-
|
|
24117
|
-
|
|
24118
|
-
|
|
24119
|
-
|
|
24120
|
-
});
|
|
24121
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
24124
|
-
|
|
24125
|
-
|
|
24126
|
-
|
|
24127
|
-
|
|
24128
|
-
|
|
24129
|
-
})
|
|
24130
|
-
|
|
24131
|
-
|
|
24132
|
-
|
|
24133
|
-
|
|
24134
|
-
|
|
24135
|
-
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
24139
|
-
|
|
24140
|
-
|
|
24141
|
-
|
|
23922
|
+
|
|
23923
|
+
// src/workspace-artifact-service.ts
|
|
23924
|
+
import fs18 from "fs/promises";
|
|
23925
|
+
import path19 from "path";
|
|
23926
|
+
function artifactRoot(record) {
|
|
23927
|
+
return path19.join(record.absPath, ".remote-codex", "artifacts");
|
|
23928
|
+
}
|
|
23929
|
+
function artifactFilePath(record, artifactId) {
|
|
23930
|
+
return path19.join(artifactRoot(record), artifactId, "artifact.bin");
|
|
23931
|
+
}
|
|
23932
|
+
function artifactMetadataPath(record, artifactId) {
|
|
23933
|
+
return path19.join(artifactRoot(record), artifactId, "metadata.json");
|
|
23934
|
+
}
|
|
23935
|
+
function safeArtifactFileName(value) {
|
|
23936
|
+
return path19.basename(value).replace(/[^a-zA-Z0-9_. -]/g, "_") || "artifact.bin";
|
|
23937
|
+
}
|
|
23938
|
+
function artifactIdFromName(name) {
|
|
23939
|
+
const base = path19.basename(name).replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
|
|
23940
|
+
return `${base || "artifact"}-${Date.now().toString(36)}`;
|
|
23941
|
+
}
|
|
23942
|
+
async function readWorkspaceArtifactMetadata(record, artifactId) {
|
|
23943
|
+
try {
|
|
23944
|
+
const raw = await fs18.readFile(artifactMetadataPath(record, artifactId), "utf8");
|
|
23945
|
+
return JSON.parse(raw);
|
|
23946
|
+
} catch (error) {
|
|
23947
|
+
if (error.code === "ENOENT") {
|
|
23948
|
+
throw new HttpError(404, {
|
|
23949
|
+
code: "not_found",
|
|
23950
|
+
message: "Workspace artifact was not found."
|
|
23951
|
+
});
|
|
23952
|
+
}
|
|
23953
|
+
throw error;
|
|
23954
|
+
}
|
|
23955
|
+
}
|
|
23956
|
+
async function listWorkspaceArtifacts(record) {
|
|
23957
|
+
let entries;
|
|
23958
|
+
try {
|
|
23959
|
+
entries = await fs18.readdir(artifactRoot(record));
|
|
23960
|
+
} catch (error) {
|
|
23961
|
+
if (error.code === "ENOENT") {
|
|
23962
|
+
return [];
|
|
23963
|
+
}
|
|
23964
|
+
throw error;
|
|
23965
|
+
}
|
|
23966
|
+
const artifacts = [];
|
|
23967
|
+
for (const entry of entries) {
|
|
23968
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(entry)) {
|
|
23969
|
+
continue;
|
|
23970
|
+
}
|
|
23971
|
+
try {
|
|
23972
|
+
artifacts.push(await readWorkspaceArtifactMetadata(record, entry));
|
|
23973
|
+
} catch (error) {
|
|
23974
|
+
if (!(error instanceof HttpError && error.statusCode === 404)) {
|
|
23975
|
+
throw error;
|
|
23976
|
+
}
|
|
23977
|
+
}
|
|
23978
|
+
}
|
|
23979
|
+
return artifacts.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
23980
|
+
}
|
|
23981
|
+
async function createWorkspaceArtifact({
|
|
23982
|
+
record,
|
|
23983
|
+
artifactId,
|
|
23984
|
+
name,
|
|
23985
|
+
mediaType,
|
|
23986
|
+
content,
|
|
23987
|
+
metadata
|
|
23988
|
+
}) {
|
|
23989
|
+
if (content.length === 0) {
|
|
23990
|
+
throw new HttpError(400, {
|
|
23991
|
+
code: "bad_request",
|
|
23992
|
+
message: "Artifact content must not be empty."
|
|
23993
|
+
});
|
|
23994
|
+
}
|
|
23995
|
+
const dir = path19.dirname(artifactFilePath(record, artifactId));
|
|
23996
|
+
await fs18.mkdir(dir, { recursive: true, mode: 448 });
|
|
23997
|
+
const filePath = artifactFilePath(record, artifactId);
|
|
23998
|
+
await fs18.writeFile(filePath, content, { flag: "wx" }).catch((error) => {
|
|
23999
|
+
if (error.code === "EEXIST") {
|
|
24000
|
+
throw new HttpError(409, {
|
|
24001
|
+
code: "conflict",
|
|
24002
|
+
message: "Workspace artifact already exists."
|
|
24003
|
+
});
|
|
24004
|
+
}
|
|
24005
|
+
throw error;
|
|
24006
|
+
});
|
|
24007
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24008
|
+
const artifact = {
|
|
24009
|
+
id: artifactId,
|
|
24010
|
+
workspaceId: record.id,
|
|
24011
|
+
name: safeArtifactFileName(name),
|
|
24012
|
+
mediaType,
|
|
24013
|
+
size: content.length,
|
|
24014
|
+
createdAt: now,
|
|
24015
|
+
updatedAt: now,
|
|
24016
|
+
metadata: metadata ?? {}
|
|
24017
|
+
};
|
|
24018
|
+
await fs18.writeFile(artifactMetadataPath(record, artifactId), JSON.stringify(artifact, null, 2));
|
|
24019
|
+
return artifact;
|
|
24020
|
+
}
|
|
24021
|
+
async function readWorkspaceArtifactContent(record, artifactId) {
|
|
24022
|
+
try {
|
|
24023
|
+
return await fs18.readFile(artifactFilePath(record, artifactId));
|
|
24024
|
+
} catch (error) {
|
|
24025
|
+
if (error.code === "ENOENT") {
|
|
24026
|
+
throw new HttpError(404, {
|
|
24027
|
+
code: "not_found",
|
|
24028
|
+
message: "Workspace artifact content was not found."
|
|
24029
|
+
});
|
|
24030
|
+
}
|
|
24031
|
+
throw error;
|
|
24032
|
+
}
|
|
24033
|
+
}
|
|
24034
|
+
async function deleteWorkspaceArtifact(record, artifactId) {
|
|
24035
|
+
const artifact = await readWorkspaceArtifactMetadata(record, artifactId);
|
|
24036
|
+
await fs18.rm(path19.dirname(artifactFilePath(record, artifactId)), {
|
|
24037
|
+
recursive: true,
|
|
24038
|
+
force: true
|
|
24039
|
+
});
|
|
24040
|
+
return artifact;
|
|
24041
|
+
}
|
|
24042
|
+
|
|
24043
|
+
// src/workspace-file-service.ts
|
|
24044
|
+
import { spawn as spawn4 } from "child_process";
|
|
24045
|
+
import fs19 from "fs/promises";
|
|
24046
|
+
import os4 from "os";
|
|
24047
|
+
import path20 from "path";
|
|
24142
24048
|
var PREVIEW_DEFAULT_LIMIT_BYTES = 5e4;
|
|
24143
24049
|
var WORKSPACE_UPLOAD_MAX_BYTES = 50 * 1024 * 1024;
|
|
24144
24050
|
var WORKSPACE_FOLDER_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
|
|
24145
24051
|
var WORKSPACE_FOLDER_DOWNLOAD_MAX_FILES = 300;
|
|
24052
|
+
var WORKSPACE_TREE_DIRECTORY_ENTRY_LIMIT = 400;
|
|
24053
|
+
var WORKSPACE_TREE_DIRECTORY_SCAN_LIMIT = 2e3;
|
|
24146
24054
|
var WORKSPACE_TREE_IGNORED_NAMES = /* @__PURE__ */ new Set([
|
|
24147
24055
|
".git",
|
|
24148
24056
|
"node_modules",
|
|
@@ -24172,7 +24080,7 @@ function toWorkspaceFileDto(file) {
|
|
|
24172
24080
|
};
|
|
24173
24081
|
}
|
|
24174
24082
|
function languageForPath(filePath) {
|
|
24175
|
-
const extension =
|
|
24083
|
+
const extension = path20.extname(filePath).slice(1).toLowerCase();
|
|
24176
24084
|
switch (extension) {
|
|
24177
24085
|
case "js":
|
|
24178
24086
|
case "jsx":
|
|
@@ -24218,18 +24126,18 @@ function languageForPath(filePath) {
|
|
|
24218
24126
|
}
|
|
24219
24127
|
}
|
|
24220
24128
|
function relativeWorkspacePath(rootPath, absPath) {
|
|
24221
|
-
const relative =
|
|
24222
|
-
return relative === "" ? "" : relative.split(
|
|
24129
|
+
const relative = path20.relative(rootPath, absPath);
|
|
24130
|
+
return relative === "" ? "" : relative.split(path20.sep).join("/");
|
|
24223
24131
|
}
|
|
24224
24132
|
async function resolveWorkspaceItemPath(rootPath, relativePath = "") {
|
|
24225
|
-
const candidate =
|
|
24133
|
+
const candidate = path20.resolve(rootPath, relativePath || ".");
|
|
24226
24134
|
const comparable = await assertPathWithinRoot(rootPath, candidate);
|
|
24227
24135
|
return comparable;
|
|
24228
24136
|
}
|
|
24229
|
-
async function buildWorkspaceTreeNode(rootPath, absPath
|
|
24230
|
-
const stats = await
|
|
24137
|
+
async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
24138
|
+
const stats = await fs19.stat(absPath);
|
|
24231
24139
|
const relativePath = relativeWorkspacePath(rootPath, absPath);
|
|
24232
|
-
const name = relativePath ?
|
|
24140
|
+
const name = relativePath ? path20.basename(absPath) : path20.basename(rootPath);
|
|
24233
24141
|
if (!stats.isDirectory()) {
|
|
24234
24142
|
return {
|
|
24235
24143
|
name,
|
|
@@ -24242,18 +24150,36 @@ async function buildWorkspaceTreeNode(rootPath, absPath, depth = 0) {
|
|
|
24242
24150
|
name,
|
|
24243
24151
|
path: relativePath,
|
|
24244
24152
|
kind: "directory",
|
|
24153
|
+
childrenLoaded: true,
|
|
24245
24154
|
children: []
|
|
24246
24155
|
};
|
|
24247
|
-
|
|
24248
|
-
return node;
|
|
24249
|
-
}
|
|
24250
|
-
let entries;
|
|
24156
|
+
const visible = [];
|
|
24251
24157
|
try {
|
|
24252
|
-
|
|
24158
|
+
const directory = await fs19.opendir(absPath);
|
|
24159
|
+
let scanned = 0;
|
|
24160
|
+
for await (const entry of directory) {
|
|
24161
|
+
scanned += 1;
|
|
24162
|
+
if (scanned > WORKSPACE_TREE_DIRECTORY_SCAN_LIMIT) {
|
|
24163
|
+
node.truncated = true;
|
|
24164
|
+
break;
|
|
24165
|
+
}
|
|
24166
|
+
if (entry.name.startsWith(".") || WORKSPACE_TREE_IGNORED_NAMES.has(entry.name)) {
|
|
24167
|
+
continue;
|
|
24168
|
+
}
|
|
24169
|
+
if (!entry.isDirectory() && !entry.isFile()) {
|
|
24170
|
+
continue;
|
|
24171
|
+
}
|
|
24172
|
+
if (visible.length >= WORKSPACE_TREE_DIRECTORY_ENTRY_LIMIT) {
|
|
24173
|
+
node.truncated = true;
|
|
24174
|
+
break;
|
|
24175
|
+
}
|
|
24176
|
+
visible.push(entry);
|
|
24177
|
+
}
|
|
24253
24178
|
} catch {
|
|
24254
24179
|
return node;
|
|
24255
24180
|
}
|
|
24256
|
-
|
|
24181
|
+
node.hasChildren = visible.length > 0;
|
|
24182
|
+
visible.sort((left, right) => {
|
|
24257
24183
|
if (left.isDirectory() && !right.isDirectory()) {
|
|
24258
24184
|
return -1;
|
|
24259
24185
|
}
|
|
@@ -24261,15 +24187,28 @@ async function buildWorkspaceTreeNode(rootPath, absPath, depth = 0) {
|
|
|
24261
24187
|
return 1;
|
|
24262
24188
|
}
|
|
24263
24189
|
return left.name.localeCompare(right.name);
|
|
24264
|
-
})
|
|
24190
|
+
});
|
|
24265
24191
|
node.children = (await Promise.all(
|
|
24266
24192
|
visible.map(async (entry) => {
|
|
24267
|
-
const childPath =
|
|
24193
|
+
const childPath = path20.join(absPath, entry.name);
|
|
24268
24194
|
try {
|
|
24269
|
-
|
|
24270
|
-
|
|
24195
|
+
const childRelativePath = relativeWorkspacePath(rootPath, childPath);
|
|
24196
|
+
if (entry.isDirectory()) {
|
|
24197
|
+
return {
|
|
24198
|
+
name: entry.name,
|
|
24199
|
+
path: childRelativePath,
|
|
24200
|
+
kind: "directory",
|
|
24201
|
+
hasChildren: true,
|
|
24202
|
+
childrenLoaded: false
|
|
24203
|
+
};
|
|
24271
24204
|
}
|
|
24272
|
-
|
|
24205
|
+
const childStats = await fs19.stat(childPath);
|
|
24206
|
+
return {
|
|
24207
|
+
name: entry.name,
|
|
24208
|
+
path: childRelativePath,
|
|
24209
|
+
kind: "file",
|
|
24210
|
+
size: childStats.size
|
|
24211
|
+
};
|
|
24273
24212
|
} catch {
|
|
24274
24213
|
return null;
|
|
24275
24214
|
}
|
|
@@ -24277,73 +24216,8 @@ async function buildWorkspaceTreeNode(rootPath, absPath, depth = 0) {
|
|
|
24277
24216
|
)).filter((child) => child !== null);
|
|
24278
24217
|
return node;
|
|
24279
24218
|
}
|
|
24280
|
-
function requireWorkspaceRecord(app, workspaceId) {
|
|
24281
|
-
const record = getWorkspaceRecordById(app.services.database.db, workspaceId);
|
|
24282
|
-
if (!record) {
|
|
24283
|
-
throw new HttpError(404, {
|
|
24284
|
-
code: "not_found",
|
|
24285
|
-
message: "Workspace was not found."
|
|
24286
|
-
});
|
|
24287
|
-
}
|
|
24288
|
-
return record;
|
|
24289
|
-
}
|
|
24290
|
-
function artifactRoot(record) {
|
|
24291
|
-
return path19.join(record.absPath, ".remote-codex", "artifacts");
|
|
24292
|
-
}
|
|
24293
|
-
function artifactFilePath(record, artifactId) {
|
|
24294
|
-
return path19.join(artifactRoot(record), artifactId, "artifact.bin");
|
|
24295
|
-
}
|
|
24296
|
-
function artifactMetadataPath(record, artifactId) {
|
|
24297
|
-
return path19.join(artifactRoot(record), artifactId, "metadata.json");
|
|
24298
|
-
}
|
|
24299
|
-
function safeArtifactFileName(value) {
|
|
24300
|
-
return path19.basename(value).replace(/[^a-zA-Z0-9_. -]/g, "_") || "artifact.bin";
|
|
24301
|
-
}
|
|
24302
|
-
function artifactIdFromName(name) {
|
|
24303
|
-
const base = path19.basename(name).replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
|
|
24304
|
-
return `${base || "artifact"}-${Date.now().toString(36)}`;
|
|
24305
|
-
}
|
|
24306
|
-
async function readArtifactMetadata(record, artifactId) {
|
|
24307
|
-
try {
|
|
24308
|
-
const raw = await fs18.readFile(artifactMetadataPath(record, artifactId), "utf8");
|
|
24309
|
-
return JSON.parse(raw);
|
|
24310
|
-
} catch (error) {
|
|
24311
|
-
if (error.code === "ENOENT") {
|
|
24312
|
-
throw new HttpError(404, {
|
|
24313
|
-
code: "not_found",
|
|
24314
|
-
message: "Workspace artifact was not found."
|
|
24315
|
-
});
|
|
24316
|
-
}
|
|
24317
|
-
throw error;
|
|
24318
|
-
}
|
|
24319
|
-
}
|
|
24320
|
-
async function listWorkspaceArtifacts(record) {
|
|
24321
|
-
let entries;
|
|
24322
|
-
try {
|
|
24323
|
-
entries = await fs18.readdir(artifactRoot(record));
|
|
24324
|
-
} catch (error) {
|
|
24325
|
-
if (error.code === "ENOENT") {
|
|
24326
|
-
return [];
|
|
24327
|
-
}
|
|
24328
|
-
throw error;
|
|
24329
|
-
}
|
|
24330
|
-
const artifacts = [];
|
|
24331
|
-
for (const entry of entries) {
|
|
24332
|
-
if (!workspaceArtifactIdSchema.safeParse(entry).success) {
|
|
24333
|
-
continue;
|
|
24334
|
-
}
|
|
24335
|
-
try {
|
|
24336
|
-
artifacts.push(await readArtifactMetadata(record, entry));
|
|
24337
|
-
} catch (error) {
|
|
24338
|
-
if (!(error instanceof HttpError && error.statusCode === 404)) {
|
|
24339
|
-
throw error;
|
|
24340
|
-
}
|
|
24341
|
-
}
|
|
24342
|
-
}
|
|
24343
|
-
return artifacts.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
24344
|
-
}
|
|
24345
24219
|
function contentTypeForPath(filePath) {
|
|
24346
|
-
switch (
|
|
24220
|
+
switch (path20.extname(filePath).slice(1).toLowerCase()) {
|
|
24347
24221
|
case "png":
|
|
24348
24222
|
return "image/png";
|
|
24349
24223
|
case "jpg":
|
|
@@ -24373,15 +24247,15 @@ function contentTypeForPath(filePath) {
|
|
|
24373
24247
|
}
|
|
24374
24248
|
}
|
|
24375
24249
|
async function collectFolderZipEntries(rootPath, folderPath) {
|
|
24376
|
-
const folderName =
|
|
24250
|
+
const folderName = path20.basename(folderPath) || "workspace-folder";
|
|
24377
24251
|
const entries = [];
|
|
24378
24252
|
let totalBytes = 0;
|
|
24379
24253
|
const pending = [folderPath];
|
|
24380
24254
|
while (pending.length > 0) {
|
|
24381
24255
|
const current = pending.pop();
|
|
24382
|
-
const children = await
|
|
24256
|
+
const children = await fs19.readdir(current, { withFileTypes: true });
|
|
24383
24257
|
for (const child of children) {
|
|
24384
|
-
const childPath = await resolveWorkspaceItemPath(rootPath,
|
|
24258
|
+
const childPath = await resolveWorkspaceItemPath(rootPath, path20.relative(rootPath, path20.join(current, child.name)));
|
|
24385
24259
|
if (child.isDirectory()) {
|
|
24386
24260
|
pending.push(childPath);
|
|
24387
24261
|
continue;
|
|
@@ -24389,7 +24263,7 @@ async function collectFolderZipEntries(rootPath, folderPath) {
|
|
|
24389
24263
|
if (!child.isFile()) {
|
|
24390
24264
|
continue;
|
|
24391
24265
|
}
|
|
24392
|
-
const stats = await
|
|
24266
|
+
const stats = await fs19.stat(childPath);
|
|
24393
24267
|
totalBytes += stats.size;
|
|
24394
24268
|
entries.push({
|
|
24395
24269
|
absPath: childPath,
|
|
@@ -24440,8 +24314,8 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
24440
24314
|
const centralParts = [];
|
|
24441
24315
|
let offset = 0;
|
|
24442
24316
|
for (const entry of entries) {
|
|
24443
|
-
const data = await
|
|
24444
|
-
const name = Buffer.from(entry.archivePath.split(
|
|
24317
|
+
const data = await fs19.readFile(entry.absPath);
|
|
24318
|
+
const name = Buffer.from(entry.archivePath.split(path20.sep).join("/"), "utf8");
|
|
24445
24319
|
const checksum = crc32(data);
|
|
24446
24320
|
const { dosDate, dosTime } = zipDosDateTime(entry.updatedAt);
|
|
24447
24321
|
const localHeader = Buffer.alloc(30);
|
|
@@ -24488,19 +24362,19 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
24488
24362
|
endRecord.writeUInt32LE(centralSize, 12);
|
|
24489
24363
|
endRecord.writeUInt32LE(offset, 16);
|
|
24490
24364
|
endRecord.writeUInt16LE(0, 20);
|
|
24491
|
-
const tempDir = await
|
|
24492
|
-
const zipPath =
|
|
24493
|
-
await
|
|
24365
|
+
const tempDir = await fs19.mkdtemp(path20.join(os4.tmpdir(), "remote-codex-folder-download-"));
|
|
24366
|
+
const zipPath = path20.join(tempDir, `${path20.basename(folderPath) || "workspace-folder"}.zip`);
|
|
24367
|
+
await fs19.writeFile(zipPath, Buffer.concat([...localParts, ...centralParts, endRecord]));
|
|
24494
24368
|
return { zipPath, tempDir };
|
|
24495
24369
|
}
|
|
24496
24370
|
function cleanupTemporaryZip(zipPath, tempDir) {
|
|
24497
24371
|
return async () => {
|
|
24498
|
-
await
|
|
24499
|
-
await
|
|
24372
|
+
await fs19.rm(zipPath, { force: true }).catch(() => void 0);
|
|
24373
|
+
await fs19.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
24500
24374
|
};
|
|
24501
24375
|
}
|
|
24502
24376
|
function sanitizeUploadFilename(filename) {
|
|
24503
|
-
const baseName =
|
|
24377
|
+
const baseName = path20.basename(filename?.trim() || "upload");
|
|
24504
24378
|
if (!baseName || baseName === "." || baseName === "..") {
|
|
24505
24379
|
return "upload";
|
|
24506
24380
|
}
|
|
@@ -24512,7 +24386,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
24512
24386
|
const normalized = withoutQuery.replace(/[\\/]+$/, "");
|
|
24513
24387
|
const rawName = normalized.split(/[/:]/).filter(Boolean).at(-1) ?? "";
|
|
24514
24388
|
const repoName = rawName.endsWith(".git") ? rawName.slice(0, -4) : rawName;
|
|
24515
|
-
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(
|
|
24389
|
+
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(path20.sep)) {
|
|
24516
24390
|
throw new HttpError(400, {
|
|
24517
24391
|
code: "bad_request",
|
|
24518
24392
|
message: "Unable to infer a target directory from the Git URL."
|
|
@@ -24522,7 +24396,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
24522
24396
|
}
|
|
24523
24397
|
async function pathExists4(absPath) {
|
|
24524
24398
|
try {
|
|
24525
|
-
await
|
|
24399
|
+
await fs19.stat(absPath);
|
|
24526
24400
|
return true;
|
|
24527
24401
|
} catch (error) {
|
|
24528
24402
|
if (error.code === "ENOENT") {
|
|
@@ -24570,6 +24444,69 @@ function cloneRepository(gitUrl, targetPath) {
|
|
|
24570
24444
|
});
|
|
24571
24445
|
});
|
|
24572
24446
|
}
|
|
24447
|
+
|
|
24448
|
+
// src/routes/workspaces.ts
|
|
24449
|
+
var createWorkspaceSchema = z6.union([
|
|
24450
|
+
z6.object({
|
|
24451
|
+
absPath: z6.string().min(1),
|
|
24452
|
+
label: z6.string().min(1).optional()
|
|
24453
|
+
}),
|
|
24454
|
+
z6.object({
|
|
24455
|
+
gitUrl: z6.string().min(1),
|
|
24456
|
+
label: z6.string().min(1).optional()
|
|
24457
|
+
})
|
|
24458
|
+
]);
|
|
24459
|
+
var updateFavoriteSchema = z6.object({
|
|
24460
|
+
isFavorite: z6.boolean()
|
|
24461
|
+
});
|
|
24462
|
+
var updateWorkspaceSchema = z6.object({
|
|
24463
|
+
label: z6.string().min(1)
|
|
24464
|
+
});
|
|
24465
|
+
var workspaceFilePathSchema = z6.string().trim().min(1).max(4096);
|
|
24466
|
+
var writeWorkspaceFileSchema = z6.object({
|
|
24467
|
+
path: workspaceFilePathSchema,
|
|
24468
|
+
content: z6.string()
|
|
24469
|
+
});
|
|
24470
|
+
var moveWorkspaceFileSchema = z6.object({
|
|
24471
|
+
fromPath: workspaceFilePathSchema,
|
|
24472
|
+
toPath: workspaceFilePathSchema,
|
|
24473
|
+
overwrite: z6.boolean().optional()
|
|
24474
|
+
});
|
|
24475
|
+
var deleteWorkspaceFileSchema = z6.object({
|
|
24476
|
+
path: workspaceFilePathSchema,
|
|
24477
|
+
recursive: z6.boolean().optional()
|
|
24478
|
+
});
|
|
24479
|
+
var workspaceArtifactIdSchema = z6.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/);
|
|
24480
|
+
var workspaceArtifactNameSchema = z6.string().trim().min(1).max(255);
|
|
24481
|
+
var createWorkspaceArtifactSchema = z6.object({
|
|
24482
|
+
id: workspaceArtifactIdSchema.optional(),
|
|
24483
|
+
name: workspaceArtifactNameSchema,
|
|
24484
|
+
mediaType: z6.string().trim().min(1).max(255).default("application/octet-stream"),
|
|
24485
|
+
contentBase64: z6.string().min(1),
|
|
24486
|
+
metadata: z6.record(z6.string(), z6.unknown()).optional()
|
|
24487
|
+
});
|
|
24488
|
+
var treeQuerySchema = z6.object({
|
|
24489
|
+
path: z6.string().optional(),
|
|
24490
|
+
showHidden: z6.coerce.boolean().optional()
|
|
24491
|
+
});
|
|
24492
|
+
var workspaceFileQuerySchema = z6.object({
|
|
24493
|
+
path: z6.string().optional().default("")
|
|
24494
|
+
});
|
|
24495
|
+
var workspacePreviewQuerySchema = z6.object({
|
|
24496
|
+
path: z6.string().min(1),
|
|
24497
|
+
offset: z6.coerce.number().int().min(0).optional(),
|
|
24498
|
+
limit: z6.coerce.number().int().positive().max(25e4).optional()
|
|
24499
|
+
});
|
|
24500
|
+
function requireWorkspaceRecord(app, workspaceId) {
|
|
24501
|
+
const record = getWorkspaceRecordById(app.services.database.db, workspaceId);
|
|
24502
|
+
if (!record) {
|
|
24503
|
+
throw new HttpError(404, {
|
|
24504
|
+
code: "not_found",
|
|
24505
|
+
message: "Workspace was not found."
|
|
24506
|
+
});
|
|
24507
|
+
}
|
|
24508
|
+
return record;
|
|
24509
|
+
}
|
|
24573
24510
|
async function registerWorkspaceRoutes(app) {
|
|
24574
24511
|
app.get("/api/workspaces", async () => {
|
|
24575
24512
|
const records = listWorkspaceRecords(app.services.database.db);
|
|
@@ -24577,7 +24514,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24577
24514
|
});
|
|
24578
24515
|
app.get("/api/workspaces/tree", async (request) => {
|
|
24579
24516
|
const query = treeQuerySchema.parse(request.query);
|
|
24580
|
-
const requestedPath = query.path ?
|
|
24517
|
+
const requestedPath = query.path ? path21.resolve(query.path) : app.services.config.workspaceRoot;
|
|
24581
24518
|
const tree = await readWorkspaceTree({
|
|
24582
24519
|
rootPath: app.services.config.workspaceRoot,
|
|
24583
24520
|
targetPath: requestedPath,
|
|
@@ -24604,7 +24541,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24604
24541
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24605
24542
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
24606
24543
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24607
|
-
const rootPath = await
|
|
24544
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24608
24545
|
const targetPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24609
24546
|
return buildWorkspaceTreeNode(rootPath, targetPath);
|
|
24610
24547
|
});
|
|
@@ -24624,9 +24561,9 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24624
24561
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24625
24562
|
const query = workspacePreviewQuerySchema.parse(request.query);
|
|
24626
24563
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24627
|
-
const rootPath = await
|
|
24564
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24628
24565
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24629
|
-
const stats = await
|
|
24566
|
+
const stats = await fs20.stat(filePath);
|
|
24630
24567
|
if (!stats.isFile()) {
|
|
24631
24568
|
throw new HttpError(400, {
|
|
24632
24569
|
code: "bad_request",
|
|
@@ -24635,7 +24572,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24635
24572
|
}
|
|
24636
24573
|
const offset = query.offset ?? 0;
|
|
24637
24574
|
const limit = query.limit ?? PREVIEW_DEFAULT_LIMIT_BYTES;
|
|
24638
|
-
const handle = await
|
|
24575
|
+
const handle = await fs20.open(filePath, "r");
|
|
24639
24576
|
try {
|
|
24640
24577
|
const length = Math.min(limit, Math.max(0, stats.size - offset));
|
|
24641
24578
|
const buffer = Buffer.alloc(length);
|
|
@@ -24643,7 +24580,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24643
24580
|
const nextOffset = offset + read.bytesRead;
|
|
24644
24581
|
return {
|
|
24645
24582
|
path: relativeWorkspacePath(rootPath, filePath),
|
|
24646
|
-
name:
|
|
24583
|
+
name: path21.basename(filePath),
|
|
24647
24584
|
content: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
24648
24585
|
language: languageForPath(filePath),
|
|
24649
24586
|
size: stats.size,
|
|
@@ -24658,9 +24595,9 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24658
24595
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24659
24596
|
const query = workspacePreviewQuerySchema.pick({ path: true }).parse(request.query);
|
|
24660
24597
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24661
|
-
const rootPath = await
|
|
24598
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24662
24599
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24663
|
-
const stats = await
|
|
24600
|
+
const stats = await fs20.stat(filePath);
|
|
24664
24601
|
if (!stats.isFile()) {
|
|
24665
24602
|
throw new HttpError(400, {
|
|
24666
24603
|
code: "bad_request",
|
|
@@ -24668,18 +24605,18 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24668
24605
|
});
|
|
24669
24606
|
}
|
|
24670
24607
|
reply.header("content-type", contentTypeForPath(filePath));
|
|
24671
|
-
return reply.send(Readable.from(await
|
|
24608
|
+
return reply.send(Readable.from(await fs20.readFile(filePath)));
|
|
24672
24609
|
});
|
|
24673
24610
|
app.get("/api/workspaces/:id/files/download", async (request, reply) => {
|
|
24674
24611
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24675
24612
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
24676
24613
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24677
|
-
const rootPath = await
|
|
24614
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24678
24615
|
const itemPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24679
|
-
const stats = await
|
|
24616
|
+
const stats = await fs20.stat(itemPath);
|
|
24680
24617
|
if (stats.isDirectory()) {
|
|
24681
24618
|
const { zipPath, tempDir } = await createFolderZipFile(rootPath, itemPath);
|
|
24682
|
-
const filename2 = `${
|
|
24619
|
+
const filename2 = `${path21.basename(itemPath) || "workspace-folder"}.zip`;
|
|
24683
24620
|
const cleanup = cleanupTemporaryZip(zipPath, tempDir);
|
|
24684
24621
|
reply.raw.once("finish", () => void cleanup());
|
|
24685
24622
|
reply.raw.once("close", () => void cleanup());
|
|
@@ -24695,18 +24632,18 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24695
24632
|
message: "Only file and folder downloads are supported from this endpoint."
|
|
24696
24633
|
});
|
|
24697
24634
|
}
|
|
24698
|
-
const filename =
|
|
24635
|
+
const filename = path21.basename(itemPath);
|
|
24699
24636
|
reply.header("content-type", contentTypeForPath(itemPath)).header(
|
|
24700
24637
|
"content-disposition",
|
|
24701
24638
|
`attachment; filename="${filename}"; filename*=UTF-8''${encodeURIComponent(filename)}`
|
|
24702
24639
|
);
|
|
24703
|
-
return reply.send(Readable.from(await
|
|
24640
|
+
return reply.send(Readable.from(await fs20.readFile(itemPath)));
|
|
24704
24641
|
});
|
|
24705
24642
|
app.post("/api/workspaces/:id/files/upload", async (request) => {
|
|
24706
24643
|
requireWorkerScope(request, "file:write");
|
|
24707
24644
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24708
24645
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24709
|
-
const rootPath = await
|
|
24646
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24710
24647
|
const uploadRequest = request;
|
|
24711
24648
|
if (!uploadRequest.isMultipart()) {
|
|
24712
24649
|
throw new HttpError(400, {
|
|
@@ -24761,7 +24698,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24761
24698
|
kind: "file",
|
|
24762
24699
|
file: {
|
|
24763
24700
|
path: file.path,
|
|
24764
|
-
name:
|
|
24701
|
+
name: path21.basename(file.path),
|
|
24765
24702
|
size: file.size
|
|
24766
24703
|
}
|
|
24767
24704
|
};
|
|
@@ -24797,36 +24734,14 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24797
24734
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24798
24735
|
const artifactId = body.id ?? artifactIdFromName(body.name);
|
|
24799
24736
|
const content = Buffer.from(body.contentBase64, "base64");
|
|
24800
|
-
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
});
|
|
24805
|
-
}
|
|
24806
|
-
const dir = path19.dirname(artifactFilePath(record, artifactId));
|
|
24807
|
-
await fs18.mkdir(dir, { recursive: true, mode: 448 });
|
|
24808
|
-
const filePath = artifactFilePath(record, artifactId);
|
|
24809
|
-
await fs18.writeFile(filePath, content, { flag: "wx" }).catch((error) => {
|
|
24810
|
-
if (error.code === "EEXIST") {
|
|
24811
|
-
throw new HttpError(409, {
|
|
24812
|
-
code: "conflict",
|
|
24813
|
-
message: "Workspace artifact already exists."
|
|
24814
|
-
});
|
|
24815
|
-
}
|
|
24816
|
-
throw error;
|
|
24817
|
-
});
|
|
24818
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24819
|
-
const artifact = {
|
|
24820
|
-
id: artifactId,
|
|
24821
|
-
workspaceId: record.id,
|
|
24822
|
-
name: safeArtifactFileName(body.name),
|
|
24737
|
+
const artifact = await createWorkspaceArtifact({
|
|
24738
|
+
record,
|
|
24739
|
+
artifactId,
|
|
24740
|
+
name: body.name,
|
|
24823
24741
|
mediaType: body.mediaType,
|
|
24824
|
-
|
|
24825
|
-
|
|
24826
|
-
|
|
24827
|
-
metadata: body.metadata ?? {}
|
|
24828
|
-
};
|
|
24829
|
-
await fs18.writeFile(artifactMetadataPath(record, artifactId), JSON.stringify(artifact, null, 2));
|
|
24742
|
+
content,
|
|
24743
|
+
...body.metadata !== void 0 ? { metadata: body.metadata } : {}
|
|
24744
|
+
});
|
|
24830
24745
|
return { artifact };
|
|
24831
24746
|
});
|
|
24832
24747
|
app.get("/api/workspaces/:id/artifacts", async (request) => {
|
|
@@ -24839,25 +24754,14 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24839
24754
|
requireWorkerScope(request, "artifact:read");
|
|
24840
24755
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24841
24756
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24842
|
-
return { artifact: await
|
|
24757
|
+
return { artifact: await readWorkspaceArtifactMetadata(record, params.artifactId) };
|
|
24843
24758
|
});
|
|
24844
24759
|
app.get("/api/workspaces/:id/artifacts/:artifactId/download", async (request, reply) => {
|
|
24845
24760
|
requireWorkerScope(request, "artifact:read");
|
|
24846
24761
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24847
24762
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24848
|
-
const artifact = await
|
|
24849
|
-
|
|
24850
|
-
try {
|
|
24851
|
-
content = await fs18.readFile(artifactFilePath(record, params.artifactId));
|
|
24852
|
-
} catch (error) {
|
|
24853
|
-
if (error.code === "ENOENT") {
|
|
24854
|
-
throw new HttpError(404, {
|
|
24855
|
-
code: "not_found",
|
|
24856
|
-
message: "Workspace artifact content was not found."
|
|
24857
|
-
});
|
|
24858
|
-
}
|
|
24859
|
-
throw error;
|
|
24860
|
-
}
|
|
24763
|
+
const artifact = await readWorkspaceArtifactMetadata(record, params.artifactId);
|
|
24764
|
+
const content = await readWorkspaceArtifactContent(record, params.artifactId);
|
|
24861
24765
|
reply.header("content-type", artifact.mediaType).header("content-length", String(content.length)).header("content-disposition", `attachment; filename="${artifact.name.replace(/"/g, "")}"`);
|
|
24862
24766
|
return reply.send(content);
|
|
24863
24767
|
});
|
|
@@ -24865,11 +24769,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24865
24769
|
requireWorkerScope(request, "artifact:write");
|
|
24866
24770
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24867
24771
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24868
|
-
const artifact = await
|
|
24869
|
-
await fs18.rm(path19.dirname(artifactFilePath(record, params.artifactId)), {
|
|
24870
|
-
recursive: true,
|
|
24871
|
-
force: true
|
|
24872
|
-
});
|
|
24772
|
+
const artifact = await deleteWorkspaceArtifact(record, params.artifactId);
|
|
24873
24773
|
return { deleted: true, artifact };
|
|
24874
24774
|
});
|
|
24875
24775
|
app.post("/api/workspaces", async (request) => {
|
|
@@ -24881,7 +24781,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24881
24781
|
let validated;
|
|
24882
24782
|
if ("gitUrl" in body) {
|
|
24883
24783
|
const repoName = inferGitRepoName(body.gitUrl);
|
|
24884
|
-
const targetPath =
|
|
24784
|
+
const targetPath = path21.join(settings.devHome, repoName);
|
|
24885
24785
|
if (await pathExists4(targetPath)) {
|
|
24886
24786
|
throw new HttpError(409, {
|
|
24887
24787
|
code: "conflict",
|
|
@@ -25144,8 +25044,8 @@ async function registerAuthRoutes(app) {
|
|
|
25144
25044
|
}
|
|
25145
25045
|
|
|
25146
25046
|
// src/provider-host-config-service.ts
|
|
25147
|
-
import
|
|
25148
|
-
import
|
|
25047
|
+
import fs21 from "fs/promises";
|
|
25048
|
+
import path22 from "path";
|
|
25149
25049
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
25150
25050
|
function providerError(message, statusCode = 404) {
|
|
25151
25051
|
const error = new Error(message);
|
|
@@ -25153,23 +25053,23 @@ function providerError(message, statusCode = 404) {
|
|
|
25153
25053
|
return error;
|
|
25154
25054
|
}
|
|
25155
25055
|
function resolveProviderHostFilePath(providerHome, name) {
|
|
25156
|
-
return
|
|
25056
|
+
return path22.join(providerHome, name);
|
|
25157
25057
|
}
|
|
25158
25058
|
function resolveArchiveRoot(providerHome) {
|
|
25159
|
-
return
|
|
25059
|
+
return path22.join(providerHome, "supervisor-config-archives");
|
|
25160
25060
|
}
|
|
25161
25061
|
function resolveArchiveIndexPath(providerHome) {
|
|
25162
|
-
return
|
|
25062
|
+
return path22.join(resolveArchiveRoot(providerHome), "index.json");
|
|
25163
25063
|
}
|
|
25164
25064
|
function resolveArchivePath(providerHome, archiveId) {
|
|
25165
|
-
return
|
|
25065
|
+
return path22.join(resolveArchiveRoot(providerHome), archiveId);
|
|
25166
25066
|
}
|
|
25167
25067
|
function defaultArchiveLabel(createdAt) {
|
|
25168
25068
|
return `Backup ${createdAt.replace("T", " ").replace(/\.\d{3}Z$/, " UTC")}`;
|
|
25169
25069
|
}
|
|
25170
25070
|
async function readArchiveIndex(providerHome) {
|
|
25171
25071
|
try {
|
|
25172
|
-
const raw = await
|
|
25072
|
+
const raw = await fs21.readFile(resolveArchiveIndexPath(providerHome), "utf8");
|
|
25173
25073
|
const parsed = JSON.parse(raw);
|
|
25174
25074
|
return {
|
|
25175
25075
|
archives: Array.isArray(parsed.archives) ? parsed.archives : []
|
|
@@ -25183,8 +25083,8 @@ async function readArchiveIndex(providerHome) {
|
|
|
25183
25083
|
}
|
|
25184
25084
|
async function writeArchiveIndex(providerHome, index) {
|
|
25185
25085
|
const root = resolveArchiveRoot(providerHome);
|
|
25186
|
-
await
|
|
25187
|
-
await
|
|
25086
|
+
await fs21.mkdir(root, { recursive: true });
|
|
25087
|
+
await fs21.writeFile(
|
|
25188
25088
|
resolveArchiveIndexPath(providerHome),
|
|
25189
25089
|
`${JSON.stringify(index, null, 2)}
|
|
25190
25090
|
`,
|
|
@@ -25255,7 +25155,7 @@ var ProviderHostConfigService = class {
|
|
|
25255
25155
|
const fileName = this.assertHostFile(provider2, name);
|
|
25256
25156
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
25257
25157
|
try {
|
|
25258
|
-
const content = await
|
|
25158
|
+
const content = await fs21.readFile(filePath, "utf8");
|
|
25259
25159
|
return {
|
|
25260
25160
|
name: fileName,
|
|
25261
25161
|
path: filePath,
|
|
@@ -25278,8 +25178,8 @@ var ProviderHostConfigService = class {
|
|
|
25278
25178
|
const providerHome = this.providerHome(provider2);
|
|
25279
25179
|
const fileName = this.assertHostFile(provider2, name);
|
|
25280
25180
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
25281
|
-
await
|
|
25282
|
-
await
|
|
25181
|
+
await fs21.mkdir(path22.dirname(filePath), { recursive: true });
|
|
25182
|
+
await fs21.writeFile(filePath, input.content, "utf8");
|
|
25283
25183
|
return this.readFile(provider2, fileName);
|
|
25284
25184
|
}
|
|
25285
25185
|
async listArchives(provider2) {
|
|
@@ -25305,7 +25205,7 @@ var ProviderHostConfigService = class {
|
|
|
25305
25205
|
}
|
|
25306
25206
|
])
|
|
25307
25207
|
);
|
|
25308
|
-
await
|
|
25208
|
+
await fs21.mkdir(archivePath, { recursive: true });
|
|
25309
25209
|
for (const name of fileNames) {
|
|
25310
25210
|
const hostFile = await this.readFile(provider2, name);
|
|
25311
25211
|
files[name] = {
|
|
@@ -25313,7 +25213,7 @@ var ProviderHostConfigService = class {
|
|
|
25313
25213
|
exists: hostFile.exists
|
|
25314
25214
|
};
|
|
25315
25215
|
if (hostFile.exists) {
|
|
25316
|
-
await
|
|
25216
|
+
await fs21.writeFile(path22.join(archivePath, name), hostFile.content, "utf8");
|
|
25317
25217
|
}
|
|
25318
25218
|
}
|
|
25319
25219
|
const archive = {
|
|
@@ -25349,14 +25249,14 @@ var ProviderHostConfigService = class {
|
|
|
25349
25249
|
const fileNames = this.archiveFileNames(provider2);
|
|
25350
25250
|
const { archive } = await findArchiveOrThrow(providerHome, id);
|
|
25351
25251
|
const archivePath = resolveArchivePath(providerHome, archive.id);
|
|
25352
|
-
await
|
|
25252
|
+
await fs21.mkdir(providerHome, { recursive: true });
|
|
25353
25253
|
for (const name of fileNames) {
|
|
25354
25254
|
const hostPath = resolveProviderHostFilePath(providerHome, name);
|
|
25355
25255
|
if (archive.files[name]?.exists) {
|
|
25356
|
-
const content = await
|
|
25357
|
-
await
|
|
25256
|
+
const content = await fs21.readFile(path22.join(archivePath, name), "utf8");
|
|
25257
|
+
await fs21.writeFile(hostPath, content, "utf8");
|
|
25358
25258
|
} else {
|
|
25359
|
-
await
|
|
25259
|
+
await fs21.rm(hostPath, { force: true });
|
|
25360
25260
|
}
|
|
25361
25261
|
}
|
|
25362
25262
|
await runtime.stop();
|
|
@@ -25369,12 +25269,12 @@ var ProviderHostConfigService = class {
|
|
|
25369
25269
|
};
|
|
25370
25270
|
|
|
25371
25271
|
// src/shell/shell-session-service.ts
|
|
25372
|
-
import
|
|
25272
|
+
import fs23 from "fs/promises";
|
|
25373
25273
|
|
|
25374
25274
|
// src/shell/shell-prompt.ts
|
|
25375
|
-
import
|
|
25275
|
+
import fs22 from "fs/promises";
|
|
25376
25276
|
import os5 from "os";
|
|
25377
|
-
import
|
|
25277
|
+
import path23 from "path";
|
|
25378
25278
|
function basenameFromPath2(filePath) {
|
|
25379
25279
|
if (!filePath) {
|
|
25380
25280
|
return "";
|
|
@@ -25383,7 +25283,7 @@ function basenameFromPath2(filePath) {
|
|
|
25383
25283
|
if (!normalized) {
|
|
25384
25284
|
return "";
|
|
25385
25285
|
}
|
|
25386
|
-
return
|
|
25286
|
+
return path23.basename(normalized) || normalized;
|
|
25387
25287
|
}
|
|
25388
25288
|
function isInteractiveShellCommand(command) {
|
|
25389
25289
|
const normalized = (command ?? "").trim().toLowerCase();
|
|
@@ -25544,11 +25444,11 @@ function buildShellPromptInitScriptContents(command) {
|
|
|
25544
25444
|
async function ensureShellPromptInitScript(command) {
|
|
25545
25445
|
const normalized = command.trim().toLowerCase();
|
|
25546
25446
|
const extension = normalized === "zsh" ? "zsh" : "sh";
|
|
25547
|
-
const filePath =
|
|
25447
|
+
const filePath = path23.join(
|
|
25548
25448
|
os5.tmpdir(),
|
|
25549
25449
|
`remote-codex-shell-prompt.${extension}`
|
|
25550
25450
|
);
|
|
25551
|
-
await
|
|
25451
|
+
await fs22.writeFile(filePath, buildShellPromptInitScriptContents(command), "utf8");
|
|
25552
25452
|
return filePath;
|
|
25553
25453
|
}
|
|
25554
25454
|
async function buildShellPromptInitCommand(command, options = {}) {
|
|
@@ -25564,7 +25464,7 @@ clear
|
|
|
25564
25464
|
// src/shell/shell-session-service.ts
|
|
25565
25465
|
async function pathExists5(filePath) {
|
|
25566
25466
|
try {
|
|
25567
|
-
await
|
|
25467
|
+
await fs23.access(filePath);
|
|
25568
25468
|
return true;
|
|
25569
25469
|
} catch {
|
|
25570
25470
|
return false;
|
|
@@ -26222,8 +26122,8 @@ var builtinPlugins = [
|
|
|
26222
26122
|
];
|
|
26223
26123
|
|
|
26224
26124
|
// src/plugins/plugin-service.ts
|
|
26225
|
-
import
|
|
26226
|
-
import
|
|
26125
|
+
import fs24 from "fs/promises";
|
|
26126
|
+
import path24 from "path";
|
|
26227
26127
|
var MANAGED_CODEX_MCP_BEGIN = "# BEGIN remote-codex managed plugin MCP servers";
|
|
26228
26128
|
var MANAGED_CODEX_MCP_END = "# END remote-codex managed plugin MCP servers";
|
|
26229
26129
|
var REMOTE_CODEX_MOLECULE_MCP_TOOL_NAME = "remote_codex_render_molecule";
|
|
@@ -26235,7 +26135,7 @@ function normalizeManagedCommand(server, repoRoot) {
|
|
|
26235
26135
|
if (server.name === "remote_codex_plugins") {
|
|
26236
26136
|
return {
|
|
26237
26137
|
command: process.execPath,
|
|
26238
|
-
args: [
|
|
26138
|
+
args: [path24.join(repoRoot, "bin", "remote-codex-plugin-mcp.mjs")]
|
|
26239
26139
|
};
|
|
26240
26140
|
}
|
|
26241
26141
|
return {
|
|
@@ -26422,10 +26322,10 @@ var PluginService = class {
|
|
|
26422
26322
|
if (!input.codexHome) {
|
|
26423
26323
|
return;
|
|
26424
26324
|
}
|
|
26425
|
-
const configPath =
|
|
26325
|
+
const configPath = path24.join(input.codexHome, "config.toml");
|
|
26426
26326
|
let current = "";
|
|
26427
26327
|
try {
|
|
26428
|
-
current = await
|
|
26328
|
+
current = await fs24.readFile(configPath, "utf8");
|
|
26429
26329
|
} catch (error) {
|
|
26430
26330
|
if (error.code !== "ENOENT") {
|
|
26431
26331
|
throw error;
|
|
@@ -26440,8 +26340,8 @@ var PluginService = class {
|
|
|
26440
26340
|
if (next === current) {
|
|
26441
26341
|
return;
|
|
26442
26342
|
}
|
|
26443
|
-
await
|
|
26444
|
-
await
|
|
26343
|
+
await fs24.mkdir(path24.dirname(configPath), { recursive: true });
|
|
26344
|
+
await fs24.writeFile(configPath, next, "utf8");
|
|
26445
26345
|
}
|
|
26446
26346
|
async importPlugin(input) {
|
|
26447
26347
|
const manifestInput = input.manifest ?? (input.manifestUrl ? await this.fetchManifestUrl(input.manifestUrl) : this.parseManifestJson(input.manifestJson));
|
|
@@ -26680,8 +26580,8 @@ var PluginSettingsStore = class {
|
|
|
26680
26580
|
};
|
|
26681
26581
|
|
|
26682
26582
|
// src/worker-bootstrap.ts
|
|
26683
|
-
import
|
|
26684
|
-
import
|
|
26583
|
+
import fs25 from "fs/promises";
|
|
26584
|
+
import path25 from "path";
|
|
26685
26585
|
function trimTrailingSlash(value) {
|
|
26686
26586
|
return value.replace(/\/+$/, "");
|
|
26687
26587
|
}
|
|
@@ -26692,9 +26592,9 @@ function tomlString(value) {
|
|
|
26692
26592
|
return JSON.stringify(value);
|
|
26693
26593
|
}
|
|
26694
26594
|
async function writePrivateFile(filePath, content) {
|
|
26695
|
-
await
|
|
26696
|
-
await
|
|
26697
|
-
await
|
|
26595
|
+
await fs25.mkdir(path25.dirname(filePath), { recursive: true, mode: 448 });
|
|
26596
|
+
await fs25.writeFile(filePath, content, { encoding: "utf8", mode: 384 });
|
|
26597
|
+
await fs25.chmod(filePath, 384);
|
|
26698
26598
|
}
|
|
26699
26599
|
async function configureWorkerProviderGateway(config) {
|
|
26700
26600
|
if (config.runtimeRole !== "worker" || !config.llmGatewayBaseUrl || !config.llmGatewayToken) {
|
|
@@ -26708,7 +26608,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26708
26608
|
process.env.ANTHROPIC_BASE_URL = anthropicBaseUrl;
|
|
26709
26609
|
if (config.agentProviders.codex.enabled) {
|
|
26710
26610
|
await writePrivateFile(
|
|
26711
|
-
|
|
26611
|
+
path25.join(config.agentProviders.codex.home, "config.toml"),
|
|
26712
26612
|
[
|
|
26713
26613
|
'model_provider = "sub2api"',
|
|
26714
26614
|
'forced_login_method = "api"',
|
|
@@ -26724,7 +26624,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26724
26624
|
].join("\n")
|
|
26725
26625
|
);
|
|
26726
26626
|
await writePrivateFile(
|
|
26727
|
-
|
|
26627
|
+
path25.join(config.agentProviders.codex.home, "auth.json"),
|
|
26728
26628
|
`${JSON.stringify(
|
|
26729
26629
|
{
|
|
26730
26630
|
OPENAI_API_KEY: config.llmGatewayToken
|
|
@@ -26737,7 +26637,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26737
26637
|
}
|
|
26738
26638
|
if (config.agentProviders.claude.enabled) {
|
|
26739
26639
|
await writePrivateFile(
|
|
26740
|
-
|
|
26640
|
+
path25.join(config.agentProviders.claude.home, "settings.json"),
|
|
26741
26641
|
`${JSON.stringify(
|
|
26742
26642
|
{
|
|
26743
26643
|
env: {
|
|
@@ -26753,7 +26653,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26753
26653
|
}
|
|
26754
26654
|
if (config.agentProviders.opencode.enabled) {
|
|
26755
26655
|
await writePrivateFile(
|
|
26756
|
-
|
|
26656
|
+
path25.join(config.agentProviders.opencode.home, "opencode.json"),
|
|
26757
26657
|
`${JSON.stringify(
|
|
26758
26658
|
{
|
|
26759
26659
|
provider: {
|
|
@@ -26800,27 +26700,27 @@ var BackendPluginHost = class {
|
|
|
26800
26700
|
};
|
|
26801
26701
|
|
|
26802
26702
|
// src/shell/pty-shell-backend.ts
|
|
26803
|
-
import
|
|
26703
|
+
import path26 from "path";
|
|
26804
26704
|
import { spawn as spawn5 } from "@homebridge/node-pty-prebuilt-multiarch";
|
|
26805
26705
|
|
|
26806
26706
|
// src/shell/default-shell.ts
|
|
26807
|
-
import
|
|
26707
|
+
import fs26 from "fs";
|
|
26808
26708
|
var POSIX_SHELL_CANDIDATES = ["/bin/bash", "/usr/bin/bash", "/bin/sh"];
|
|
26809
26709
|
function resolveDefaultShell(env = process.env) {
|
|
26810
26710
|
if (process.platform === "win32") {
|
|
26811
26711
|
return env.COMSPEC ?? "cmd.exe";
|
|
26812
26712
|
}
|
|
26813
|
-
if (env.SHELL &&
|
|
26713
|
+
if (env.SHELL && fs26.existsSync(env.SHELL)) {
|
|
26814
26714
|
return env.SHELL;
|
|
26815
26715
|
}
|
|
26816
|
-
return POSIX_SHELL_CANDIDATES.find((candidate) =>
|
|
26716
|
+
return POSIX_SHELL_CANDIDATES.find((candidate) => fs26.existsSync(candidate)) ?? "/bin/sh";
|
|
26817
26717
|
}
|
|
26818
26718
|
|
|
26819
26719
|
// src/shell/pty-shell-backend.ts
|
|
26820
26720
|
var MAX_SCROLLBACK_BYTES = 512 * 1024;
|
|
26821
26721
|
var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g");
|
|
26822
26722
|
function shellArgs(shell) {
|
|
26823
|
-
const shellName =
|
|
26723
|
+
const shellName = path26.basename(shell).toLowerCase();
|
|
26824
26724
|
if (process.platform === "win32") {
|
|
26825
26725
|
return [];
|
|
26826
26726
|
}
|
|
@@ -26842,7 +26742,7 @@ function lastVisibleLine(snapshot) {
|
|
|
26842
26742
|
}
|
|
26843
26743
|
function inferRuntime(session) {
|
|
26844
26744
|
const promptLine = lastVisibleLine(session.scrollback);
|
|
26845
|
-
const shell =
|
|
26745
|
+
const shell = path26.basename(session.shell);
|
|
26846
26746
|
const isCommandRunning = session.exitCode !== null ? false : !/[$#>]\s*$/.test(promptLine.trimEnd());
|
|
26847
26747
|
return {
|
|
26848
26748
|
panePid: session.pty.pid,
|
|
@@ -26988,8 +26888,8 @@ var PtyShellBackend = class {
|
|
|
26988
26888
|
};
|
|
26989
26889
|
|
|
26990
26890
|
// src/shell/tmux-manager.ts
|
|
26991
|
-
import
|
|
26992
|
-
import
|
|
26891
|
+
import fs27 from "fs";
|
|
26892
|
+
import path27 from "path";
|
|
26993
26893
|
import { spawn as spawnChild } from "child_process";
|
|
26994
26894
|
async function defaultExecCommand(command, args) {
|
|
26995
26895
|
return await new Promise((resolve, reject) => {
|
|
@@ -27016,17 +26916,17 @@ async function defaultExecCommand(command, args) {
|
|
|
27016
26916
|
});
|
|
27017
26917
|
}
|
|
27018
26918
|
function resolveExecutablePath(command) {
|
|
27019
|
-
if (command.includes(
|
|
26919
|
+
if (command.includes(path27.sep)) {
|
|
27020
26920
|
return command;
|
|
27021
26921
|
}
|
|
27022
26922
|
const searchPath = process.env.PATH ?? "";
|
|
27023
|
-
for (const entry of searchPath.split(
|
|
26923
|
+
for (const entry of searchPath.split(path27.delimiter)) {
|
|
27024
26924
|
const trimmed = entry.trim();
|
|
27025
26925
|
if (!trimmed) {
|
|
27026
26926
|
continue;
|
|
27027
26927
|
}
|
|
27028
|
-
const candidate =
|
|
27029
|
-
if (
|
|
26928
|
+
const candidate = path27.join(trimmed, command);
|
|
26929
|
+
if (fs27.existsSync(candidate)) {
|
|
27030
26930
|
return candidate;
|
|
27031
26931
|
}
|
|
27032
26932
|
}
|
|
@@ -27787,22 +27687,22 @@ function payloadItems(payload, fields) {
|
|
|
27787
27687
|
const record = recordFrom(payload);
|
|
27788
27688
|
return arrayField(record, fields) ?? (record ? [record] : []);
|
|
27789
27689
|
}
|
|
27790
|
-
function artifactPreviewKind(type, format,
|
|
27690
|
+
function artifactPreviewKind(type, format, path29) {
|
|
27791
27691
|
const candidates = [
|
|
27792
27692
|
type,
|
|
27793
27693
|
format,
|
|
27794
|
-
|
|
27694
|
+
path29?.split(".").pop() ?? null
|
|
27795
27695
|
].map((value) => value?.trim().toLowerCase()).filter(Boolean);
|
|
27796
27696
|
return candidates.some((value) => MOLECULE_ARTIFACT_TYPES.has(value)) ? "molecule" : "file";
|
|
27797
27697
|
}
|
|
27798
27698
|
function normalizeArtifactRef(value) {
|
|
27799
27699
|
const record = recordFrom(value);
|
|
27800
|
-
const
|
|
27700
|
+
const path29 = stringField3(record, ["path", "filePath", "file_path", "filename", "fileName", "name"]);
|
|
27801
27701
|
const type = stringField3(record, ["type", "artifactType", "artifact_type", "format", "extension"]);
|
|
27802
|
-
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ??
|
|
27702
|
+
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ?? path29 ?? "artifact";
|
|
27803
27703
|
return {
|
|
27804
27704
|
title,
|
|
27805
|
-
path:
|
|
27705
|
+
path: path29,
|
|
27806
27706
|
type,
|
|
27807
27707
|
downloadUrl: stringField3(record, ["downloadUrl", "download_url", "url", "href"])
|
|
27808
27708
|
};
|
|
@@ -27836,21 +27736,21 @@ function normalizeArtifact(module, runId, value) {
|
|
|
27836
27736
|
if (!record) {
|
|
27837
27737
|
return null;
|
|
27838
27738
|
}
|
|
27839
|
-
const
|
|
27739
|
+
const path29 = stringField3(record, ["path", "filePath", "file_path", "filename", "fileName", "name"]);
|
|
27840
27740
|
const type = stringField3(record, ["type", "artifactType", "artifact_type"]);
|
|
27841
27741
|
const format = stringField3(record, ["format", "fileFormat", "file_format", "extension"]) ?? type;
|
|
27842
|
-
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ??
|
|
27742
|
+
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ?? path29 ?? `${module} artifact`;
|
|
27843
27743
|
return {
|
|
27844
27744
|
module,
|
|
27845
27745
|
runId,
|
|
27846
27746
|
title,
|
|
27847
|
-
path:
|
|
27747
|
+
path: path29,
|
|
27848
27748
|
type,
|
|
27849
27749
|
format,
|
|
27850
27750
|
mimeType: stringField3(record, ["mimeType", "mime_type", "contentType", "content_type"]),
|
|
27851
27751
|
sizeBytes: numberField2(record, ["sizeBytes", "size_bytes", "bytes"]),
|
|
27852
27752
|
downloadUrl: stringField3(record, ["downloadUrl", "download_url", "url", "href"]),
|
|
27853
|
-
previewKind: artifactPreviewKind(type, format,
|
|
27753
|
+
previewKind: artifactPreviewKind(type, format, path29)
|
|
27854
27754
|
};
|
|
27855
27755
|
}
|
|
27856
27756
|
function parseTomlScalar(raw) {
|
|
@@ -28080,9 +27980,9 @@ var WorkerHarnessClient = class {
|
|
|
28080
27980
|
}
|
|
28081
27981
|
return { baseUrl, apiKey };
|
|
28082
27982
|
}
|
|
28083
|
-
async fetchText(
|
|
27983
|
+
async fetchText(path29) {
|
|
28084
27984
|
const config = this.requireHarnessConfig();
|
|
28085
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
27985
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28086
27986
|
headers: {
|
|
28087
27987
|
"x-api-key": config.apiKey
|
|
28088
27988
|
}
|
|
@@ -28093,11 +27993,11 @@ var WorkerHarnessClient = class {
|
|
|
28093
27993
|
}
|
|
28094
27994
|
return { text: text2 };
|
|
28095
27995
|
}
|
|
28096
|
-
async fetchPayload(
|
|
27996
|
+
async fetchPayload(path29, init = {}) {
|
|
28097
27997
|
const config = this.requireHarnessConfig();
|
|
28098
27998
|
const headers = new Headers(init.headers);
|
|
28099
27999
|
headers.set("x-api-key", config.apiKey);
|
|
28100
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
28000
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28101
28001
|
...init,
|
|
28102
28002
|
headers
|
|
28103
28003
|
});
|
|
@@ -28111,9 +28011,9 @@ var WorkerHarnessClient = class {
|
|
|
28111
28011
|
return { text: text2 };
|
|
28112
28012
|
}
|
|
28113
28013
|
}
|
|
28114
|
-
async fetchBinary(
|
|
28014
|
+
async fetchBinary(path29) {
|
|
28115
28015
|
const config = this.requireHarnessConfig();
|
|
28116
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
28016
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28117
28017
|
headers: {
|
|
28118
28018
|
"x-api-key": config.apiKey
|
|
28119
28019
|
}
|
|
@@ -28957,16 +28857,16 @@ var HttpError = class extends Error {
|
|
|
28957
28857
|
};
|
|
28958
28858
|
function findRepoRoot(start = process.cwd()) {
|
|
28959
28859
|
if (process.env.REMOTE_CODEX_REPO_ROOT) {
|
|
28960
|
-
return
|
|
28860
|
+
return path28.resolve(process.env.REMOTE_CODEX_REPO_ROOT);
|
|
28961
28861
|
}
|
|
28962
|
-
let current =
|
|
28963
|
-
while (current !==
|
|
28964
|
-
if (
|
|
28862
|
+
let current = path28.resolve(start);
|
|
28863
|
+
while (current !== path28.dirname(current)) {
|
|
28864
|
+
if (fs28.existsSync(path28.join(current, "pnpm-workspace.yaml")) && fs28.existsSync(path28.join(current, "scripts", "service-restart.mjs"))) {
|
|
28965
28865
|
return current;
|
|
28966
28866
|
}
|
|
28967
|
-
current =
|
|
28867
|
+
current = path28.dirname(current);
|
|
28968
28868
|
}
|
|
28969
|
-
return
|
|
28869
|
+
return path28.resolve(process.cwd());
|
|
28970
28870
|
}
|
|
28971
28871
|
function createServiceLifecycle() {
|
|
28972
28872
|
return {
|
|
@@ -28978,8 +28878,8 @@ function createServiceLifecycle() {
|
|
|
28978
28878
|
});
|
|
28979
28879
|
}
|
|
28980
28880
|
const repoRoot = findRepoRoot();
|
|
28981
|
-
const restartScript =
|
|
28982
|
-
if (!
|
|
28881
|
+
const restartScript = path28.join(repoRoot, "scripts", "service-restart.mjs");
|
|
28882
|
+
if (!fs28.existsSync(restartScript) || !fs28.existsSync(path28.join(repoRoot, "pnpm-workspace.yaml"))) {
|
|
28983
28883
|
throw new HttpError(503, {
|
|
28984
28884
|
code: "service_unavailable",
|
|
28985
28885
|
message: "Build and restart requires a Remote Codex source checkout. Set REMOTE_CODEX_REPO_ROOT to the checkout path, or update the npm package with npm install -g remote-codex@latest."
|
|
@@ -29022,7 +28922,7 @@ function buildApp(options = {}) {
|
|
|
29022
28922
|
const shellService = options.shellService ?? new ShellSessionService(
|
|
29023
28923
|
database.db,
|
|
29024
28924
|
eventBus,
|
|
29025
|
-
createTerminalShellBackend(options.env)
|
|
28925
|
+
options.shellBackend ?? createTerminalShellBackend(options.env)
|
|
29026
28926
|
);
|
|
29027
28927
|
const providerHostConfigService = new ProviderHostConfigService(
|
|
29028
28928
|
agentRuntimes,
|