remote-codex 0.11.18 → 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-TGPTF6DT.js → chunk-IZBNFCMP.js} +418 -551
- 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-DnSeQdfq.js → thread-ui-TBhog-RK.js} +17 -17
- 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/apps/supervisor-web/dist/assets/index-C6wykq95.js +0 -6
|
@@ -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,64 +23914,137 @@ 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;
|
|
@@ -24174,7 +24080,7 @@ function toWorkspaceFileDto(file) {
|
|
|
24174
24080
|
};
|
|
24175
24081
|
}
|
|
24176
24082
|
function languageForPath(filePath) {
|
|
24177
|
-
const extension =
|
|
24083
|
+
const extension = path20.extname(filePath).slice(1).toLowerCase();
|
|
24178
24084
|
switch (extension) {
|
|
24179
24085
|
case "js":
|
|
24180
24086
|
case "jsx":
|
|
@@ -24220,18 +24126,18 @@ function languageForPath(filePath) {
|
|
|
24220
24126
|
}
|
|
24221
24127
|
}
|
|
24222
24128
|
function relativeWorkspacePath(rootPath, absPath) {
|
|
24223
|
-
const relative =
|
|
24224
|
-
return relative === "" ? "" : relative.split(
|
|
24129
|
+
const relative = path20.relative(rootPath, absPath);
|
|
24130
|
+
return relative === "" ? "" : relative.split(path20.sep).join("/");
|
|
24225
24131
|
}
|
|
24226
24132
|
async function resolveWorkspaceItemPath(rootPath, relativePath = "") {
|
|
24227
|
-
const candidate =
|
|
24133
|
+
const candidate = path20.resolve(rootPath, relativePath || ".");
|
|
24228
24134
|
const comparable = await assertPathWithinRoot(rootPath, candidate);
|
|
24229
24135
|
return comparable;
|
|
24230
24136
|
}
|
|
24231
24137
|
async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
24232
|
-
const stats = await
|
|
24138
|
+
const stats = await fs19.stat(absPath);
|
|
24233
24139
|
const relativePath = relativeWorkspacePath(rootPath, absPath);
|
|
24234
|
-
const name = relativePath ?
|
|
24140
|
+
const name = relativePath ? path20.basename(absPath) : path20.basename(rootPath);
|
|
24235
24141
|
if (!stats.isDirectory()) {
|
|
24236
24142
|
return {
|
|
24237
24143
|
name,
|
|
@@ -24249,7 +24155,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
24249
24155
|
};
|
|
24250
24156
|
const visible = [];
|
|
24251
24157
|
try {
|
|
24252
|
-
const directory = await
|
|
24158
|
+
const directory = await fs19.opendir(absPath);
|
|
24253
24159
|
let scanned = 0;
|
|
24254
24160
|
for await (const entry of directory) {
|
|
24255
24161
|
scanned += 1;
|
|
@@ -24284,7 +24190,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
24284
24190
|
});
|
|
24285
24191
|
node.children = (await Promise.all(
|
|
24286
24192
|
visible.map(async (entry) => {
|
|
24287
|
-
const childPath =
|
|
24193
|
+
const childPath = path20.join(absPath, entry.name);
|
|
24288
24194
|
try {
|
|
24289
24195
|
const childRelativePath = relativeWorkspacePath(rootPath, childPath);
|
|
24290
24196
|
if (entry.isDirectory()) {
|
|
@@ -24296,7 +24202,7 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
24296
24202
|
childrenLoaded: false
|
|
24297
24203
|
};
|
|
24298
24204
|
}
|
|
24299
|
-
const childStats = await
|
|
24205
|
+
const childStats = await fs19.stat(childPath);
|
|
24300
24206
|
return {
|
|
24301
24207
|
name: entry.name,
|
|
24302
24208
|
path: childRelativePath,
|
|
@@ -24310,73 +24216,8 @@ async function buildWorkspaceTreeNode(rootPath, absPath) {
|
|
|
24310
24216
|
)).filter((child) => child !== null);
|
|
24311
24217
|
return node;
|
|
24312
24218
|
}
|
|
24313
|
-
function requireWorkspaceRecord(app, workspaceId) {
|
|
24314
|
-
const record = getWorkspaceRecordById(app.services.database.db, workspaceId);
|
|
24315
|
-
if (!record) {
|
|
24316
|
-
throw new HttpError(404, {
|
|
24317
|
-
code: "not_found",
|
|
24318
|
-
message: "Workspace was not found."
|
|
24319
|
-
});
|
|
24320
|
-
}
|
|
24321
|
-
return record;
|
|
24322
|
-
}
|
|
24323
|
-
function artifactRoot(record) {
|
|
24324
|
-
return path19.join(record.absPath, ".remote-codex", "artifacts");
|
|
24325
|
-
}
|
|
24326
|
-
function artifactFilePath(record, artifactId) {
|
|
24327
|
-
return path19.join(artifactRoot(record), artifactId, "artifact.bin");
|
|
24328
|
-
}
|
|
24329
|
-
function artifactMetadataPath(record, artifactId) {
|
|
24330
|
-
return path19.join(artifactRoot(record), artifactId, "metadata.json");
|
|
24331
|
-
}
|
|
24332
|
-
function safeArtifactFileName(value) {
|
|
24333
|
-
return path19.basename(value).replace(/[^a-zA-Z0-9_. -]/g, "_") || "artifact.bin";
|
|
24334
|
-
}
|
|
24335
|
-
function artifactIdFromName(name) {
|
|
24336
|
-
const base = path19.basename(name).replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
|
|
24337
|
-
return `${base || "artifact"}-${Date.now().toString(36)}`;
|
|
24338
|
-
}
|
|
24339
|
-
async function readArtifactMetadata(record, artifactId) {
|
|
24340
|
-
try {
|
|
24341
|
-
const raw = await fs18.readFile(artifactMetadataPath(record, artifactId), "utf8");
|
|
24342
|
-
return JSON.parse(raw);
|
|
24343
|
-
} catch (error) {
|
|
24344
|
-
if (error.code === "ENOENT") {
|
|
24345
|
-
throw new HttpError(404, {
|
|
24346
|
-
code: "not_found",
|
|
24347
|
-
message: "Workspace artifact was not found."
|
|
24348
|
-
});
|
|
24349
|
-
}
|
|
24350
|
-
throw error;
|
|
24351
|
-
}
|
|
24352
|
-
}
|
|
24353
|
-
async function listWorkspaceArtifacts(record) {
|
|
24354
|
-
let entries;
|
|
24355
|
-
try {
|
|
24356
|
-
entries = await fs18.readdir(artifactRoot(record));
|
|
24357
|
-
} catch (error) {
|
|
24358
|
-
if (error.code === "ENOENT") {
|
|
24359
|
-
return [];
|
|
24360
|
-
}
|
|
24361
|
-
throw error;
|
|
24362
|
-
}
|
|
24363
|
-
const artifacts = [];
|
|
24364
|
-
for (const entry of entries) {
|
|
24365
|
-
if (!workspaceArtifactIdSchema.safeParse(entry).success) {
|
|
24366
|
-
continue;
|
|
24367
|
-
}
|
|
24368
|
-
try {
|
|
24369
|
-
artifacts.push(await readArtifactMetadata(record, entry));
|
|
24370
|
-
} catch (error) {
|
|
24371
|
-
if (!(error instanceof HttpError && error.statusCode === 404)) {
|
|
24372
|
-
throw error;
|
|
24373
|
-
}
|
|
24374
|
-
}
|
|
24375
|
-
}
|
|
24376
|
-
return artifacts.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
24377
|
-
}
|
|
24378
24219
|
function contentTypeForPath(filePath) {
|
|
24379
|
-
switch (
|
|
24220
|
+
switch (path20.extname(filePath).slice(1).toLowerCase()) {
|
|
24380
24221
|
case "png":
|
|
24381
24222
|
return "image/png";
|
|
24382
24223
|
case "jpg":
|
|
@@ -24406,15 +24247,15 @@ function contentTypeForPath(filePath) {
|
|
|
24406
24247
|
}
|
|
24407
24248
|
}
|
|
24408
24249
|
async function collectFolderZipEntries(rootPath, folderPath) {
|
|
24409
|
-
const folderName =
|
|
24250
|
+
const folderName = path20.basename(folderPath) || "workspace-folder";
|
|
24410
24251
|
const entries = [];
|
|
24411
24252
|
let totalBytes = 0;
|
|
24412
24253
|
const pending = [folderPath];
|
|
24413
24254
|
while (pending.length > 0) {
|
|
24414
24255
|
const current = pending.pop();
|
|
24415
|
-
const children = await
|
|
24256
|
+
const children = await fs19.readdir(current, { withFileTypes: true });
|
|
24416
24257
|
for (const child of children) {
|
|
24417
|
-
const childPath = await resolveWorkspaceItemPath(rootPath,
|
|
24258
|
+
const childPath = await resolveWorkspaceItemPath(rootPath, path20.relative(rootPath, path20.join(current, child.name)));
|
|
24418
24259
|
if (child.isDirectory()) {
|
|
24419
24260
|
pending.push(childPath);
|
|
24420
24261
|
continue;
|
|
@@ -24422,7 +24263,7 @@ async function collectFolderZipEntries(rootPath, folderPath) {
|
|
|
24422
24263
|
if (!child.isFile()) {
|
|
24423
24264
|
continue;
|
|
24424
24265
|
}
|
|
24425
|
-
const stats = await
|
|
24266
|
+
const stats = await fs19.stat(childPath);
|
|
24426
24267
|
totalBytes += stats.size;
|
|
24427
24268
|
entries.push({
|
|
24428
24269
|
absPath: childPath,
|
|
@@ -24473,8 +24314,8 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
24473
24314
|
const centralParts = [];
|
|
24474
24315
|
let offset = 0;
|
|
24475
24316
|
for (const entry of entries) {
|
|
24476
|
-
const data = await
|
|
24477
|
-
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");
|
|
24478
24319
|
const checksum = crc32(data);
|
|
24479
24320
|
const { dosDate, dosTime } = zipDosDateTime(entry.updatedAt);
|
|
24480
24321
|
const localHeader = Buffer.alloc(30);
|
|
@@ -24521,19 +24362,19 @@ async function createFolderZipFile(rootPath, folderPath) {
|
|
|
24521
24362
|
endRecord.writeUInt32LE(centralSize, 12);
|
|
24522
24363
|
endRecord.writeUInt32LE(offset, 16);
|
|
24523
24364
|
endRecord.writeUInt16LE(0, 20);
|
|
24524
|
-
const tempDir = await
|
|
24525
|
-
const zipPath =
|
|
24526
|
-
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]));
|
|
24527
24368
|
return { zipPath, tempDir };
|
|
24528
24369
|
}
|
|
24529
24370
|
function cleanupTemporaryZip(zipPath, tempDir) {
|
|
24530
24371
|
return async () => {
|
|
24531
|
-
await
|
|
24532
|
-
await
|
|
24372
|
+
await fs19.rm(zipPath, { force: true }).catch(() => void 0);
|
|
24373
|
+
await fs19.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
24533
24374
|
};
|
|
24534
24375
|
}
|
|
24535
24376
|
function sanitizeUploadFilename(filename) {
|
|
24536
|
-
const baseName =
|
|
24377
|
+
const baseName = path20.basename(filename?.trim() || "upload");
|
|
24537
24378
|
if (!baseName || baseName === "." || baseName === "..") {
|
|
24538
24379
|
return "upload";
|
|
24539
24380
|
}
|
|
@@ -24545,7 +24386,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
24545
24386
|
const normalized = withoutQuery.replace(/[\\/]+$/, "");
|
|
24546
24387
|
const rawName = normalized.split(/[/:]/).filter(Boolean).at(-1) ?? "";
|
|
24547
24388
|
const repoName = rawName.endsWith(".git") ? rawName.slice(0, -4) : rawName;
|
|
24548
|
-
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(
|
|
24389
|
+
if (!repoName || repoName === "." || repoName === ".." || repoName.includes(path20.sep)) {
|
|
24549
24390
|
throw new HttpError(400, {
|
|
24550
24391
|
code: "bad_request",
|
|
24551
24392
|
message: "Unable to infer a target directory from the Git URL."
|
|
@@ -24555,7 +24396,7 @@ function inferGitRepoName(gitUrl) {
|
|
|
24555
24396
|
}
|
|
24556
24397
|
async function pathExists4(absPath) {
|
|
24557
24398
|
try {
|
|
24558
|
-
await
|
|
24399
|
+
await fs19.stat(absPath);
|
|
24559
24400
|
return true;
|
|
24560
24401
|
} catch (error) {
|
|
24561
24402
|
if (error.code === "ENOENT") {
|
|
@@ -24603,6 +24444,69 @@ function cloneRepository(gitUrl, targetPath) {
|
|
|
24603
24444
|
});
|
|
24604
24445
|
});
|
|
24605
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
|
+
}
|
|
24606
24510
|
async function registerWorkspaceRoutes(app) {
|
|
24607
24511
|
app.get("/api/workspaces", async () => {
|
|
24608
24512
|
const records = listWorkspaceRecords(app.services.database.db);
|
|
@@ -24610,7 +24514,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24610
24514
|
});
|
|
24611
24515
|
app.get("/api/workspaces/tree", async (request) => {
|
|
24612
24516
|
const query = treeQuerySchema.parse(request.query);
|
|
24613
|
-
const requestedPath = query.path ?
|
|
24517
|
+
const requestedPath = query.path ? path21.resolve(query.path) : app.services.config.workspaceRoot;
|
|
24614
24518
|
const tree = await readWorkspaceTree({
|
|
24615
24519
|
rootPath: app.services.config.workspaceRoot,
|
|
24616
24520
|
targetPath: requestedPath,
|
|
@@ -24637,7 +24541,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24637
24541
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24638
24542
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
24639
24543
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24640
|
-
const rootPath = await
|
|
24544
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24641
24545
|
const targetPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24642
24546
|
return buildWorkspaceTreeNode(rootPath, targetPath);
|
|
24643
24547
|
});
|
|
@@ -24657,9 +24561,9 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24657
24561
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24658
24562
|
const query = workspacePreviewQuerySchema.parse(request.query);
|
|
24659
24563
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24660
|
-
const rootPath = await
|
|
24564
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24661
24565
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24662
|
-
const stats = await
|
|
24566
|
+
const stats = await fs20.stat(filePath);
|
|
24663
24567
|
if (!stats.isFile()) {
|
|
24664
24568
|
throw new HttpError(400, {
|
|
24665
24569
|
code: "bad_request",
|
|
@@ -24668,7 +24572,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24668
24572
|
}
|
|
24669
24573
|
const offset = query.offset ?? 0;
|
|
24670
24574
|
const limit = query.limit ?? PREVIEW_DEFAULT_LIMIT_BYTES;
|
|
24671
|
-
const handle = await
|
|
24575
|
+
const handle = await fs20.open(filePath, "r");
|
|
24672
24576
|
try {
|
|
24673
24577
|
const length = Math.min(limit, Math.max(0, stats.size - offset));
|
|
24674
24578
|
const buffer = Buffer.alloc(length);
|
|
@@ -24676,7 +24580,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24676
24580
|
const nextOffset = offset + read.bytesRead;
|
|
24677
24581
|
return {
|
|
24678
24582
|
path: relativeWorkspacePath(rootPath, filePath),
|
|
24679
|
-
name:
|
|
24583
|
+
name: path21.basename(filePath),
|
|
24680
24584
|
content: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
24681
24585
|
language: languageForPath(filePath),
|
|
24682
24586
|
size: stats.size,
|
|
@@ -24691,9 +24595,9 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24691
24595
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24692
24596
|
const query = workspacePreviewQuerySchema.pick({ path: true }).parse(request.query);
|
|
24693
24597
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24694
|
-
const rootPath = await
|
|
24598
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24695
24599
|
const filePath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24696
|
-
const stats = await
|
|
24600
|
+
const stats = await fs20.stat(filePath);
|
|
24697
24601
|
if (!stats.isFile()) {
|
|
24698
24602
|
throw new HttpError(400, {
|
|
24699
24603
|
code: "bad_request",
|
|
@@ -24701,18 +24605,18 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24701
24605
|
});
|
|
24702
24606
|
}
|
|
24703
24607
|
reply.header("content-type", contentTypeForPath(filePath));
|
|
24704
|
-
return reply.send(Readable.from(await
|
|
24608
|
+
return reply.send(Readable.from(await fs20.readFile(filePath)));
|
|
24705
24609
|
});
|
|
24706
24610
|
app.get("/api/workspaces/:id/files/download", async (request, reply) => {
|
|
24707
24611
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24708
24612
|
const query = workspaceFileQuerySchema.parse(request.query);
|
|
24709
24613
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24710
|
-
const rootPath = await
|
|
24614
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24711
24615
|
const itemPath = await resolveWorkspaceItemPath(rootPath, query.path);
|
|
24712
|
-
const stats = await
|
|
24616
|
+
const stats = await fs20.stat(itemPath);
|
|
24713
24617
|
if (stats.isDirectory()) {
|
|
24714
24618
|
const { zipPath, tempDir } = await createFolderZipFile(rootPath, itemPath);
|
|
24715
|
-
const filename2 = `${
|
|
24619
|
+
const filename2 = `${path21.basename(itemPath) || "workspace-folder"}.zip`;
|
|
24716
24620
|
const cleanup = cleanupTemporaryZip(zipPath, tempDir);
|
|
24717
24621
|
reply.raw.once("finish", () => void cleanup());
|
|
24718
24622
|
reply.raw.once("close", () => void cleanup());
|
|
@@ -24728,18 +24632,18 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24728
24632
|
message: "Only file and folder downloads are supported from this endpoint."
|
|
24729
24633
|
});
|
|
24730
24634
|
}
|
|
24731
|
-
const filename =
|
|
24635
|
+
const filename = path21.basename(itemPath);
|
|
24732
24636
|
reply.header("content-type", contentTypeForPath(itemPath)).header(
|
|
24733
24637
|
"content-disposition",
|
|
24734
24638
|
`attachment; filename="${filename}"; filename*=UTF-8''${encodeURIComponent(filename)}`
|
|
24735
24639
|
);
|
|
24736
|
-
return reply.send(Readable.from(await
|
|
24640
|
+
return reply.send(Readable.from(await fs20.readFile(itemPath)));
|
|
24737
24641
|
});
|
|
24738
24642
|
app.post("/api/workspaces/:id/files/upload", async (request) => {
|
|
24739
24643
|
requireWorkerScope(request, "file:write");
|
|
24740
24644
|
const params = z6.object({ id: z6.string().uuid() }).parse(request.params);
|
|
24741
24645
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24742
|
-
const rootPath = await
|
|
24646
|
+
const rootPath = await fs20.realpath(record.absPath);
|
|
24743
24647
|
const uploadRequest = request;
|
|
24744
24648
|
if (!uploadRequest.isMultipart()) {
|
|
24745
24649
|
throw new HttpError(400, {
|
|
@@ -24794,7 +24698,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24794
24698
|
kind: "file",
|
|
24795
24699
|
file: {
|
|
24796
24700
|
path: file.path,
|
|
24797
|
-
name:
|
|
24701
|
+
name: path21.basename(file.path),
|
|
24798
24702
|
size: file.size
|
|
24799
24703
|
}
|
|
24800
24704
|
};
|
|
@@ -24830,36 +24734,14 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24830
24734
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24831
24735
|
const artifactId = body.id ?? artifactIdFromName(body.name);
|
|
24832
24736
|
const content = Buffer.from(body.contentBase64, "base64");
|
|
24833
|
-
|
|
24834
|
-
|
|
24835
|
-
|
|
24836
|
-
|
|
24837
|
-
});
|
|
24838
|
-
}
|
|
24839
|
-
const dir = path19.dirname(artifactFilePath(record, artifactId));
|
|
24840
|
-
await fs18.mkdir(dir, { recursive: true, mode: 448 });
|
|
24841
|
-
const filePath = artifactFilePath(record, artifactId);
|
|
24842
|
-
await fs18.writeFile(filePath, content, { flag: "wx" }).catch((error) => {
|
|
24843
|
-
if (error.code === "EEXIST") {
|
|
24844
|
-
throw new HttpError(409, {
|
|
24845
|
-
code: "conflict",
|
|
24846
|
-
message: "Workspace artifact already exists."
|
|
24847
|
-
});
|
|
24848
|
-
}
|
|
24849
|
-
throw error;
|
|
24850
|
-
});
|
|
24851
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24852
|
-
const artifact = {
|
|
24853
|
-
id: artifactId,
|
|
24854
|
-
workspaceId: record.id,
|
|
24855
|
-
name: safeArtifactFileName(body.name),
|
|
24737
|
+
const artifact = await createWorkspaceArtifact({
|
|
24738
|
+
record,
|
|
24739
|
+
artifactId,
|
|
24740
|
+
name: body.name,
|
|
24856
24741
|
mediaType: body.mediaType,
|
|
24857
|
-
|
|
24858
|
-
|
|
24859
|
-
|
|
24860
|
-
metadata: body.metadata ?? {}
|
|
24861
|
-
};
|
|
24862
|
-
await fs18.writeFile(artifactMetadataPath(record, artifactId), JSON.stringify(artifact, null, 2));
|
|
24742
|
+
content,
|
|
24743
|
+
...body.metadata !== void 0 ? { metadata: body.metadata } : {}
|
|
24744
|
+
});
|
|
24863
24745
|
return { artifact };
|
|
24864
24746
|
});
|
|
24865
24747
|
app.get("/api/workspaces/:id/artifacts", async (request) => {
|
|
@@ -24872,25 +24754,14 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24872
24754
|
requireWorkerScope(request, "artifact:read");
|
|
24873
24755
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24874
24756
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24875
|
-
return { artifact: await
|
|
24757
|
+
return { artifact: await readWorkspaceArtifactMetadata(record, params.artifactId) };
|
|
24876
24758
|
});
|
|
24877
24759
|
app.get("/api/workspaces/:id/artifacts/:artifactId/download", async (request, reply) => {
|
|
24878
24760
|
requireWorkerScope(request, "artifact:read");
|
|
24879
24761
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24880
24762
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24881
|
-
const artifact = await
|
|
24882
|
-
|
|
24883
|
-
try {
|
|
24884
|
-
content = await fs18.readFile(artifactFilePath(record, params.artifactId));
|
|
24885
|
-
} catch (error) {
|
|
24886
|
-
if (error.code === "ENOENT") {
|
|
24887
|
-
throw new HttpError(404, {
|
|
24888
|
-
code: "not_found",
|
|
24889
|
-
message: "Workspace artifact content was not found."
|
|
24890
|
-
});
|
|
24891
|
-
}
|
|
24892
|
-
throw error;
|
|
24893
|
-
}
|
|
24763
|
+
const artifact = await readWorkspaceArtifactMetadata(record, params.artifactId);
|
|
24764
|
+
const content = await readWorkspaceArtifactContent(record, params.artifactId);
|
|
24894
24765
|
reply.header("content-type", artifact.mediaType).header("content-length", String(content.length)).header("content-disposition", `attachment; filename="${artifact.name.replace(/"/g, "")}"`);
|
|
24895
24766
|
return reply.send(content);
|
|
24896
24767
|
});
|
|
@@ -24898,11 +24769,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24898
24769
|
requireWorkerScope(request, "artifact:write");
|
|
24899
24770
|
const params = z6.object({ id: z6.string().uuid(), artifactId: workspaceArtifactIdSchema }).parse(request.params);
|
|
24900
24771
|
const record = requireWorkspaceRecord(app, params.id);
|
|
24901
|
-
const artifact = await
|
|
24902
|
-
await fs18.rm(path19.dirname(artifactFilePath(record, params.artifactId)), {
|
|
24903
|
-
recursive: true,
|
|
24904
|
-
force: true
|
|
24905
|
-
});
|
|
24772
|
+
const artifact = await deleteWorkspaceArtifact(record, params.artifactId);
|
|
24906
24773
|
return { deleted: true, artifact };
|
|
24907
24774
|
});
|
|
24908
24775
|
app.post("/api/workspaces", async (request) => {
|
|
@@ -24914,7 +24781,7 @@ async function registerWorkspaceRoutes(app) {
|
|
|
24914
24781
|
let validated;
|
|
24915
24782
|
if ("gitUrl" in body) {
|
|
24916
24783
|
const repoName = inferGitRepoName(body.gitUrl);
|
|
24917
|
-
const targetPath =
|
|
24784
|
+
const targetPath = path21.join(settings.devHome, repoName);
|
|
24918
24785
|
if (await pathExists4(targetPath)) {
|
|
24919
24786
|
throw new HttpError(409, {
|
|
24920
24787
|
code: "conflict",
|
|
@@ -25177,8 +25044,8 @@ async function registerAuthRoutes(app) {
|
|
|
25177
25044
|
}
|
|
25178
25045
|
|
|
25179
25046
|
// src/provider-host-config-service.ts
|
|
25180
|
-
import
|
|
25181
|
-
import
|
|
25047
|
+
import fs21 from "fs/promises";
|
|
25048
|
+
import path22 from "path";
|
|
25182
25049
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
25183
25050
|
function providerError(message, statusCode = 404) {
|
|
25184
25051
|
const error = new Error(message);
|
|
@@ -25186,23 +25053,23 @@ function providerError(message, statusCode = 404) {
|
|
|
25186
25053
|
return error;
|
|
25187
25054
|
}
|
|
25188
25055
|
function resolveProviderHostFilePath(providerHome, name) {
|
|
25189
|
-
return
|
|
25056
|
+
return path22.join(providerHome, name);
|
|
25190
25057
|
}
|
|
25191
25058
|
function resolveArchiveRoot(providerHome) {
|
|
25192
|
-
return
|
|
25059
|
+
return path22.join(providerHome, "supervisor-config-archives");
|
|
25193
25060
|
}
|
|
25194
25061
|
function resolveArchiveIndexPath(providerHome) {
|
|
25195
|
-
return
|
|
25062
|
+
return path22.join(resolveArchiveRoot(providerHome), "index.json");
|
|
25196
25063
|
}
|
|
25197
25064
|
function resolveArchivePath(providerHome, archiveId) {
|
|
25198
|
-
return
|
|
25065
|
+
return path22.join(resolveArchiveRoot(providerHome), archiveId);
|
|
25199
25066
|
}
|
|
25200
25067
|
function defaultArchiveLabel(createdAt) {
|
|
25201
25068
|
return `Backup ${createdAt.replace("T", " ").replace(/\.\d{3}Z$/, " UTC")}`;
|
|
25202
25069
|
}
|
|
25203
25070
|
async function readArchiveIndex(providerHome) {
|
|
25204
25071
|
try {
|
|
25205
|
-
const raw = await
|
|
25072
|
+
const raw = await fs21.readFile(resolveArchiveIndexPath(providerHome), "utf8");
|
|
25206
25073
|
const parsed = JSON.parse(raw);
|
|
25207
25074
|
return {
|
|
25208
25075
|
archives: Array.isArray(parsed.archives) ? parsed.archives : []
|
|
@@ -25216,8 +25083,8 @@ async function readArchiveIndex(providerHome) {
|
|
|
25216
25083
|
}
|
|
25217
25084
|
async function writeArchiveIndex(providerHome, index) {
|
|
25218
25085
|
const root = resolveArchiveRoot(providerHome);
|
|
25219
|
-
await
|
|
25220
|
-
await
|
|
25086
|
+
await fs21.mkdir(root, { recursive: true });
|
|
25087
|
+
await fs21.writeFile(
|
|
25221
25088
|
resolveArchiveIndexPath(providerHome),
|
|
25222
25089
|
`${JSON.stringify(index, null, 2)}
|
|
25223
25090
|
`,
|
|
@@ -25288,7 +25155,7 @@ var ProviderHostConfigService = class {
|
|
|
25288
25155
|
const fileName = this.assertHostFile(provider2, name);
|
|
25289
25156
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
25290
25157
|
try {
|
|
25291
|
-
const content = await
|
|
25158
|
+
const content = await fs21.readFile(filePath, "utf8");
|
|
25292
25159
|
return {
|
|
25293
25160
|
name: fileName,
|
|
25294
25161
|
path: filePath,
|
|
@@ -25311,8 +25178,8 @@ var ProviderHostConfigService = class {
|
|
|
25311
25178
|
const providerHome = this.providerHome(provider2);
|
|
25312
25179
|
const fileName = this.assertHostFile(provider2, name);
|
|
25313
25180
|
const filePath = resolveProviderHostFilePath(providerHome, fileName);
|
|
25314
|
-
await
|
|
25315
|
-
await
|
|
25181
|
+
await fs21.mkdir(path22.dirname(filePath), { recursive: true });
|
|
25182
|
+
await fs21.writeFile(filePath, input.content, "utf8");
|
|
25316
25183
|
return this.readFile(provider2, fileName);
|
|
25317
25184
|
}
|
|
25318
25185
|
async listArchives(provider2) {
|
|
@@ -25338,7 +25205,7 @@ var ProviderHostConfigService = class {
|
|
|
25338
25205
|
}
|
|
25339
25206
|
])
|
|
25340
25207
|
);
|
|
25341
|
-
await
|
|
25208
|
+
await fs21.mkdir(archivePath, { recursive: true });
|
|
25342
25209
|
for (const name of fileNames) {
|
|
25343
25210
|
const hostFile = await this.readFile(provider2, name);
|
|
25344
25211
|
files[name] = {
|
|
@@ -25346,7 +25213,7 @@ var ProviderHostConfigService = class {
|
|
|
25346
25213
|
exists: hostFile.exists
|
|
25347
25214
|
};
|
|
25348
25215
|
if (hostFile.exists) {
|
|
25349
|
-
await
|
|
25216
|
+
await fs21.writeFile(path22.join(archivePath, name), hostFile.content, "utf8");
|
|
25350
25217
|
}
|
|
25351
25218
|
}
|
|
25352
25219
|
const archive = {
|
|
@@ -25382,14 +25249,14 @@ var ProviderHostConfigService = class {
|
|
|
25382
25249
|
const fileNames = this.archiveFileNames(provider2);
|
|
25383
25250
|
const { archive } = await findArchiveOrThrow(providerHome, id);
|
|
25384
25251
|
const archivePath = resolveArchivePath(providerHome, archive.id);
|
|
25385
|
-
await
|
|
25252
|
+
await fs21.mkdir(providerHome, { recursive: true });
|
|
25386
25253
|
for (const name of fileNames) {
|
|
25387
25254
|
const hostPath = resolveProviderHostFilePath(providerHome, name);
|
|
25388
25255
|
if (archive.files[name]?.exists) {
|
|
25389
|
-
const content = await
|
|
25390
|
-
await
|
|
25256
|
+
const content = await fs21.readFile(path22.join(archivePath, name), "utf8");
|
|
25257
|
+
await fs21.writeFile(hostPath, content, "utf8");
|
|
25391
25258
|
} else {
|
|
25392
|
-
await
|
|
25259
|
+
await fs21.rm(hostPath, { force: true });
|
|
25393
25260
|
}
|
|
25394
25261
|
}
|
|
25395
25262
|
await runtime.stop();
|
|
@@ -25402,12 +25269,12 @@ var ProviderHostConfigService = class {
|
|
|
25402
25269
|
};
|
|
25403
25270
|
|
|
25404
25271
|
// src/shell/shell-session-service.ts
|
|
25405
|
-
import
|
|
25272
|
+
import fs23 from "fs/promises";
|
|
25406
25273
|
|
|
25407
25274
|
// src/shell/shell-prompt.ts
|
|
25408
|
-
import
|
|
25275
|
+
import fs22 from "fs/promises";
|
|
25409
25276
|
import os5 from "os";
|
|
25410
|
-
import
|
|
25277
|
+
import path23 from "path";
|
|
25411
25278
|
function basenameFromPath2(filePath) {
|
|
25412
25279
|
if (!filePath) {
|
|
25413
25280
|
return "";
|
|
@@ -25416,7 +25283,7 @@ function basenameFromPath2(filePath) {
|
|
|
25416
25283
|
if (!normalized) {
|
|
25417
25284
|
return "";
|
|
25418
25285
|
}
|
|
25419
|
-
return
|
|
25286
|
+
return path23.basename(normalized) || normalized;
|
|
25420
25287
|
}
|
|
25421
25288
|
function isInteractiveShellCommand(command) {
|
|
25422
25289
|
const normalized = (command ?? "").trim().toLowerCase();
|
|
@@ -25577,11 +25444,11 @@ function buildShellPromptInitScriptContents(command) {
|
|
|
25577
25444
|
async function ensureShellPromptInitScript(command) {
|
|
25578
25445
|
const normalized = command.trim().toLowerCase();
|
|
25579
25446
|
const extension = normalized === "zsh" ? "zsh" : "sh";
|
|
25580
|
-
const filePath =
|
|
25447
|
+
const filePath = path23.join(
|
|
25581
25448
|
os5.tmpdir(),
|
|
25582
25449
|
`remote-codex-shell-prompt.${extension}`
|
|
25583
25450
|
);
|
|
25584
|
-
await
|
|
25451
|
+
await fs22.writeFile(filePath, buildShellPromptInitScriptContents(command), "utf8");
|
|
25585
25452
|
return filePath;
|
|
25586
25453
|
}
|
|
25587
25454
|
async function buildShellPromptInitCommand(command, options = {}) {
|
|
@@ -25597,7 +25464,7 @@ clear
|
|
|
25597
25464
|
// src/shell/shell-session-service.ts
|
|
25598
25465
|
async function pathExists5(filePath) {
|
|
25599
25466
|
try {
|
|
25600
|
-
await
|
|
25467
|
+
await fs23.access(filePath);
|
|
25601
25468
|
return true;
|
|
25602
25469
|
} catch {
|
|
25603
25470
|
return false;
|
|
@@ -26255,8 +26122,8 @@ var builtinPlugins = [
|
|
|
26255
26122
|
];
|
|
26256
26123
|
|
|
26257
26124
|
// src/plugins/plugin-service.ts
|
|
26258
|
-
import
|
|
26259
|
-
import
|
|
26125
|
+
import fs24 from "fs/promises";
|
|
26126
|
+
import path24 from "path";
|
|
26260
26127
|
var MANAGED_CODEX_MCP_BEGIN = "# BEGIN remote-codex managed plugin MCP servers";
|
|
26261
26128
|
var MANAGED_CODEX_MCP_END = "# END remote-codex managed plugin MCP servers";
|
|
26262
26129
|
var REMOTE_CODEX_MOLECULE_MCP_TOOL_NAME = "remote_codex_render_molecule";
|
|
@@ -26268,7 +26135,7 @@ function normalizeManagedCommand(server, repoRoot) {
|
|
|
26268
26135
|
if (server.name === "remote_codex_plugins") {
|
|
26269
26136
|
return {
|
|
26270
26137
|
command: process.execPath,
|
|
26271
|
-
args: [
|
|
26138
|
+
args: [path24.join(repoRoot, "bin", "remote-codex-plugin-mcp.mjs")]
|
|
26272
26139
|
};
|
|
26273
26140
|
}
|
|
26274
26141
|
return {
|
|
@@ -26455,10 +26322,10 @@ var PluginService = class {
|
|
|
26455
26322
|
if (!input.codexHome) {
|
|
26456
26323
|
return;
|
|
26457
26324
|
}
|
|
26458
|
-
const configPath =
|
|
26325
|
+
const configPath = path24.join(input.codexHome, "config.toml");
|
|
26459
26326
|
let current = "";
|
|
26460
26327
|
try {
|
|
26461
|
-
current = await
|
|
26328
|
+
current = await fs24.readFile(configPath, "utf8");
|
|
26462
26329
|
} catch (error) {
|
|
26463
26330
|
if (error.code !== "ENOENT") {
|
|
26464
26331
|
throw error;
|
|
@@ -26473,8 +26340,8 @@ var PluginService = class {
|
|
|
26473
26340
|
if (next === current) {
|
|
26474
26341
|
return;
|
|
26475
26342
|
}
|
|
26476
|
-
await
|
|
26477
|
-
await
|
|
26343
|
+
await fs24.mkdir(path24.dirname(configPath), { recursive: true });
|
|
26344
|
+
await fs24.writeFile(configPath, next, "utf8");
|
|
26478
26345
|
}
|
|
26479
26346
|
async importPlugin(input) {
|
|
26480
26347
|
const manifestInput = input.manifest ?? (input.manifestUrl ? await this.fetchManifestUrl(input.manifestUrl) : this.parseManifestJson(input.manifestJson));
|
|
@@ -26713,8 +26580,8 @@ var PluginSettingsStore = class {
|
|
|
26713
26580
|
};
|
|
26714
26581
|
|
|
26715
26582
|
// src/worker-bootstrap.ts
|
|
26716
|
-
import
|
|
26717
|
-
import
|
|
26583
|
+
import fs25 from "fs/promises";
|
|
26584
|
+
import path25 from "path";
|
|
26718
26585
|
function trimTrailingSlash(value) {
|
|
26719
26586
|
return value.replace(/\/+$/, "");
|
|
26720
26587
|
}
|
|
@@ -26725,9 +26592,9 @@ function tomlString(value) {
|
|
|
26725
26592
|
return JSON.stringify(value);
|
|
26726
26593
|
}
|
|
26727
26594
|
async function writePrivateFile(filePath, content) {
|
|
26728
|
-
await
|
|
26729
|
-
await
|
|
26730
|
-
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);
|
|
26731
26598
|
}
|
|
26732
26599
|
async function configureWorkerProviderGateway(config) {
|
|
26733
26600
|
if (config.runtimeRole !== "worker" || !config.llmGatewayBaseUrl || !config.llmGatewayToken) {
|
|
@@ -26741,7 +26608,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26741
26608
|
process.env.ANTHROPIC_BASE_URL = anthropicBaseUrl;
|
|
26742
26609
|
if (config.agentProviders.codex.enabled) {
|
|
26743
26610
|
await writePrivateFile(
|
|
26744
|
-
|
|
26611
|
+
path25.join(config.agentProviders.codex.home, "config.toml"),
|
|
26745
26612
|
[
|
|
26746
26613
|
'model_provider = "sub2api"',
|
|
26747
26614
|
'forced_login_method = "api"',
|
|
@@ -26757,7 +26624,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26757
26624
|
].join("\n")
|
|
26758
26625
|
);
|
|
26759
26626
|
await writePrivateFile(
|
|
26760
|
-
|
|
26627
|
+
path25.join(config.agentProviders.codex.home, "auth.json"),
|
|
26761
26628
|
`${JSON.stringify(
|
|
26762
26629
|
{
|
|
26763
26630
|
OPENAI_API_KEY: config.llmGatewayToken
|
|
@@ -26770,7 +26637,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26770
26637
|
}
|
|
26771
26638
|
if (config.agentProviders.claude.enabled) {
|
|
26772
26639
|
await writePrivateFile(
|
|
26773
|
-
|
|
26640
|
+
path25.join(config.agentProviders.claude.home, "settings.json"),
|
|
26774
26641
|
`${JSON.stringify(
|
|
26775
26642
|
{
|
|
26776
26643
|
env: {
|
|
@@ -26786,7 +26653,7 @@ async function configureWorkerProviderGateway(config) {
|
|
|
26786
26653
|
}
|
|
26787
26654
|
if (config.agentProviders.opencode.enabled) {
|
|
26788
26655
|
await writePrivateFile(
|
|
26789
|
-
|
|
26656
|
+
path25.join(config.agentProviders.opencode.home, "opencode.json"),
|
|
26790
26657
|
`${JSON.stringify(
|
|
26791
26658
|
{
|
|
26792
26659
|
provider: {
|
|
@@ -26833,27 +26700,27 @@ var BackendPluginHost = class {
|
|
|
26833
26700
|
};
|
|
26834
26701
|
|
|
26835
26702
|
// src/shell/pty-shell-backend.ts
|
|
26836
|
-
import
|
|
26703
|
+
import path26 from "path";
|
|
26837
26704
|
import { spawn as spawn5 } from "@homebridge/node-pty-prebuilt-multiarch";
|
|
26838
26705
|
|
|
26839
26706
|
// src/shell/default-shell.ts
|
|
26840
|
-
import
|
|
26707
|
+
import fs26 from "fs";
|
|
26841
26708
|
var POSIX_SHELL_CANDIDATES = ["/bin/bash", "/usr/bin/bash", "/bin/sh"];
|
|
26842
26709
|
function resolveDefaultShell(env = process.env) {
|
|
26843
26710
|
if (process.platform === "win32") {
|
|
26844
26711
|
return env.COMSPEC ?? "cmd.exe";
|
|
26845
26712
|
}
|
|
26846
|
-
if (env.SHELL &&
|
|
26713
|
+
if (env.SHELL && fs26.existsSync(env.SHELL)) {
|
|
26847
26714
|
return env.SHELL;
|
|
26848
26715
|
}
|
|
26849
|
-
return POSIX_SHELL_CANDIDATES.find((candidate) =>
|
|
26716
|
+
return POSIX_SHELL_CANDIDATES.find((candidate) => fs26.existsSync(candidate)) ?? "/bin/sh";
|
|
26850
26717
|
}
|
|
26851
26718
|
|
|
26852
26719
|
// src/shell/pty-shell-backend.ts
|
|
26853
26720
|
var MAX_SCROLLBACK_BYTES = 512 * 1024;
|
|
26854
26721
|
var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g");
|
|
26855
26722
|
function shellArgs(shell) {
|
|
26856
|
-
const shellName =
|
|
26723
|
+
const shellName = path26.basename(shell).toLowerCase();
|
|
26857
26724
|
if (process.platform === "win32") {
|
|
26858
26725
|
return [];
|
|
26859
26726
|
}
|
|
@@ -26875,7 +26742,7 @@ function lastVisibleLine(snapshot) {
|
|
|
26875
26742
|
}
|
|
26876
26743
|
function inferRuntime(session) {
|
|
26877
26744
|
const promptLine = lastVisibleLine(session.scrollback);
|
|
26878
|
-
const shell =
|
|
26745
|
+
const shell = path26.basename(session.shell);
|
|
26879
26746
|
const isCommandRunning = session.exitCode !== null ? false : !/[$#>]\s*$/.test(promptLine.trimEnd());
|
|
26880
26747
|
return {
|
|
26881
26748
|
panePid: session.pty.pid,
|
|
@@ -27021,8 +26888,8 @@ var PtyShellBackend = class {
|
|
|
27021
26888
|
};
|
|
27022
26889
|
|
|
27023
26890
|
// src/shell/tmux-manager.ts
|
|
27024
|
-
import
|
|
27025
|
-
import
|
|
26891
|
+
import fs27 from "fs";
|
|
26892
|
+
import path27 from "path";
|
|
27026
26893
|
import { spawn as spawnChild } from "child_process";
|
|
27027
26894
|
async function defaultExecCommand(command, args) {
|
|
27028
26895
|
return await new Promise((resolve, reject) => {
|
|
@@ -27049,17 +26916,17 @@ async function defaultExecCommand(command, args) {
|
|
|
27049
26916
|
});
|
|
27050
26917
|
}
|
|
27051
26918
|
function resolveExecutablePath(command) {
|
|
27052
|
-
if (command.includes(
|
|
26919
|
+
if (command.includes(path27.sep)) {
|
|
27053
26920
|
return command;
|
|
27054
26921
|
}
|
|
27055
26922
|
const searchPath = process.env.PATH ?? "";
|
|
27056
|
-
for (const entry of searchPath.split(
|
|
26923
|
+
for (const entry of searchPath.split(path27.delimiter)) {
|
|
27057
26924
|
const trimmed = entry.trim();
|
|
27058
26925
|
if (!trimmed) {
|
|
27059
26926
|
continue;
|
|
27060
26927
|
}
|
|
27061
|
-
const candidate =
|
|
27062
|
-
if (
|
|
26928
|
+
const candidate = path27.join(trimmed, command);
|
|
26929
|
+
if (fs27.existsSync(candidate)) {
|
|
27063
26930
|
return candidate;
|
|
27064
26931
|
}
|
|
27065
26932
|
}
|
|
@@ -27820,22 +27687,22 @@ function payloadItems(payload, fields) {
|
|
|
27820
27687
|
const record = recordFrom(payload);
|
|
27821
27688
|
return arrayField(record, fields) ?? (record ? [record] : []);
|
|
27822
27689
|
}
|
|
27823
|
-
function artifactPreviewKind(type, format,
|
|
27690
|
+
function artifactPreviewKind(type, format, path29) {
|
|
27824
27691
|
const candidates = [
|
|
27825
27692
|
type,
|
|
27826
27693
|
format,
|
|
27827
|
-
|
|
27694
|
+
path29?.split(".").pop() ?? null
|
|
27828
27695
|
].map((value) => value?.trim().toLowerCase()).filter(Boolean);
|
|
27829
27696
|
return candidates.some((value) => MOLECULE_ARTIFACT_TYPES.has(value)) ? "molecule" : "file";
|
|
27830
27697
|
}
|
|
27831
27698
|
function normalizeArtifactRef(value) {
|
|
27832
27699
|
const record = recordFrom(value);
|
|
27833
|
-
const
|
|
27700
|
+
const path29 = stringField3(record, ["path", "filePath", "file_path", "filename", "fileName", "name"]);
|
|
27834
27701
|
const type = stringField3(record, ["type", "artifactType", "artifact_type", "format", "extension"]);
|
|
27835
|
-
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ??
|
|
27702
|
+
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ?? path29 ?? "artifact";
|
|
27836
27703
|
return {
|
|
27837
27704
|
title,
|
|
27838
|
-
path:
|
|
27705
|
+
path: path29,
|
|
27839
27706
|
type,
|
|
27840
27707
|
downloadUrl: stringField3(record, ["downloadUrl", "download_url", "url", "href"])
|
|
27841
27708
|
};
|
|
@@ -27869,21 +27736,21 @@ function normalizeArtifact(module, runId, value) {
|
|
|
27869
27736
|
if (!record) {
|
|
27870
27737
|
return null;
|
|
27871
27738
|
}
|
|
27872
|
-
const
|
|
27739
|
+
const path29 = stringField3(record, ["path", "filePath", "file_path", "filename", "fileName", "name"]);
|
|
27873
27740
|
const type = stringField3(record, ["type", "artifactType", "artifact_type"]);
|
|
27874
27741
|
const format = stringField3(record, ["format", "fileFormat", "file_format", "extension"]) ?? type;
|
|
27875
|
-
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ??
|
|
27742
|
+
const title = stringField3(record, ["title", "label", "name", "filename", "fileName"]) ?? path29 ?? `${module} artifact`;
|
|
27876
27743
|
return {
|
|
27877
27744
|
module,
|
|
27878
27745
|
runId,
|
|
27879
27746
|
title,
|
|
27880
|
-
path:
|
|
27747
|
+
path: path29,
|
|
27881
27748
|
type,
|
|
27882
27749
|
format,
|
|
27883
27750
|
mimeType: stringField3(record, ["mimeType", "mime_type", "contentType", "content_type"]),
|
|
27884
27751
|
sizeBytes: numberField2(record, ["sizeBytes", "size_bytes", "bytes"]),
|
|
27885
27752
|
downloadUrl: stringField3(record, ["downloadUrl", "download_url", "url", "href"]),
|
|
27886
|
-
previewKind: artifactPreviewKind(type, format,
|
|
27753
|
+
previewKind: artifactPreviewKind(type, format, path29)
|
|
27887
27754
|
};
|
|
27888
27755
|
}
|
|
27889
27756
|
function parseTomlScalar(raw) {
|
|
@@ -28113,9 +27980,9 @@ var WorkerHarnessClient = class {
|
|
|
28113
27980
|
}
|
|
28114
27981
|
return { baseUrl, apiKey };
|
|
28115
27982
|
}
|
|
28116
|
-
async fetchText(
|
|
27983
|
+
async fetchText(path29) {
|
|
28117
27984
|
const config = this.requireHarnessConfig();
|
|
28118
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
27985
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28119
27986
|
headers: {
|
|
28120
27987
|
"x-api-key": config.apiKey
|
|
28121
27988
|
}
|
|
@@ -28126,11 +27993,11 @@ var WorkerHarnessClient = class {
|
|
|
28126
27993
|
}
|
|
28127
27994
|
return { text: text2 };
|
|
28128
27995
|
}
|
|
28129
|
-
async fetchPayload(
|
|
27996
|
+
async fetchPayload(path29, init = {}) {
|
|
28130
27997
|
const config = this.requireHarnessConfig();
|
|
28131
27998
|
const headers = new Headers(init.headers);
|
|
28132
27999
|
headers.set("x-api-key", config.apiKey);
|
|
28133
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
28000
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28134
28001
|
...init,
|
|
28135
28002
|
headers
|
|
28136
28003
|
});
|
|
@@ -28144,9 +28011,9 @@ var WorkerHarnessClient = class {
|
|
|
28144
28011
|
return { text: text2 };
|
|
28145
28012
|
}
|
|
28146
28013
|
}
|
|
28147
|
-
async fetchBinary(
|
|
28014
|
+
async fetchBinary(path29) {
|
|
28148
28015
|
const config = this.requireHarnessConfig();
|
|
28149
|
-
const response = await this.fetchImpl(`${config.baseUrl}${
|
|
28016
|
+
const response = await this.fetchImpl(`${config.baseUrl}${path29}`, {
|
|
28150
28017
|
headers: {
|
|
28151
28018
|
"x-api-key": config.apiKey
|
|
28152
28019
|
}
|
|
@@ -28990,16 +28857,16 @@ var HttpError = class extends Error {
|
|
|
28990
28857
|
};
|
|
28991
28858
|
function findRepoRoot(start = process.cwd()) {
|
|
28992
28859
|
if (process.env.REMOTE_CODEX_REPO_ROOT) {
|
|
28993
|
-
return
|
|
28860
|
+
return path28.resolve(process.env.REMOTE_CODEX_REPO_ROOT);
|
|
28994
28861
|
}
|
|
28995
|
-
let current =
|
|
28996
|
-
while (current !==
|
|
28997
|
-
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"))) {
|
|
28998
28865
|
return current;
|
|
28999
28866
|
}
|
|
29000
|
-
current =
|
|
28867
|
+
current = path28.dirname(current);
|
|
29001
28868
|
}
|
|
29002
|
-
return
|
|
28869
|
+
return path28.resolve(process.cwd());
|
|
29003
28870
|
}
|
|
29004
28871
|
function createServiceLifecycle() {
|
|
29005
28872
|
return {
|
|
@@ -29011,8 +28878,8 @@ function createServiceLifecycle() {
|
|
|
29011
28878
|
});
|
|
29012
28879
|
}
|
|
29013
28880
|
const repoRoot = findRepoRoot();
|
|
29014
|
-
const restartScript =
|
|
29015
|
-
if (!
|
|
28881
|
+
const restartScript = path28.join(repoRoot, "scripts", "service-restart.mjs");
|
|
28882
|
+
if (!fs28.existsSync(restartScript) || !fs28.existsSync(path28.join(repoRoot, "pnpm-workspace.yaml"))) {
|
|
29016
28883
|
throw new HttpError(503, {
|
|
29017
28884
|
code: "service_unavailable",
|
|
29018
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."
|
|
@@ -29055,7 +28922,7 @@ function buildApp(options = {}) {
|
|
|
29055
28922
|
const shellService = options.shellService ?? new ShellSessionService(
|
|
29056
28923
|
database.db,
|
|
29057
28924
|
eventBus,
|
|
29058
|
-
createTerminalShellBackend(options.env)
|
|
28925
|
+
options.shellBackend ?? createTerminalShellBackend(options.env)
|
|
29059
28926
|
);
|
|
29060
28927
|
const providerHostConfigService = new ProviderHostConfigService(
|
|
29061
28928
|
agentRuntimes,
|