appostle-installer 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appostle-installer.js +25 -14
- package/dist/appostle-installer.js.map +2 -2
- package/dist/appostle.js +2179 -586
- package/dist/appostle.js.map +4 -4
- package/dist/worker.js +2549 -1127
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/appostle.js
CHANGED
|
@@ -454,18 +454,18 @@ function resolveNodePtyPackageRoot() {
|
|
|
454
454
|
return null;
|
|
455
455
|
}
|
|
456
456
|
}
|
|
457
|
-
function ensureExecutableBit(
|
|
458
|
-
if (!existsSync2(
|
|
457
|
+
function ensureExecutableBit(path26) {
|
|
458
|
+
if (!existsSync2(path26)) {
|
|
459
459
|
return;
|
|
460
460
|
}
|
|
461
|
-
const stat5 = statSync(
|
|
461
|
+
const stat5 = statSync(path26);
|
|
462
462
|
if (!stat5.isFile()) {
|
|
463
463
|
return;
|
|
464
464
|
}
|
|
465
465
|
if ((stat5.mode & 73) === 73) {
|
|
466
466
|
return;
|
|
467
467
|
}
|
|
468
|
-
chmodSync(
|
|
468
|
+
chmodSync(path26, stat5.mode | 73);
|
|
469
469
|
}
|
|
470
470
|
function ensureNodePtySpawnHelperExecutableForCurrentPlatform(options = {}) {
|
|
471
471
|
const platform2 = options.platform ?? process.platform;
|
|
@@ -547,11 +547,11 @@ function parseGitRevParsePath(stdout) {
|
|
|
547
547
|
if (lines.length !== 1) {
|
|
548
548
|
return null;
|
|
549
549
|
}
|
|
550
|
-
const
|
|
551
|
-
if (!
|
|
550
|
+
const path26 = lines[0]?.trim() ?? "";
|
|
551
|
+
if (!path26 || path26.startsWith("--")) {
|
|
552
552
|
return null;
|
|
553
553
|
}
|
|
554
|
-
return
|
|
554
|
+
return path26;
|
|
555
555
|
}
|
|
556
556
|
function resolveGitRevParsePath(cwd, stdout) {
|
|
557
557
|
const parsed = parseGitRevParsePath(stdout);
|
|
@@ -1324,9 +1324,9 @@ async function deleteAppostleWorktree({
|
|
|
1324
1324
|
}
|
|
1325
1325
|
}
|
|
1326
1326
|
}
|
|
1327
|
-
async function pathExists(
|
|
1327
|
+
async function pathExists(path26) {
|
|
1328
1328
|
try {
|
|
1329
|
-
await stat(
|
|
1329
|
+
await stat(path26);
|
|
1330
1330
|
return true;
|
|
1331
1331
|
} catch (error) {
|
|
1332
1332
|
if (error.code === "ENOENT") {
|
|
@@ -1335,8 +1335,8 @@ async function pathExists(path24) {
|
|
|
1335
1335
|
throw error;
|
|
1336
1336
|
}
|
|
1337
1337
|
}
|
|
1338
|
-
async function removeDirectoryWithRetries(
|
|
1339
|
-
if (!await pathExists(
|
|
1338
|
+
async function removeDirectoryWithRetries(path26) {
|
|
1339
|
+
if (!await pathExists(path26)) {
|
|
1340
1340
|
return;
|
|
1341
1341
|
}
|
|
1342
1342
|
const delaysMs = [0, 100, 300, 700, 1500];
|
|
@@ -1346,17 +1346,17 @@ async function removeDirectoryWithRetries(path24) {
|
|
|
1346
1346
|
await new Promise((resolve12) => setTimeout(resolve12, delay));
|
|
1347
1347
|
}
|
|
1348
1348
|
try {
|
|
1349
|
-
await rm(
|
|
1350
|
-
if (!await pathExists(
|
|
1349
|
+
await rm(path26, { recursive: true, force: true });
|
|
1350
|
+
if (!await pathExists(path26)) {
|
|
1351
1351
|
return;
|
|
1352
1352
|
}
|
|
1353
|
-
lastError = new Error(`Directory still present after rm: ${
|
|
1353
|
+
lastError = new Error(`Directory still present after rm: ${path26}`);
|
|
1354
1354
|
} catch (error) {
|
|
1355
1355
|
lastError = error;
|
|
1356
1356
|
}
|
|
1357
1357
|
}
|
|
1358
|
-
if (await pathExists(
|
|
1359
|
-
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${
|
|
1358
|
+
if (await pathExists(path26)) {
|
|
1359
|
+
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${path26}`);
|
|
1360
1360
|
}
|
|
1361
1361
|
}
|
|
1362
1362
|
var createWorktree = async ({
|
|
@@ -1664,7 +1664,8 @@ function toAgentPayload(agent, options) {
|
|
|
1664
1664
|
pendingPermissions: sanitizePendingPermissions(agent.pendingPermissions),
|
|
1665
1665
|
persistence: sanitizePersistenceHandle(agent.persistence),
|
|
1666
1666
|
title: options?.title ?? null,
|
|
1667
|
-
labels: agent.labels
|
|
1667
|
+
labels: agent.labels,
|
|
1668
|
+
internal: agent.internal
|
|
1668
1669
|
};
|
|
1669
1670
|
const usage = sanitizeUsage(agent.lastUsage);
|
|
1670
1671
|
if (usage !== void 0) {
|
|
@@ -1734,7 +1735,8 @@ function buildStoredAgentPayload(record, providerRegistry, logger) {
|
|
|
1734
1735
|
attentionReason: record.attentionReason ?? null,
|
|
1735
1736
|
attentionTimestamp: record.attentionTimestamp ?? null,
|
|
1736
1737
|
archivedAt: record.archivedAt ?? null,
|
|
1737
|
-
labels: record.labels
|
|
1738
|
+
labels: record.labels,
|
|
1739
|
+
internal: record.internal
|
|
1738
1740
|
};
|
|
1739
1741
|
}
|
|
1740
1742
|
function resolveStoredAgentPayloadUpdatedAt(record) {
|
|
@@ -1783,34 +1785,42 @@ function cloneAvailableModes(modes) {
|
|
|
1783
1785
|
function normalizeFeatures(features) {
|
|
1784
1786
|
return Array.isArray(features) ? features.map((feature) => ({ ...feature })) : [];
|
|
1785
1787
|
}
|
|
1786
|
-
|
|
1788
|
+
var SANITIZE_MAX_DEPTH = 32;
|
|
1789
|
+
function sanitizeOptionalJson(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
1787
1790
|
if (value === void 0) {
|
|
1788
1791
|
return void 0;
|
|
1789
1792
|
}
|
|
1790
1793
|
if (value === null) {
|
|
1791
1794
|
return null;
|
|
1792
1795
|
}
|
|
1793
|
-
if (
|
|
1794
|
-
|
|
1795
|
-
return sanitized;
|
|
1796
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1797
|
+
return value;
|
|
1796
1798
|
}
|
|
1797
1799
|
if (value instanceof Date) {
|
|
1798
1800
|
return value.toISOString();
|
|
1799
1801
|
}
|
|
1800
|
-
if (typeof value
|
|
1801
|
-
|
|
1802
|
-
for (const [key, val] of Object.entries(value)) {
|
|
1803
|
-
const sanitized = sanitizeOptionalJson(val);
|
|
1804
|
-
if (sanitized !== void 0) {
|
|
1805
|
-
result[key] = sanitized;
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
return Object.keys(result).length ? result : void 0;
|
|
1802
|
+
if (typeof value !== "object") {
|
|
1803
|
+
return void 0;
|
|
1809
1804
|
}
|
|
1810
|
-
if (
|
|
1811
|
-
return
|
|
1805
|
+
if (depth >= SANITIZE_MAX_DEPTH) {
|
|
1806
|
+
return void 0;
|
|
1812
1807
|
}
|
|
1813
|
-
|
|
1808
|
+
if (seen.has(value)) {
|
|
1809
|
+
return void 0;
|
|
1810
|
+
}
|
|
1811
|
+
seen.add(value);
|
|
1812
|
+
if (Array.isArray(value)) {
|
|
1813
|
+
const sanitized = value.map((item) => sanitizeOptionalJson(item, seen, depth + 1)).filter((item) => item !== void 0);
|
|
1814
|
+
return sanitized;
|
|
1815
|
+
}
|
|
1816
|
+
const result = {};
|
|
1817
|
+
for (const [key, val] of Object.entries(value)) {
|
|
1818
|
+
const sanitized = sanitizeOptionalJson(val, seen, depth + 1);
|
|
1819
|
+
if (sanitized !== void 0) {
|
|
1820
|
+
result[key] = sanitized;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
return Object.keys(result).length ? result : void 0;
|
|
1814
1824
|
}
|
|
1815
1825
|
function isJsonObject(value) {
|
|
1816
1826
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1871,6 +1881,34 @@ function sanitizeUsage(value) {
|
|
|
1871
1881
|
} else if (contextWindowUsedTokens !== void 0 && contextWindowUsedTokens !== null) {
|
|
1872
1882
|
return void 0;
|
|
1873
1883
|
}
|
|
1884
|
+
const rateLimits = sanitized.rateLimits;
|
|
1885
|
+
if (Array.isArray(rateLimits)) {
|
|
1886
|
+
const VALID_RATE_LIMIT_TYPES = /* @__PURE__ */ new Set([
|
|
1887
|
+
"five_hour",
|
|
1888
|
+
"seven_day",
|
|
1889
|
+
"seven_day_opus",
|
|
1890
|
+
"seven_day_sonnet",
|
|
1891
|
+
"overage"
|
|
1892
|
+
]);
|
|
1893
|
+
const sanitizedRateLimits = [];
|
|
1894
|
+
for (const entry of rateLimits) {
|
|
1895
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1896
|
+
const { rateLimitType, utilization, resetsAt } = entry;
|
|
1897
|
+
if (typeof rateLimitType !== "string" || !VALID_RATE_LIMIT_TYPES.has(rateLimitType)) continue;
|
|
1898
|
+
if (typeof utilization !== "number" || !Number.isFinite(utilization)) continue;
|
|
1899
|
+
const limit = {
|
|
1900
|
+
rateLimitType,
|
|
1901
|
+
utilization
|
|
1902
|
+
};
|
|
1903
|
+
if (typeof resetsAt === "number" && Number.isFinite(resetsAt)) {
|
|
1904
|
+
limit.resetsAt = resetsAt;
|
|
1905
|
+
}
|
|
1906
|
+
sanitizedRateLimits.push(limit);
|
|
1907
|
+
}
|
|
1908
|
+
if (sanitizedRateLimits.length > 0) {
|
|
1909
|
+
result.rateLimits = sanitizedRateLimits;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1874
1912
|
return Object.keys(result).length ? result : void 0;
|
|
1875
1913
|
}
|
|
1876
1914
|
function sanitizeRuntimeInfo(runtimeInfo) {
|
|
@@ -2647,6 +2685,13 @@ var QuestRecordSchema = z8.object({
|
|
|
2647
2685
|
cwd: z8.string(),
|
|
2648
2686
|
/** User's task prompt — fed to every card unless overridden per-card. */
|
|
2649
2687
|
prompt: z8.string(),
|
|
2688
|
+
/**
|
|
2689
|
+
* Short auto-generated label for the quest, derived from the user prompt by a
|
|
2690
|
+
* small internal metadata agent (mirrors how single-agent sessions get a
|
|
2691
|
+
* title). Empty string until generation finishes (or if it fails). Optional +
|
|
2692
|
+
* default so old persisted records remain valid.
|
|
2693
|
+
*/
|
|
2694
|
+
name: z8.string().optional().default(""),
|
|
2650
2695
|
defaultProvider: AgentProviderSchema,
|
|
2651
2696
|
defaultModel: z8.string().nullable(),
|
|
2652
2697
|
termination: QuestTerminationSchema,
|
|
@@ -2717,6 +2762,8 @@ var QuestListItemSchema = z9.object({
|
|
|
2717
2762
|
status: QuestStatusSchema,
|
|
2718
2763
|
cwd: z9.string(),
|
|
2719
2764
|
prompt: z9.string(),
|
|
2765
|
+
/** Auto-generated short label. Optional for backward compat with old daemons. */
|
|
2766
|
+
name: z9.string().optional().default(""),
|
|
2720
2767
|
createdAt: z9.string(),
|
|
2721
2768
|
updatedAt: z9.string()
|
|
2722
2769
|
});
|
|
@@ -3068,13 +3115,25 @@ var AgentCapabilityFlagsSchema = z10.object({
|
|
|
3068
3115
|
supportsReasoningStream: z10.boolean(),
|
|
3069
3116
|
supportsToolInvocations: z10.boolean()
|
|
3070
3117
|
});
|
|
3118
|
+
var AgentRateLimitSchema = z10.object({
|
|
3119
|
+
rateLimitType: z10.enum([
|
|
3120
|
+
"five_hour",
|
|
3121
|
+
"seven_day",
|
|
3122
|
+
"seven_day_opus",
|
|
3123
|
+
"seven_day_sonnet",
|
|
3124
|
+
"overage"
|
|
3125
|
+
]),
|
|
3126
|
+
utilization: z10.number(),
|
|
3127
|
+
resetsAt: z10.number().optional()
|
|
3128
|
+
});
|
|
3071
3129
|
var AgentUsageSchema = z10.object({
|
|
3072
3130
|
inputTokens: z10.number().optional(),
|
|
3073
3131
|
cachedInputTokens: z10.number().optional(),
|
|
3074
3132
|
outputTokens: z10.number().optional(),
|
|
3075
3133
|
totalCostUsd: z10.number().optional(),
|
|
3076
3134
|
contextWindowMaxTokens: z10.number().optional(),
|
|
3077
|
-
contextWindowUsedTokens: z10.number().optional()
|
|
3135
|
+
contextWindowUsedTokens: z10.number().optional(),
|
|
3136
|
+
rateLimits: z10.array(AgentRateLimitSchema).optional()
|
|
3078
3137
|
});
|
|
3079
3138
|
var McpStdioServerConfigSchema = z10.object({
|
|
3080
3139
|
type: z10.literal("stdio"),
|
|
@@ -3314,7 +3373,46 @@ var AgentTimelineItemPayloadSchema = z10.union([
|
|
|
3314
3373
|
z10.object({
|
|
3315
3374
|
type: z10.literal("user_message"),
|
|
3316
3375
|
text: z10.string(),
|
|
3317
|
-
messageId: z10.string().optional()
|
|
3376
|
+
messageId: z10.string().optional(),
|
|
3377
|
+
// Bytes-stripped attachment metadata so a reload re-renders the chip
|
|
3378
|
+
// without round-tripping image/file bytes through the timeline. Both
|
|
3379
|
+
// optional → backward-compatible with old daemons (silently strip) and
|
|
3380
|
+
// old clients (silently ignore). Inlined (not factored to a const)
|
|
3381
|
+
// because they're only used in this one place and forward-declaring
|
|
3382
|
+
// them would force a top-level reorder.
|
|
3383
|
+
images: z10.array(
|
|
3384
|
+
z10.object({
|
|
3385
|
+
id: z10.string(),
|
|
3386
|
+
mimeType: z10.string(),
|
|
3387
|
+
fileName: z10.string().optional(),
|
|
3388
|
+
byteSize: z10.number().int().nonnegative().optional(),
|
|
3389
|
+
// Daemon-side pointer for fetch-by-id. Optional → old daemons
|
|
3390
|
+
// (which only emit local-cache-friendly metadata) keep working;
|
|
3391
|
+
// old clients see the field and ignore it.
|
|
3392
|
+
daemonRef: z10.object({
|
|
3393
|
+
kind: z10.enum(["image", "file"]),
|
|
3394
|
+
attachmentId: z10.string(),
|
|
3395
|
+
relativePath: z10.string()
|
|
3396
|
+
}).optional()
|
|
3397
|
+
})
|
|
3398
|
+
).optional(),
|
|
3399
|
+
files: z10.array(
|
|
3400
|
+
z10.object({
|
|
3401
|
+
id: z10.string(),
|
|
3402
|
+
fileName: z10.string(),
|
|
3403
|
+
mimeType: z10.string(),
|
|
3404
|
+
size: z10.number().int().nonnegative(),
|
|
3405
|
+
daemonRef: z10.object({
|
|
3406
|
+
kind: z10.enum(["image", "file"]),
|
|
3407
|
+
attachmentId: z10.string(),
|
|
3408
|
+
relativePath: z10.string()
|
|
3409
|
+
}).optional()
|
|
3410
|
+
})
|
|
3411
|
+
).optional(),
|
|
3412
|
+
// Marker for daemon-synthesized text (e.g. "Sent 1 image" when the
|
|
3413
|
+
// user typed nothing). The chat bubble hides the text when this is
|
|
3414
|
+
// true; the LLM still sees it for cache_control compliance.
|
|
3415
|
+
textIsSynthesized: z10.boolean().optional()
|
|
3318
3416
|
}),
|
|
3319
3417
|
z10.object({
|
|
3320
3418
|
type: z10.literal("assistant_message"),
|
|
@@ -3460,7 +3558,15 @@ var AgentSnapshotPayloadSchema = z10.object({
|
|
|
3460
3558
|
archivedAt: z10.string().nullable().optional(),
|
|
3461
3559
|
// Fork lineage — optional for backward compat with pre-fork agents.
|
|
3462
3560
|
parentAgentId: z10.string().optional(),
|
|
3463
|
-
forkedFromMessageUuid: z10.string().optional()
|
|
3561
|
+
forkedFromMessageUuid: z10.string().optional(),
|
|
3562
|
+
/**
|
|
3563
|
+
* True when the agent is a system-spawned helper (orchestrator queens,
|
|
3564
|
+
* handoff workers). Optional for backward compat — older clients ignore
|
|
3565
|
+
* the field. Newer clients filter these out of workspace tab lists / agent
|
|
3566
|
+
* lists at display time. The agent itself is a real, full-featured session
|
|
3567
|
+
* in every other respect; `internal` is a UI visibility hint, nothing more.
|
|
3568
|
+
*/
|
|
3569
|
+
internal: z10.boolean().optional()
|
|
3464
3570
|
});
|
|
3465
3571
|
var VoiceAudioChunkMessageSchema = z10.object({
|
|
3466
3572
|
type: z10.literal("voice_audio_chunk"),
|
|
@@ -3586,8 +3692,34 @@ var AgentAttachmentsSchema = z10.unknown().transform(normalizeAgentAttachments).
|
|
|
3586
3692
|
var ImageAttachmentSchema = z10.object({
|
|
3587
3693
|
data: z10.string(),
|
|
3588
3694
|
// base64 encoded image
|
|
3589
|
-
mimeType: z10.string()
|
|
3695
|
+
mimeType: z10.string(),
|
|
3590
3696
|
// e.g., "image/jpeg", "image/png"
|
|
3697
|
+
// Optional client-side identity. When provided, the daemon echoes these
|
|
3698
|
+
// (without bytes) on the `user_message` timeline event so a reload can
|
|
3699
|
+
// re-render the chip. All optional → backward-compatible: old clients
|
|
3700
|
+
// omit them, old daemons strip them.
|
|
3701
|
+
id: z10.string().optional(),
|
|
3702
|
+
fileName: z10.string().optional(),
|
|
3703
|
+
byteSize: z10.number().int().nonnegative().optional()
|
|
3704
|
+
});
|
|
3705
|
+
var FileAttachmentSchema = z10.object({
|
|
3706
|
+
fileName: z10.string().min(1).max(255),
|
|
3707
|
+
mimeType: z10.string().min(1).max(255),
|
|
3708
|
+
size: z10.number().int().nonnegative(),
|
|
3709
|
+
data: z10.string()
|
|
3710
|
+
// base64-encoded bytes
|
|
3711
|
+
});
|
|
3712
|
+
var SessionUploadSchema = z10.object({
|
|
3713
|
+
id: z10.string(),
|
|
3714
|
+
fileName: z10.string(),
|
|
3715
|
+
mimeType: z10.string(),
|
|
3716
|
+
size: z10.number().int().nonnegative(),
|
|
3717
|
+
createdAt: z10.string(),
|
|
3718
|
+
// ISO-8601 UTC timestamp
|
|
3719
|
+
messageId: z10.string().nullable(),
|
|
3720
|
+
// Path relative to the agent's workspace root, e.g.
|
|
3721
|
+
// ".appostle/uploaded_files/<id>__report.sql". Always uses POSIX separators.
|
|
3722
|
+
relativePath: z10.string()
|
|
3591
3723
|
});
|
|
3592
3724
|
var SendAgentMessageSchema = z10.object({
|
|
3593
3725
|
type: z10.literal("send_agent_message"),
|
|
@@ -3596,6 +3728,7 @@ var SendAgentMessageSchema = z10.object({
|
|
|
3596
3728
|
messageId: z10.string().optional(),
|
|
3597
3729
|
// Client-provided ID for deduplication
|
|
3598
3730
|
images: z10.array(ImageAttachmentSchema).optional(),
|
|
3731
|
+
files: z10.array(FileAttachmentSchema).optional(),
|
|
3599
3732
|
attachments: AgentAttachmentsSchema
|
|
3600
3733
|
});
|
|
3601
3734
|
var FetchAgentsRequestMessageSchema = z10.object({
|
|
@@ -3660,6 +3793,7 @@ var SendAgentMessageRequestSchema = z10.object({
|
|
|
3660
3793
|
messageId: z10.string().optional(),
|
|
3661
3794
|
// Client-provided ID for deduplication
|
|
3662
3795
|
images: z10.array(ImageAttachmentSchema).optional(),
|
|
3796
|
+
files: z10.array(FileAttachmentSchema).optional(),
|
|
3663
3797
|
attachments: AgentAttachmentsSchema
|
|
3664
3798
|
});
|
|
3665
3799
|
var WaitForFinishRequestSchema = z10.object({
|
|
@@ -3721,6 +3855,7 @@ var CreateAgentRequestMessageSchema = z10.object({
|
|
|
3721
3855
|
clientMessageId: z10.string().optional(),
|
|
3722
3856
|
outputSchema: z10.record(z10.unknown()).optional(),
|
|
3723
3857
|
images: z10.array(ImageAttachmentSchema).optional(),
|
|
3858
|
+
files: z10.array(FileAttachmentSchema).optional(),
|
|
3724
3859
|
attachments: AgentAttachmentsSchema,
|
|
3725
3860
|
git: GitSetupOptionsSchema.optional(),
|
|
3726
3861
|
labels: z10.record(z10.string()).default({}),
|
|
@@ -4517,6 +4652,131 @@ var LinkAccountResponseMessageSchema = z10.object({
|
|
|
4517
4652
|
})
|
|
4518
4653
|
])
|
|
4519
4654
|
});
|
|
4655
|
+
var ListSessionUploadsRequestSchema = z10.object({
|
|
4656
|
+
type: z10.literal("list_session_uploads_request"),
|
|
4657
|
+
requestId: z10.string(),
|
|
4658
|
+
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
|
|
4659
|
+
agentId: z10.string()
|
|
4660
|
+
});
|
|
4661
|
+
var ListSessionUploadsResponseSchema = z10.object({
|
|
4662
|
+
type: z10.literal("list_session_uploads_response"),
|
|
4663
|
+
payload: z10.discriminatedUnion("ok", [
|
|
4664
|
+
z10.object({
|
|
4665
|
+
requestId: z10.string(),
|
|
4666
|
+
ok: z10.literal(true),
|
|
4667
|
+
uploads: z10.array(SessionUploadSchema)
|
|
4668
|
+
}),
|
|
4669
|
+
z10.object({
|
|
4670
|
+
requestId: z10.string(),
|
|
4671
|
+
ok: z10.literal(false),
|
|
4672
|
+
error: z10.string()
|
|
4673
|
+
})
|
|
4674
|
+
])
|
|
4675
|
+
});
|
|
4676
|
+
var DeleteSessionUploadRequestSchema = z10.object({
|
|
4677
|
+
type: z10.literal("delete_session_upload_request"),
|
|
4678
|
+
requestId: z10.string(),
|
|
4679
|
+
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
|
|
4680
|
+
agentId: z10.string(),
|
|
4681
|
+
uploadId: z10.string()
|
|
4682
|
+
});
|
|
4683
|
+
var DeleteSessionUploadResponseSchema = z10.object({
|
|
4684
|
+
type: z10.literal("delete_session_upload_response"),
|
|
4685
|
+
payload: z10.discriminatedUnion("ok", [
|
|
4686
|
+
z10.object({
|
|
4687
|
+
requestId: z10.string(),
|
|
4688
|
+
ok: z10.literal(true)
|
|
4689
|
+
}),
|
|
4690
|
+
z10.object({
|
|
4691
|
+
requestId: z10.string(),
|
|
4692
|
+
ok: z10.literal(false),
|
|
4693
|
+
error: z10.string()
|
|
4694
|
+
})
|
|
4695
|
+
])
|
|
4696
|
+
});
|
|
4697
|
+
var SessionImageSchema = z10.object({
|
|
4698
|
+
id: z10.string(),
|
|
4699
|
+
fileName: z10.string(),
|
|
4700
|
+
mimeType: z10.string(),
|
|
4701
|
+
size: z10.number().int().nonnegative(),
|
|
4702
|
+
createdAt: z10.string(),
|
|
4703
|
+
// ISO-8601 UTC timestamp
|
|
4704
|
+
relativePath: z10.string()
|
|
4705
|
+
});
|
|
4706
|
+
var ListSessionImagesRequestSchema = z10.object({
|
|
4707
|
+
type: z10.literal("list_session_images_request"),
|
|
4708
|
+
requestId: z10.string(),
|
|
4709
|
+
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
|
|
4710
|
+
agentId: z10.string()
|
|
4711
|
+
});
|
|
4712
|
+
var ListSessionImagesResponseSchema = z10.object({
|
|
4713
|
+
type: z10.literal("list_session_images_response"),
|
|
4714
|
+
payload: z10.discriminatedUnion("ok", [
|
|
4715
|
+
z10.object({
|
|
4716
|
+
requestId: z10.string(),
|
|
4717
|
+
ok: z10.literal(true),
|
|
4718
|
+
images: z10.array(SessionImageSchema)
|
|
4719
|
+
}),
|
|
4720
|
+
z10.object({
|
|
4721
|
+
requestId: z10.string(),
|
|
4722
|
+
ok: z10.literal(false),
|
|
4723
|
+
error: z10.string()
|
|
4724
|
+
})
|
|
4725
|
+
])
|
|
4726
|
+
});
|
|
4727
|
+
var DeleteSessionImageRequestSchema = z10.object({
|
|
4728
|
+
type: z10.literal("delete_session_image_request"),
|
|
4729
|
+
requestId: z10.string(),
|
|
4730
|
+
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
|
|
4731
|
+
agentId: z10.string(),
|
|
4732
|
+
/** Workspace-relative POSIX path; traversal-guarded by the daemon. */
|
|
4733
|
+
relativePath: z10.string()
|
|
4734
|
+
});
|
|
4735
|
+
var DeleteSessionImageResponseSchema = z10.object({
|
|
4736
|
+
type: z10.literal("delete_session_image_response"),
|
|
4737
|
+
payload: z10.discriminatedUnion("ok", [
|
|
4738
|
+
z10.object({
|
|
4739
|
+
requestId: z10.string(),
|
|
4740
|
+
ok: z10.literal(true)
|
|
4741
|
+
}),
|
|
4742
|
+
z10.object({
|
|
4743
|
+
requestId: z10.string(),
|
|
4744
|
+
ok: z10.literal(false),
|
|
4745
|
+
error: z10.string()
|
|
4746
|
+
})
|
|
4747
|
+
])
|
|
4748
|
+
});
|
|
4749
|
+
var FetchAttachmentBytesRequestSchema = z10.object({
|
|
4750
|
+
type: z10.literal("fetch_attachment_bytes_request"),
|
|
4751
|
+
requestId: z10.string(),
|
|
4752
|
+
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
|
|
4753
|
+
agentId: z10.string(),
|
|
4754
|
+
kind: z10.enum(["image", "file"]),
|
|
4755
|
+
attachmentId: z10.string(),
|
|
4756
|
+
/** Workspace-relative POSIX path to the byte source on the daemon's disk. */
|
|
4757
|
+
relativePath: z10.string()
|
|
4758
|
+
});
|
|
4759
|
+
var FetchAttachmentBytesResponseSchema = z10.object({
|
|
4760
|
+
type: z10.literal("fetch_attachment_bytes_response"),
|
|
4761
|
+
payload: z10.discriminatedUnion("ok", [
|
|
4762
|
+
z10.object({
|
|
4763
|
+
requestId: z10.string(),
|
|
4764
|
+
ok: z10.literal(true),
|
|
4765
|
+
attachmentId: z10.string(),
|
|
4766
|
+
kind: z10.enum(["image", "file"]),
|
|
4767
|
+
data: z10.string(),
|
|
4768
|
+
// base64
|
|
4769
|
+
mimeType: z10.string(),
|
|
4770
|
+
byteSize: z10.number().int().nonnegative(),
|
|
4771
|
+
fileName: z10.string().optional()
|
|
4772
|
+
}),
|
|
4773
|
+
z10.object({
|
|
4774
|
+
requestId: z10.string(),
|
|
4775
|
+
ok: z10.literal(false),
|
|
4776
|
+
error: z10.string()
|
|
4777
|
+
})
|
|
4778
|
+
])
|
|
4779
|
+
});
|
|
4520
4780
|
var SessionInboundMessageSchema = z10.discriminatedUnion("type", [
|
|
4521
4781
|
VoiceAudioChunkMessageSchema,
|
|
4522
4782
|
AbortRequestMessageSchema,
|
|
@@ -4658,7 +4918,12 @@ var SessionInboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
4658
4918
|
QuestListRequestSchema,
|
|
4659
4919
|
QuestInspectRequestSchema,
|
|
4660
4920
|
QuestStopRequestSchema,
|
|
4661
|
-
RolesListRequestSchema
|
|
4921
|
+
RolesListRequestSchema,
|
|
4922
|
+
ListSessionUploadsRequestSchema,
|
|
4923
|
+
DeleteSessionUploadRequestSchema,
|
|
4924
|
+
ListSessionImagesRequestSchema,
|
|
4925
|
+
DeleteSessionImageRequestSchema,
|
|
4926
|
+
FetchAttachmentBytesRequestSchema
|
|
4662
4927
|
]);
|
|
4663
4928
|
var ActivityLogPayloadSchema = z10.object({
|
|
4664
4929
|
id: z10.string(),
|
|
@@ -6202,7 +6467,12 @@ var SessionOutboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
6202
6467
|
BrandAssetCopyResponseSchema,
|
|
6203
6468
|
BrandAssetUploadResponseSchema,
|
|
6204
6469
|
GetVapidPublicKeyResponseSchema,
|
|
6205
|
-
LinkAccountResponseMessageSchema
|
|
6470
|
+
LinkAccountResponseMessageSchema,
|
|
6471
|
+
ListSessionUploadsResponseSchema,
|
|
6472
|
+
DeleteSessionUploadResponseSchema,
|
|
6473
|
+
ListSessionImagesResponseSchema,
|
|
6474
|
+
DeleteSessionImageResponseSchema,
|
|
6475
|
+
FetchAttachmentBytesResponseSchema
|
|
6206
6476
|
]);
|
|
6207
6477
|
var WSPingMessageSchema = z10.object({
|
|
6208
6478
|
type: z10.literal("ping")
|
|
@@ -8604,14 +8874,14 @@ var DictationStreamManager = class {
|
|
|
8604
8874
|
PCM_CHANNELS,
|
|
8605
8875
|
PCM_BITS_PER_SAMPLE
|
|
8606
8876
|
);
|
|
8607
|
-
const
|
|
8877
|
+
const path26 = await maybePersistDictationDebugAudio(
|
|
8608
8878
|
wavBuffer,
|
|
8609
8879
|
{ sessionId: state.sessionId, dictationId: state.dictationId, format: "audio/wav" },
|
|
8610
8880
|
this.logger,
|
|
8611
8881
|
state.debugChunkWriter?.folder
|
|
8612
8882
|
);
|
|
8613
|
-
state.debugRecordingPath =
|
|
8614
|
-
return
|
|
8883
|
+
state.debugRecordingPath = path26;
|
|
8884
|
+
return path26;
|
|
8615
8885
|
}
|
|
8616
8886
|
failDictationStream(dictationId, error, retryable) {
|
|
8617
8887
|
this.emit({
|
|
@@ -9640,6 +9910,9 @@ async function sendPromptToAgent(params) {
|
|
|
9640
9910
|
messageId,
|
|
9641
9911
|
runOptions,
|
|
9642
9912
|
sessionMode,
|
|
9913
|
+
userMessageImages,
|
|
9914
|
+
userMessageFiles,
|
|
9915
|
+
userMessageTextIsSynthesized,
|
|
9643
9916
|
logger
|
|
9644
9917
|
} = params;
|
|
9645
9918
|
await unarchiveAgentState(agentStorage, agentManager, agentId);
|
|
@@ -9654,7 +9927,10 @@ async function sendPromptToAgent(params) {
|
|
|
9654
9927
|
try {
|
|
9655
9928
|
agentManager.recordUserMessage(agentId, userMessageText, {
|
|
9656
9929
|
messageId,
|
|
9657
|
-
emitState: false
|
|
9930
|
+
emitState: false,
|
|
9931
|
+
...userMessageImages && userMessageImages.length > 0 ? { images: userMessageImages } : {},
|
|
9932
|
+
...userMessageFiles && userMessageFiles.length > 0 ? { files: userMessageFiles } : {},
|
|
9933
|
+
...userMessageTextIsSynthesized ? { textIsSynthesized: true } : {}
|
|
9658
9934
|
});
|
|
9659
9935
|
} catch (error) {
|
|
9660
9936
|
logger.error({ err: error, agentId }, "Failed to record user message");
|
|
@@ -9665,6 +9941,544 @@ async function sendPromptToAgent(params) {
|
|
|
9665
9941
|
});
|
|
9666
9942
|
}
|
|
9667
9943
|
|
|
9944
|
+
// ../server/src/server/agent/prompt-attachments.ts
|
|
9945
|
+
function renderPromptAttachmentAsText(attachment) {
|
|
9946
|
+
switch (attachment.type) {
|
|
9947
|
+
case "github_pr": {
|
|
9948
|
+
const lines = [`GitHub PR #${attachment.number}: ${attachment.title}`, attachment.url];
|
|
9949
|
+
if (attachment.baseRefName) {
|
|
9950
|
+
lines.push(`Base: ${attachment.baseRefName}`);
|
|
9951
|
+
}
|
|
9952
|
+
if (attachment.headRefName) {
|
|
9953
|
+
lines.push(`Head: ${attachment.headRefName}`);
|
|
9954
|
+
}
|
|
9955
|
+
if (attachment.body) {
|
|
9956
|
+
lines.push("", attachment.body);
|
|
9957
|
+
}
|
|
9958
|
+
return lines.join("\n");
|
|
9959
|
+
}
|
|
9960
|
+
case "github_issue": {
|
|
9961
|
+
const lines = [`GitHub Issue #${attachment.number}: ${attachment.title}`, attachment.url];
|
|
9962
|
+
if (attachment.body) {
|
|
9963
|
+
lines.push("", attachment.body);
|
|
9964
|
+
}
|
|
9965
|
+
return lines.join("\n");
|
|
9966
|
+
}
|
|
9967
|
+
case "gitlab_mr": {
|
|
9968
|
+
const lines = [`GitLab MR !${attachment.iid}: ${attachment.title}`, attachment.url];
|
|
9969
|
+
if (attachment.targetBranch) {
|
|
9970
|
+
lines.push(`Target: ${attachment.targetBranch}`);
|
|
9971
|
+
}
|
|
9972
|
+
if (attachment.sourceBranch) {
|
|
9973
|
+
lines.push(`Source: ${attachment.sourceBranch}`);
|
|
9974
|
+
}
|
|
9975
|
+
if (attachment.body) {
|
|
9976
|
+
lines.push("", attachment.body);
|
|
9977
|
+
}
|
|
9978
|
+
return lines.join("\n");
|
|
9979
|
+
}
|
|
9980
|
+
case "gitlab_issue": {
|
|
9981
|
+
const lines = [`GitLab Issue #${attachment.iid}: ${attachment.title}`, attachment.url];
|
|
9982
|
+
if (attachment.body) {
|
|
9983
|
+
lines.push("", attachment.body);
|
|
9984
|
+
}
|
|
9985
|
+
return lines.join("\n");
|
|
9986
|
+
}
|
|
9987
|
+
}
|
|
9988
|
+
}
|
|
9989
|
+
function renderFileAttachmentFooter(uploads) {
|
|
9990
|
+
if (uploads.length === 0) {
|
|
9991
|
+
return "";
|
|
9992
|
+
}
|
|
9993
|
+
const lines = [
|
|
9994
|
+
"",
|
|
9995
|
+
"The user attached the following file(s) to this message. Read them with your file-read tool when relevant; the paths are relative to the workspace root:"
|
|
9996
|
+
];
|
|
9997
|
+
for (const upload of uploads) {
|
|
9998
|
+
const size = formatBytes(upload.size);
|
|
9999
|
+
lines.push(`- ${upload.relativePath} (${upload.mimeType}, ${size})`);
|
|
10000
|
+
}
|
|
10001
|
+
return lines.join("\n");
|
|
10002
|
+
}
|
|
10003
|
+
function formatBytes(bytes) {
|
|
10004
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
10005
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
10006
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
10007
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
10008
|
+
}
|
|
10009
|
+
function findGitHubPrAttachment(attachments) {
|
|
10010
|
+
if (!attachments) {
|
|
10011
|
+
return null;
|
|
10012
|
+
}
|
|
10013
|
+
return attachments.find(
|
|
10014
|
+
(attachment) => attachment.type === "github_pr"
|
|
10015
|
+
) ?? null;
|
|
10016
|
+
}
|
|
10017
|
+
|
|
10018
|
+
// ../server/src/server/uploads/session-upload-store.ts
|
|
10019
|
+
import { promises as fs } from "node:fs";
|
|
10020
|
+
import * as path5 from "node:path";
|
|
10021
|
+
var MAX_UPLOAD_BYTES_PER_MESSAGE = 50 * 1024 * 1024;
|
|
10022
|
+
var MAX_UPLOAD_BYTES_PER_FILE = 50 * 1024 * 1024;
|
|
10023
|
+
var UPLOADS_DIR_REL = path5.posix.join(".appostle", "uploaded_files");
|
|
10024
|
+
var MANIFEST_BASENAME = "uploads.json";
|
|
10025
|
+
function originalUploadFileName(relativePath) {
|
|
10026
|
+
const safe = path5.posix.normalize(relativePath);
|
|
10027
|
+
if (!safe.startsWith(`${UPLOADS_DIR_REL}/`)) return null;
|
|
10028
|
+
const baseName = path5.posix.basename(safe);
|
|
10029
|
+
const sep4 = baseName.indexOf("__");
|
|
10030
|
+
if (sep4 < 0) return null;
|
|
10031
|
+
return baseName.slice(sep4 + 2);
|
|
10032
|
+
}
|
|
10033
|
+
var GITIGNORE_MARKER_LINE = "# Added by Appostle: uploaded chat attachments";
|
|
10034
|
+
var GITIGNORE_BLOCK = `${GITIGNORE_MARKER_LINE}
|
|
10035
|
+
.appostle/
|
|
10036
|
+
`;
|
|
10037
|
+
function emptyManifest() {
|
|
10038
|
+
return { version: 1, uploads: [] };
|
|
10039
|
+
}
|
|
10040
|
+
function sanitizeFileName(input) {
|
|
10041
|
+
const stripped = input.replace(/[\\/\x00-\x1F\x7F]/g, "_").trim();
|
|
10042
|
+
const noTraversal = stripped.replace(/\.{2,}/g, ".");
|
|
10043
|
+
const trimmed = noTraversal.replace(/^[.\s]+|[\s.]+$/g, "");
|
|
10044
|
+
if (trimmed.length === 0) {
|
|
10045
|
+
return "upload.bin";
|
|
10046
|
+
}
|
|
10047
|
+
return trimmed.slice(0, 200);
|
|
10048
|
+
}
|
|
10049
|
+
function manifestDirFor(cwd) {
|
|
10050
|
+
return path5.join(cwd, UPLOADS_DIR_REL);
|
|
10051
|
+
}
|
|
10052
|
+
function manifestPathFor(cwd) {
|
|
10053
|
+
return path5.join(manifestDirFor(cwd), MANIFEST_BASENAME);
|
|
10054
|
+
}
|
|
10055
|
+
function relativeUploadPath(id, sanitizedName) {
|
|
10056
|
+
return path5.posix.join(UPLOADS_DIR_REL, `${id}__${sanitizedName}`);
|
|
10057
|
+
}
|
|
10058
|
+
var UploadSizeError = class extends Error {
|
|
10059
|
+
constructor(totalBytes, cap) {
|
|
10060
|
+
super(`Upload payload exceeds ${cap} bytes (got ${totalBytes})`);
|
|
10061
|
+
this.totalBytes = totalBytes;
|
|
10062
|
+
this.cap = cap;
|
|
10063
|
+
this.name = "UploadSizeError";
|
|
10064
|
+
}
|
|
10065
|
+
};
|
|
10066
|
+
var SessionUploadStore = class {
|
|
10067
|
+
constructor(opts = {}) {
|
|
10068
|
+
this.logger = opts.logger;
|
|
10069
|
+
}
|
|
10070
|
+
/**
|
|
10071
|
+
* Decode and write each file. Throws `UploadSizeError` if either a single
|
|
10072
|
+
* file exceeds the per-file cap or the message total exceeds the per-message
|
|
10073
|
+
* cap. On success, returns the manifest entries (in the same order as input).
|
|
10074
|
+
*/
|
|
10075
|
+
async persistFiles(params) {
|
|
10076
|
+
const { cwd, agentId, files, messageId } = params;
|
|
10077
|
+
if (files.length === 0) {
|
|
10078
|
+
return [];
|
|
10079
|
+
}
|
|
10080
|
+
const decoded = [];
|
|
10081
|
+
let totalBytes = 0;
|
|
10082
|
+
for (const file of files) {
|
|
10083
|
+
let bytes;
|
|
10084
|
+
try {
|
|
10085
|
+
bytes = Buffer.from(file.data, "base64");
|
|
10086
|
+
} catch (error) {
|
|
10087
|
+
throw new Error(
|
|
10088
|
+
`Failed to decode base64 payload for "${file.fileName}": ${error instanceof Error ? error.message : String(error)}`
|
|
10089
|
+
);
|
|
10090
|
+
}
|
|
10091
|
+
if (bytes.length > MAX_UPLOAD_BYTES_PER_FILE) {
|
|
10092
|
+
throw new UploadSizeError(bytes.length, MAX_UPLOAD_BYTES_PER_FILE);
|
|
10093
|
+
}
|
|
10094
|
+
totalBytes += bytes.length;
|
|
10095
|
+
if (totalBytes > MAX_UPLOAD_BYTES_PER_MESSAGE) {
|
|
10096
|
+
throw new UploadSizeError(totalBytes, MAX_UPLOAD_BYTES_PER_MESSAGE);
|
|
10097
|
+
}
|
|
10098
|
+
decoded.push({ file, bytes });
|
|
10099
|
+
}
|
|
10100
|
+
await fs.mkdir(manifestDirFor(cwd), { recursive: true });
|
|
10101
|
+
await this.ensureGitignoreEntry(cwd);
|
|
10102
|
+
const manifest = await this.readManifest(cwd);
|
|
10103
|
+
const created = [];
|
|
10104
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10105
|
+
for (const { file, bytes } of decoded) {
|
|
10106
|
+
const id = crypto.randomUUID();
|
|
10107
|
+
const sanitizedName = sanitizeFileName(file.fileName);
|
|
10108
|
+
const relPath = relativeUploadPath(id, sanitizedName);
|
|
10109
|
+
const absPath = path5.join(cwd, relPath);
|
|
10110
|
+
await fs.writeFile(absPath, bytes);
|
|
10111
|
+
const entry = {
|
|
10112
|
+
id,
|
|
10113
|
+
fileName: sanitizedName,
|
|
10114
|
+
mimeType: file.mimeType,
|
|
10115
|
+
size: bytes.length,
|
|
10116
|
+
createdAt,
|
|
10117
|
+
messageId,
|
|
10118
|
+
relativePath: relPath
|
|
10119
|
+
};
|
|
10120
|
+
manifest.uploads.push({ ...entry, agentId });
|
|
10121
|
+
created.push(entry);
|
|
10122
|
+
}
|
|
10123
|
+
await this.writeManifest(cwd, manifest);
|
|
10124
|
+
this.logger?.info(
|
|
10125
|
+
{ agentId, count: created.length, totalBytes },
|
|
10126
|
+
"Persisted chat file uploads"
|
|
10127
|
+
);
|
|
10128
|
+
return created;
|
|
10129
|
+
}
|
|
10130
|
+
/**
|
|
10131
|
+
* Read bytes back from disk by workspace-relative path. Symmetric with
|
|
10132
|
+
* `SessionImageStore.readImageBytes` so the fetch-bytes RPC handler can
|
|
10133
|
+
* route uniformly. Traversal-guarded: refuses anything that escapes
|
|
10134
|
+
* `.appostle/uploaded_files/` or contains `..`.
|
|
10135
|
+
*/
|
|
10136
|
+
async readUploadBytes(params) {
|
|
10137
|
+
const safe = path5.posix.normalize(params.relativePath);
|
|
10138
|
+
if (!safe.startsWith(`${UPLOADS_DIR_REL}/`) || safe.includes("..")) {
|
|
10139
|
+
throw new Error(`Refused to read upload outside ${UPLOADS_DIR_REL}: ${params.relativePath}`);
|
|
10140
|
+
}
|
|
10141
|
+
const abs = path5.join(params.cwd, safe);
|
|
10142
|
+
const data = await fs.readFile(abs);
|
|
10143
|
+
const baseName = path5.posix.basename(safe);
|
|
10144
|
+
const sep4 = baseName.indexOf("__");
|
|
10145
|
+
const fileName = sep4 >= 0 ? baseName.slice(sep4 + 2) : baseName;
|
|
10146
|
+
let mimeType = "application/octet-stream";
|
|
10147
|
+
try {
|
|
10148
|
+
const manifest = await this.readManifest(params.cwd);
|
|
10149
|
+
const match = manifest.uploads.find((entry) => entry.relativePath === safe);
|
|
10150
|
+
if (match) {
|
|
10151
|
+
mimeType = match.mimeType;
|
|
10152
|
+
}
|
|
10153
|
+
} catch {
|
|
10154
|
+
}
|
|
10155
|
+
return { data, mimeType, byteSize: data.length, fileName };
|
|
10156
|
+
}
|
|
10157
|
+
/**
|
|
10158
|
+
* List every file stored for this workspace. Workspace-scoped, NOT
|
|
10159
|
+
* agent-scoped — uploads live under `<workspace>/.appostle/uploaded_files/`
|
|
10160
|
+
* and persist across agent lifecycles, so filtering by `agentId` would hide
|
|
10161
|
+
* files attached by older or sibling agents in the same workspace. (Mirrors
|
|
10162
|
+
* the image store's directory-scan behaviour.) The `agentId` parameter is
|
|
10163
|
+
* preserved on the API for backward-compat but ignored.
|
|
10164
|
+
*
|
|
10165
|
+
* Newest first.
|
|
10166
|
+
*/
|
|
10167
|
+
async listUploads(params) {
|
|
10168
|
+
const manifest = await this.readManifest(params.cwd);
|
|
10169
|
+
return manifest.uploads.map(({ agentId: _agentId, ...rest }) => rest).sort((a, b) => a.createdAt < b.createdAt ? 1 : -1);
|
|
10170
|
+
}
|
|
10171
|
+
/**
|
|
10172
|
+
* Delete a file by upload id. Workspace-scoped to match `listUploads` — any
|
|
10173
|
+
* agent in the workspace can delete any file. The `agentId` parameter is
|
|
10174
|
+
* preserved on the API for backward-compat but ignored.
|
|
10175
|
+
*/
|
|
10176
|
+
async deleteUpload(params) {
|
|
10177
|
+
const manifest = await this.readManifest(params.cwd);
|
|
10178
|
+
const idx = manifest.uploads.findIndex((entry) => entry.id === params.uploadId);
|
|
10179
|
+
if (idx === -1) {
|
|
10180
|
+
return { deleted: false };
|
|
10181
|
+
}
|
|
10182
|
+
const [removed] = manifest.uploads.splice(idx, 1);
|
|
10183
|
+
if (removed) {
|
|
10184
|
+
const absPath = path5.join(params.cwd, removed.relativePath);
|
|
10185
|
+
try {
|
|
10186
|
+
await fs.unlink(absPath);
|
|
10187
|
+
} catch (error) {
|
|
10188
|
+
this.logger?.warn(
|
|
10189
|
+
{ uploadId: params.uploadId, error: error.message },
|
|
10190
|
+
"deleteUpload: file already missing, removing manifest entry"
|
|
10191
|
+
);
|
|
10192
|
+
}
|
|
10193
|
+
}
|
|
10194
|
+
await this.writeManifest(params.cwd, manifest);
|
|
10195
|
+
return { deleted: true };
|
|
10196
|
+
}
|
|
10197
|
+
async readManifest(cwd) {
|
|
10198
|
+
const file = manifestPathFor(cwd);
|
|
10199
|
+
let raw;
|
|
10200
|
+
try {
|
|
10201
|
+
raw = await fs.readFile(file, "utf8");
|
|
10202
|
+
} catch (error) {
|
|
10203
|
+
if (error.code === "ENOENT") {
|
|
10204
|
+
return emptyManifest();
|
|
10205
|
+
}
|
|
10206
|
+
throw error;
|
|
10207
|
+
}
|
|
10208
|
+
let parsed;
|
|
10209
|
+
try {
|
|
10210
|
+
parsed = JSON.parse(raw);
|
|
10211
|
+
} catch {
|
|
10212
|
+
this.logger?.warn({ file }, "Manifest is not valid JSON; treating as empty");
|
|
10213
|
+
return emptyManifest();
|
|
10214
|
+
}
|
|
10215
|
+
if (typeof parsed === "object" && parsed !== null && "version" in parsed && parsed.version === 1 && Array.isArray(parsed.uploads)) {
|
|
10216
|
+
return parsed;
|
|
10217
|
+
}
|
|
10218
|
+
return emptyManifest();
|
|
10219
|
+
}
|
|
10220
|
+
async writeManifest(cwd, manifest) {
|
|
10221
|
+
const file = manifestPathFor(cwd);
|
|
10222
|
+
const tmp = `${file}.tmp`;
|
|
10223
|
+
await fs.writeFile(tmp, JSON.stringify(manifest, null, 2), "utf8");
|
|
10224
|
+
await fs.rename(tmp, file);
|
|
10225
|
+
}
|
|
10226
|
+
/** Append a `.appostle/` ignore line to the workspace `.gitignore`, idempotently. */
|
|
10227
|
+
async ensureGitignoreEntry(cwd) {
|
|
10228
|
+
const file = path5.join(cwd, ".gitignore");
|
|
10229
|
+
let existing = "";
|
|
10230
|
+
try {
|
|
10231
|
+
existing = await fs.readFile(file, "utf8");
|
|
10232
|
+
} catch (error) {
|
|
10233
|
+
if (error.code !== "ENOENT") {
|
|
10234
|
+
throw error;
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
if (existing.includes(GITIGNORE_MARKER_LINE)) {
|
|
10238
|
+
return;
|
|
10239
|
+
}
|
|
10240
|
+
const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
10241
|
+
await fs.writeFile(file, `${existing}${separator}${GITIGNORE_BLOCK}`, "utf8");
|
|
10242
|
+
}
|
|
10243
|
+
};
|
|
10244
|
+
|
|
10245
|
+
// ../server/src/server/uploads/session-image-store.ts
|
|
10246
|
+
import { promises as fs2 } from "node:fs";
|
|
10247
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
10248
|
+
import * as path6 from "node:path";
|
|
10249
|
+
var IMAGES_DIR_REL = path6.posix.join(".appostle", "uploaded_images");
|
|
10250
|
+
var GITIGNORE_MARKER_LINE2 = "# Added by Appostle: uploaded chat attachments";
|
|
10251
|
+
var GITIGNORE_BLOCK2 = `${GITIGNORE_MARKER_LINE2}
|
|
10252
|
+
.appostle/
|
|
10253
|
+
`;
|
|
10254
|
+
var EXTENSION_BY_MIME = {
|
|
10255
|
+
"image/png": "png",
|
|
10256
|
+
"image/jpeg": "jpg",
|
|
10257
|
+
"image/jpg": "jpg",
|
|
10258
|
+
"image/gif": "gif",
|
|
10259
|
+
"image/webp": "webp",
|
|
10260
|
+
"image/avif": "avif",
|
|
10261
|
+
"image/heic": "heic",
|
|
10262
|
+
"image/heif": "heif",
|
|
10263
|
+
"image/tiff": "tiff",
|
|
10264
|
+
"image/bmp": "bmp",
|
|
10265
|
+
"image/svg+xml": "svg"
|
|
10266
|
+
};
|
|
10267
|
+
function extensionForImage(input) {
|
|
10268
|
+
if (input.fileName) {
|
|
10269
|
+
const dot = input.fileName.lastIndexOf(".");
|
|
10270
|
+
if (dot >= 0 && dot < input.fileName.length - 1) {
|
|
10271
|
+
const ext = input.fileName.slice(dot + 1).toLowerCase();
|
|
10272
|
+
if (/^[a-z0-9]{1,8}$/.test(ext)) {
|
|
10273
|
+
return ext;
|
|
10274
|
+
}
|
|
10275
|
+
}
|
|
10276
|
+
}
|
|
10277
|
+
return EXTENSION_BY_MIME[input.mimeType] ?? "img";
|
|
10278
|
+
}
|
|
10279
|
+
function mimeTypeForExtension(ext) {
|
|
10280
|
+
switch (ext) {
|
|
10281
|
+
case "png":
|
|
10282
|
+
return "image/png";
|
|
10283
|
+
case "jpg":
|
|
10284
|
+
case "jpeg":
|
|
10285
|
+
return "image/jpeg";
|
|
10286
|
+
case "gif":
|
|
10287
|
+
return "image/gif";
|
|
10288
|
+
case "webp":
|
|
10289
|
+
return "image/webp";
|
|
10290
|
+
case "avif":
|
|
10291
|
+
return "image/avif";
|
|
10292
|
+
case "heic":
|
|
10293
|
+
return "image/heic";
|
|
10294
|
+
case "heif":
|
|
10295
|
+
return "image/heif";
|
|
10296
|
+
case "tiff":
|
|
10297
|
+
return "image/tiff";
|
|
10298
|
+
case "bmp":
|
|
10299
|
+
return "image/bmp";
|
|
10300
|
+
case "svg":
|
|
10301
|
+
return "image/svg+xml";
|
|
10302
|
+
default:
|
|
10303
|
+
return "application/octet-stream";
|
|
10304
|
+
}
|
|
10305
|
+
}
|
|
10306
|
+
var SessionImageStore = class {
|
|
10307
|
+
constructor(opts = {}) {
|
|
10308
|
+
this.logger = opts.logger;
|
|
10309
|
+
}
|
|
10310
|
+
/**
|
|
10311
|
+
* Decode and write each image. Throws `UploadSizeError` on cap violations.
|
|
10312
|
+
* On success returns one entry per input image (in order).
|
|
10313
|
+
*/
|
|
10314
|
+
async persistImages(params) {
|
|
10315
|
+
const { cwd, agentId, messageId, images } = params;
|
|
10316
|
+
if (images.length === 0) {
|
|
10317
|
+
return [];
|
|
10318
|
+
}
|
|
10319
|
+
const decoded = [];
|
|
10320
|
+
let totalBytes = 0;
|
|
10321
|
+
for (const image of images) {
|
|
10322
|
+
let bytes;
|
|
10323
|
+
try {
|
|
10324
|
+
bytes = Buffer.from(image.data, "base64");
|
|
10325
|
+
} catch (error) {
|
|
10326
|
+
throw new Error(
|
|
10327
|
+
`Failed to decode base64 image: ${error instanceof Error ? error.message : String(error)}`
|
|
10328
|
+
);
|
|
10329
|
+
}
|
|
10330
|
+
if (bytes.length > MAX_UPLOAD_BYTES_PER_FILE) {
|
|
10331
|
+
throw new UploadSizeError(bytes.length, MAX_UPLOAD_BYTES_PER_FILE);
|
|
10332
|
+
}
|
|
10333
|
+
totalBytes += bytes.length;
|
|
10334
|
+
if (totalBytes > MAX_UPLOAD_BYTES_PER_MESSAGE) {
|
|
10335
|
+
throw new UploadSizeError(totalBytes, MAX_UPLOAD_BYTES_PER_MESSAGE);
|
|
10336
|
+
}
|
|
10337
|
+
const hash = createHash2("sha256").update(bytes).digest("hex").slice(0, 16);
|
|
10338
|
+
decoded.push({ image, bytes, hash });
|
|
10339
|
+
}
|
|
10340
|
+
const dir = path6.join(cwd, IMAGES_DIR_REL);
|
|
10341
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
10342
|
+
await this.ensureGitignoreEntry(cwd);
|
|
10343
|
+
const created = [];
|
|
10344
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10345
|
+
const timestampMs = Date.now();
|
|
10346
|
+
for (const entry of decoded) {
|
|
10347
|
+
const ext = extensionForImage({
|
|
10348
|
+
mimeType: entry.image.mimeType,
|
|
10349
|
+
fileName: entry.image.fileName
|
|
10350
|
+
});
|
|
10351
|
+
const baseName = `${timestampMs}-${entry.hash}.${ext}`;
|
|
10352
|
+
const relPath = path6.posix.join(IMAGES_DIR_REL, baseName);
|
|
10353
|
+
const absPath = path6.join(cwd, relPath);
|
|
10354
|
+
await fs2.writeFile(absPath, entry.bytes);
|
|
10355
|
+
created.push({
|
|
10356
|
+
id: entry.image.id ?? randomUUID(),
|
|
10357
|
+
fileName: entry.image.fileName ?? null,
|
|
10358
|
+
mimeType: entry.image.mimeType,
|
|
10359
|
+
size: entry.bytes.length,
|
|
10360
|
+
createdAt,
|
|
10361
|
+
messageId,
|
|
10362
|
+
relativePath: relPath,
|
|
10363
|
+
hash: entry.hash
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
this.logger?.info(
|
|
10367
|
+
{ agentId, count: created.length, totalBytes },
|
|
10368
|
+
"Persisted chat image uploads"
|
|
10369
|
+
);
|
|
10370
|
+
return created;
|
|
10371
|
+
}
|
|
10372
|
+
/**
|
|
10373
|
+
* Read bytes back from disk by workspace-relative path. Used by the (future
|
|
10374
|
+
* Phase 4) fetch-bytes RPC and by Phase 3's prompt-build path.
|
|
10375
|
+
*
|
|
10376
|
+
* Defense in depth: refuses anything outside `.appostle/uploaded_images/`
|
|
10377
|
+
* or anything containing `..`. The caller is the trust boundary, but
|
|
10378
|
+
* costing nothing to double-check at the I/O edge.
|
|
10379
|
+
*/
|
|
10380
|
+
async readImageBytes(params) {
|
|
10381
|
+
const safe = path6.posix.normalize(params.relativePath);
|
|
10382
|
+
if (!safe.startsWith(`${IMAGES_DIR_REL}/`) || safe.includes("..")) {
|
|
10383
|
+
throw new Error(`Refused to read image outside ${IMAGES_DIR_REL}: ${params.relativePath}`);
|
|
10384
|
+
}
|
|
10385
|
+
const abs = path6.join(params.cwd, safe);
|
|
10386
|
+
const data = await fs2.readFile(abs);
|
|
10387
|
+
const ext = path6.extname(safe).slice(1).toLowerCase();
|
|
10388
|
+
return { data, mimeType: mimeTypeForExtension(ext), byteSize: data.length };
|
|
10389
|
+
}
|
|
10390
|
+
/**
|
|
10391
|
+
* List every image stored for this workspace. There is no manifest — the
|
|
10392
|
+
* directory listing IS the source of truth. Filenames carry the timestamp
|
|
10393
|
+
* (`<unix_ms>-<hash>.<ext>`); we parse that for `createdAt` and fall back to
|
|
10394
|
+
* file mtime if the prefix is missing.
|
|
10395
|
+
*
|
|
10396
|
+
* Returns newest-first to match the upload-store ordering expected by the UI.
|
|
10397
|
+
*/
|
|
10398
|
+
async listImages(params) {
|
|
10399
|
+
const dir = path6.join(params.cwd, IMAGES_DIR_REL);
|
|
10400
|
+
let names;
|
|
10401
|
+
try {
|
|
10402
|
+
names = await fs2.readdir(dir);
|
|
10403
|
+
} catch (error) {
|
|
10404
|
+
if (error.code === "ENOENT") {
|
|
10405
|
+
return [];
|
|
10406
|
+
}
|
|
10407
|
+
throw error;
|
|
10408
|
+
}
|
|
10409
|
+
const entries = [];
|
|
10410
|
+
for (const name of names) {
|
|
10411
|
+
if (name.startsWith(".")) continue;
|
|
10412
|
+
const abs = path6.join(dir, name);
|
|
10413
|
+
let stat5;
|
|
10414
|
+
try {
|
|
10415
|
+
stat5 = await fs2.stat(abs);
|
|
10416
|
+
} catch {
|
|
10417
|
+
continue;
|
|
10418
|
+
}
|
|
10419
|
+
if (!stat5.isFile()) continue;
|
|
10420
|
+
const ext = path6.extname(name).slice(1).toLowerCase();
|
|
10421
|
+
const mimeType = mimeTypeForExtension(ext);
|
|
10422
|
+
let createdAt;
|
|
10423
|
+
const dash = name.indexOf("-");
|
|
10424
|
+
const msCandidate = dash > 0 ? Number(name.slice(0, dash)) : Number.NaN;
|
|
10425
|
+
if (Number.isFinite(msCandidate) && msCandidate > 0) {
|
|
10426
|
+
createdAt = new Date(msCandidate).toISOString();
|
|
10427
|
+
} else {
|
|
10428
|
+
createdAt = stat5.mtime.toISOString();
|
|
10429
|
+
}
|
|
10430
|
+
const relPath = path6.posix.join(IMAGES_DIR_REL, name);
|
|
10431
|
+
entries.push({
|
|
10432
|
+
id: relPath,
|
|
10433
|
+
fileName: name,
|
|
10434
|
+
mimeType,
|
|
10435
|
+
size: stat5.size,
|
|
10436
|
+
createdAt,
|
|
10437
|
+
relativePath: relPath
|
|
10438
|
+
});
|
|
10439
|
+
}
|
|
10440
|
+
entries.sort((a, b) => a.createdAt < b.createdAt ? 1 : -1);
|
|
10441
|
+
return entries;
|
|
10442
|
+
}
|
|
10443
|
+
/**
|
|
10444
|
+
* Delete a single stored image by workspace-relative path. Same traversal
|
|
10445
|
+
* guard as `readImageBytes`. Idempotent: returns `{ deleted: false }` if
|
|
10446
|
+
* the file is already gone (so the caller can refresh its listing).
|
|
10447
|
+
*/
|
|
10448
|
+
async deleteImage(params) {
|
|
10449
|
+
const safe = path6.posix.normalize(params.relativePath);
|
|
10450
|
+
if (!safe.startsWith(`${IMAGES_DIR_REL}/`) || safe.includes("..")) {
|
|
10451
|
+
throw new Error(`Refused to delete image outside ${IMAGES_DIR_REL}: ${params.relativePath}`);
|
|
10452
|
+
}
|
|
10453
|
+
const abs = path6.join(params.cwd, safe);
|
|
10454
|
+
try {
|
|
10455
|
+
await fs2.unlink(abs);
|
|
10456
|
+
return { deleted: true };
|
|
10457
|
+
} catch (error) {
|
|
10458
|
+
if (error.code === "ENOENT") {
|
|
10459
|
+
return { deleted: false };
|
|
10460
|
+
}
|
|
10461
|
+
throw error;
|
|
10462
|
+
}
|
|
10463
|
+
}
|
|
10464
|
+
async ensureGitignoreEntry(cwd) {
|
|
10465
|
+
const file = path6.join(cwd, ".gitignore");
|
|
10466
|
+
let existing = "";
|
|
10467
|
+
try {
|
|
10468
|
+
existing = await fs2.readFile(file, "utf8");
|
|
10469
|
+
} catch (error) {
|
|
10470
|
+
if (error.code !== "ENOENT") {
|
|
10471
|
+
throw error;
|
|
10472
|
+
}
|
|
10473
|
+
}
|
|
10474
|
+
if (existing.includes(GITIGNORE_MARKER_LINE2)) {
|
|
10475
|
+
return;
|
|
10476
|
+
}
|
|
10477
|
+
const sep4 = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
10478
|
+
await fs2.writeFile(file, `${existing}${sep4}${GITIGNORE_BLOCK2}`, "utf8");
|
|
10479
|
+
}
|
|
10480
|
+
};
|
|
10481
|
+
|
|
9668
10482
|
// ../server/src/server/session.ts
|
|
9669
10483
|
import { experimental_createMCPClient } from "ai";
|
|
9670
10484
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -9857,14 +10671,14 @@ function parseDiff(diffText) {
|
|
|
9857
10671
|
const firstLine = lines[0];
|
|
9858
10672
|
const isNew = section.includes("new file mode") || section.includes("--- /dev/null");
|
|
9859
10673
|
const isDeleted = section.includes("deleted file mode") || section.includes("+++ /dev/null");
|
|
9860
|
-
let
|
|
10674
|
+
let path26 = "unknown";
|
|
9861
10675
|
const pathMatch = firstLine.match(/a\/(.*?) b\//);
|
|
9862
10676
|
if (pathMatch) {
|
|
9863
|
-
|
|
10677
|
+
path26 = pathMatch[1];
|
|
9864
10678
|
} else {
|
|
9865
10679
|
const newFileMatch = firstLine.match(/b\/(.+)$/);
|
|
9866
10680
|
if (newFileMatch) {
|
|
9867
|
-
|
|
10681
|
+
path26 = newFileMatch[1];
|
|
9868
10682
|
}
|
|
9869
10683
|
}
|
|
9870
10684
|
const hunks = [];
|
|
@@ -9908,7 +10722,7 @@ function parseDiff(diffText) {
|
|
|
9908
10722
|
if (currentHunk) {
|
|
9909
10723
|
hunks.push(currentHunk);
|
|
9910
10724
|
}
|
|
9911
|
-
files.push({ path:
|
|
10725
|
+
files.push({ path: path26, isNew, isDeleted, additions, deletions, hunks });
|
|
9912
10726
|
}
|
|
9913
10727
|
return files;
|
|
9914
10728
|
}
|
|
@@ -9964,9 +10778,9 @@ function buildTokenLookup(lineMap, highlighted) {
|
|
|
9964
10778
|
}
|
|
9965
10779
|
return lookup2;
|
|
9966
10780
|
}
|
|
9967
|
-
function buildFullFileTokenLookup(fileContent,
|
|
10781
|
+
function buildFullFileTokenLookup(fileContent, path26) {
|
|
9968
10782
|
const lookup2 = /* @__PURE__ */ new Map();
|
|
9969
|
-
const highlighted = highlightCode(fileContent,
|
|
10783
|
+
const highlighted = highlightCode(fileContent, path26);
|
|
9970
10784
|
for (let i = 0; i < highlighted.length; i++) {
|
|
9971
10785
|
lookup2.set(i + 1, highlighted[i]);
|
|
9972
10786
|
}
|
|
@@ -11667,11 +12481,11 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
11667
12481
|
}
|
|
11668
12482
|
continue;
|
|
11669
12483
|
}
|
|
11670
|
-
const
|
|
11671
|
-
if (!
|
|
12484
|
+
const path26 = tabParts[1];
|
|
12485
|
+
if (!path26) continue;
|
|
11672
12486
|
const code = rawStatus[0];
|
|
11673
12487
|
changes.push({
|
|
11674
|
-
path:
|
|
12488
|
+
path: path26,
|
|
11675
12489
|
status: rawStatus,
|
|
11676
12490
|
isNew: code === "A",
|
|
11677
12491
|
isDeleted: code === "D"
|
|
@@ -11706,9 +12520,9 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
11706
12520
|
}
|
|
11707
12521
|
return Array.from(byPath.values());
|
|
11708
12522
|
}
|
|
11709
|
-
async function readGitFileContentAtRef(cwd, ref,
|
|
12523
|
+
async function readGitFileContentAtRef(cwd, ref, path26) {
|
|
11710
12524
|
try {
|
|
11711
|
-
const { stdout } = await runGitCommand(["show", `${ref}:${
|
|
12525
|
+
const { stdout } = await runGitCommand(["show", `${ref}:${path26}`], {
|
|
11712
12526
|
cwd,
|
|
11713
12527
|
env: READ_ONLY_GIT_ENV2
|
|
11714
12528
|
});
|
|
@@ -11769,21 +12583,21 @@ async function getTrackedNumstatByPath(cwd, ref, ignoreWhitespace = false) {
|
|
|
11769
12583
|
const additionsField = parts[0] ?? "";
|
|
11770
12584
|
const deletionsField = parts[1] ?? "";
|
|
11771
12585
|
const rawPath = parts.slice(2).join(" ");
|
|
11772
|
-
const
|
|
11773
|
-
if (!
|
|
12586
|
+
const path26 = normalizeNumstatPath(rawPath);
|
|
12587
|
+
if (!path26) {
|
|
11774
12588
|
continue;
|
|
11775
12589
|
}
|
|
11776
12590
|
if (additionsField === "-" || deletionsField === "-") {
|
|
11777
|
-
stats.set(
|
|
12591
|
+
stats.set(path26, { additions: 0, deletions: 0, isBinary: true });
|
|
11778
12592
|
continue;
|
|
11779
12593
|
}
|
|
11780
12594
|
const additions = Number.parseInt(additionsField, 10);
|
|
11781
12595
|
const deletions = Number.parseInt(deletionsField, 10);
|
|
11782
12596
|
if (Number.isNaN(additions) || Number.isNaN(deletions)) {
|
|
11783
|
-
stats.set(
|
|
12597
|
+
stats.set(path26, null);
|
|
11784
12598
|
continue;
|
|
11785
12599
|
}
|
|
11786
|
-
stats.set(
|
|
12600
|
+
stats.set(path26, { additions, deletions, isBinary: false });
|
|
11787
12601
|
}
|
|
11788
12602
|
return stats;
|
|
11789
12603
|
}
|
|
@@ -12368,10 +13182,10 @@ async function listUncommittedFiles(cwd) {
|
|
|
12368
13182
|
if (dest) results.push({ path: dest, changeType: code });
|
|
12369
13183
|
continue;
|
|
12370
13184
|
}
|
|
12371
|
-
const
|
|
12372
|
-
if (!
|
|
13185
|
+
const path26 = parts[1];
|
|
13186
|
+
if (!path26) continue;
|
|
12373
13187
|
if (code === "A" || code === "M" || code === "D") {
|
|
12374
|
-
results.push({ path:
|
|
13188
|
+
results.push({ path: path26, changeType: code });
|
|
12375
13189
|
}
|
|
12376
13190
|
}
|
|
12377
13191
|
} catch {
|
|
@@ -14069,11 +14883,11 @@ async function spawnWorktreeScripts(options) {
|
|
|
14069
14883
|
}
|
|
14070
14884
|
|
|
14071
14885
|
// ../server/src/server/agent/providers/claude-agent.ts
|
|
14072
|
-
import { randomUUID } from "node:crypto";
|
|
14073
|
-
import
|
|
14886
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
14887
|
+
import fs3 from "node:fs";
|
|
14074
14888
|
import { promises } from "node:fs";
|
|
14075
14889
|
import os2 from "node:os";
|
|
14076
|
-
import
|
|
14890
|
+
import path8 from "node:path";
|
|
14077
14891
|
import {
|
|
14078
14892
|
query
|
|
14079
14893
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
@@ -15148,7 +15962,7 @@ function mapClaudeCanceledToolCall(params) {
|
|
|
15148
15962
|
}
|
|
15149
15963
|
|
|
15150
15964
|
// ../server/src/server/agent/providers/claude/task-notification-tool-call.ts
|
|
15151
|
-
import { createHash as
|
|
15965
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15152
15966
|
import { z as z21 } from "zod";
|
|
15153
15967
|
var TASK_NOTIFICATION_MARKER = "<task-notification>";
|
|
15154
15968
|
var TAG_NAME_PATTERN = /[.*+?^${}()|[\]\\]/g;
|
|
@@ -15285,7 +16099,7 @@ function buildTaskNotificationCallId(envelope) {
|
|
|
15285
16099
|
return `task_notification_${taskSegment}`;
|
|
15286
16100
|
}
|
|
15287
16101
|
const seed = [envelope.status, envelope.summary, envelope.outputFile, envelope.rawText].filter((value) => typeof value === "string").join("|") || "task_notification";
|
|
15288
|
-
const digest =
|
|
16102
|
+
const digest = createHash3("sha1").update(seed).digest("hex").slice(0, 12);
|
|
15289
16103
|
return `task_notification_${digest}`;
|
|
15290
16104
|
}
|
|
15291
16105
|
function buildTaskNotificationLabel(envelope) {
|
|
@@ -15464,6 +16278,40 @@ function normalizeClaudeRuntimeModelId(value) {
|
|
|
15464
16278
|
return `claude-${family}-${major}-${minor}${suffix}`;
|
|
15465
16279
|
}
|
|
15466
16280
|
|
|
16281
|
+
// ../server/src/server/agent/providers/claude-feature-definitions.ts
|
|
16282
|
+
var CLAUDE_FAST_MODE_SUPPORTED_MODEL_PREFIXES = ["claude-opus-"];
|
|
16283
|
+
var CLAUDE_FAST_MODE_FEATURE = {
|
|
16284
|
+
type: "toggle",
|
|
16285
|
+
id: "fast_mode",
|
|
16286
|
+
label: "Fast",
|
|
16287
|
+
description: "Priority inference for faster responses",
|
|
16288
|
+
tooltip: "Toggle fast mode",
|
|
16289
|
+
icon: "zap"
|
|
16290
|
+
};
|
|
16291
|
+
function normalizeClaudeModelId(modelId) {
|
|
16292
|
+
const normalized = typeof modelId === "string" ? modelId.trim() : "";
|
|
16293
|
+
return normalized.length > 0 ? normalized : null;
|
|
16294
|
+
}
|
|
16295
|
+
function claudeModelSupportsFastMode(modelId) {
|
|
16296
|
+
const normalizedModelId = normalizeClaudeModelId(modelId);
|
|
16297
|
+
if (!normalizedModelId) {
|
|
16298
|
+
return false;
|
|
16299
|
+
}
|
|
16300
|
+
return CLAUDE_FAST_MODE_SUPPORTED_MODEL_PREFIXES.some(
|
|
16301
|
+
(prefix) => normalizedModelId.startsWith(prefix)
|
|
16302
|
+
);
|
|
16303
|
+
}
|
|
16304
|
+
function buildClaudeFeatures(input) {
|
|
16305
|
+
const features = [];
|
|
16306
|
+
if (claudeModelSupportsFastMode(input.modelId)) {
|
|
16307
|
+
features.push({
|
|
16308
|
+
...CLAUDE_FAST_MODE_FEATURE,
|
|
16309
|
+
value: input.fastModeEnabled
|
|
16310
|
+
});
|
|
16311
|
+
}
|
|
16312
|
+
return features;
|
|
16313
|
+
}
|
|
16314
|
+
|
|
15467
16315
|
// ../server/src/server/agent/providers/claude/partial-json.ts
|
|
15468
16316
|
function skipWhitespace(input, index) {
|
|
15469
16317
|
let currentIndex = index;
|
|
@@ -16031,62 +16879,8 @@ async function resolveBinaryVersion(binaryPath) {
|
|
|
16031
16879
|
}
|
|
16032
16880
|
}
|
|
16033
16881
|
|
|
16034
|
-
// ../server/src/server/agent/prompt-attachments.ts
|
|
16035
|
-
function renderPromptAttachmentAsText(attachment) {
|
|
16036
|
-
switch (attachment.type) {
|
|
16037
|
-
case "github_pr": {
|
|
16038
|
-
const lines = [`GitHub PR #${attachment.number}: ${attachment.title}`, attachment.url];
|
|
16039
|
-
if (attachment.baseRefName) {
|
|
16040
|
-
lines.push(`Base: ${attachment.baseRefName}`);
|
|
16041
|
-
}
|
|
16042
|
-
if (attachment.headRefName) {
|
|
16043
|
-
lines.push(`Head: ${attachment.headRefName}`);
|
|
16044
|
-
}
|
|
16045
|
-
if (attachment.body) {
|
|
16046
|
-
lines.push("", attachment.body);
|
|
16047
|
-
}
|
|
16048
|
-
return lines.join("\n");
|
|
16049
|
-
}
|
|
16050
|
-
case "github_issue": {
|
|
16051
|
-
const lines = [`GitHub Issue #${attachment.number}: ${attachment.title}`, attachment.url];
|
|
16052
|
-
if (attachment.body) {
|
|
16053
|
-
lines.push("", attachment.body);
|
|
16054
|
-
}
|
|
16055
|
-
return lines.join("\n");
|
|
16056
|
-
}
|
|
16057
|
-
case "gitlab_mr": {
|
|
16058
|
-
const lines = [`GitLab MR !${attachment.iid}: ${attachment.title}`, attachment.url];
|
|
16059
|
-
if (attachment.targetBranch) {
|
|
16060
|
-
lines.push(`Target: ${attachment.targetBranch}`);
|
|
16061
|
-
}
|
|
16062
|
-
if (attachment.sourceBranch) {
|
|
16063
|
-
lines.push(`Source: ${attachment.sourceBranch}`);
|
|
16064
|
-
}
|
|
16065
|
-
if (attachment.body) {
|
|
16066
|
-
lines.push("", attachment.body);
|
|
16067
|
-
}
|
|
16068
|
-
return lines.join("\n");
|
|
16069
|
-
}
|
|
16070
|
-
case "gitlab_issue": {
|
|
16071
|
-
const lines = [`GitLab Issue #${attachment.iid}: ${attachment.title}`, attachment.url];
|
|
16072
|
-
if (attachment.body) {
|
|
16073
|
-
lines.push("", attachment.body);
|
|
16074
|
-
}
|
|
16075
|
-
return lines.join("\n");
|
|
16076
|
-
}
|
|
16077
|
-
}
|
|
16078
|
-
}
|
|
16079
|
-
function findGitHubPrAttachment(attachments) {
|
|
16080
|
-
if (!attachments) {
|
|
16081
|
-
return null;
|
|
16082
|
-
}
|
|
16083
|
-
return attachments.find(
|
|
16084
|
-
(attachment) => attachment.type === "github_pr"
|
|
16085
|
-
) ?? null;
|
|
16086
|
-
}
|
|
16087
|
-
|
|
16088
16882
|
// ../server/src/server/agent/plan-filename.ts
|
|
16089
|
-
import
|
|
16883
|
+
import path7 from "node:path";
|
|
16090
16884
|
var MAX_SLUG_LENGTH2 = 40;
|
|
16091
16885
|
function slugifyPlanName(name) {
|
|
16092
16886
|
if (typeof name !== "string") {
|
|
@@ -16135,12 +16929,12 @@ function extractPlanNameFromFrontmatter(content) {
|
|
|
16135
16929
|
}
|
|
16136
16930
|
function resolvePlanFilename(options) {
|
|
16137
16931
|
const { originalPath, newSlug, existsSync: existsSync14 } = options;
|
|
16138
|
-
const dir =
|
|
16932
|
+
const dir = path7.dirname(originalPath);
|
|
16139
16933
|
const base = `${newSlug}.md`;
|
|
16140
|
-
let candidate =
|
|
16934
|
+
let candidate = path7.join(dir, base);
|
|
16141
16935
|
let counter = 2;
|
|
16142
16936
|
while (candidate !== originalPath && existsSync14(candidate)) {
|
|
16143
|
-
candidate =
|
|
16937
|
+
candidate = path7.join(dir, `${newSlug}-${counter}.md`);
|
|
16144
16938
|
counter += 1;
|
|
16145
16939
|
}
|
|
16146
16940
|
return candidate;
|
|
@@ -16691,18 +17485,20 @@ function toClaudeSdkMcpConfig(config) {
|
|
|
16691
17485
|
url: config.url,
|
|
16692
17486
|
headers: config.headers
|
|
16693
17487
|
};
|
|
17488
|
+
case "sdk":
|
|
17489
|
+
return config.config;
|
|
16694
17490
|
}
|
|
16695
17491
|
}
|
|
16696
17492
|
var homeMcpServersCache = null;
|
|
16697
17493
|
function loadHomeMcpServers(logger) {
|
|
16698
17494
|
try {
|
|
16699
17495
|
const home = process.env.HOME ?? os2.homedir();
|
|
16700
|
-
const filePath =
|
|
16701
|
-
const stat5 =
|
|
17496
|
+
const filePath = path8.join(home, ".claude.json");
|
|
17497
|
+
const stat5 = fs3.statSync(filePath);
|
|
16702
17498
|
if (homeMcpServersCache && homeMcpServersCache.mtimeMs === stat5.mtimeMs) {
|
|
16703
17499
|
return homeMcpServersCache.servers;
|
|
16704
17500
|
}
|
|
16705
|
-
const raw =
|
|
17501
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
16706
17502
|
const parsed = JSON.parse(raw);
|
|
16707
17503
|
if (!isMetadata(parsed)) return null;
|
|
16708
17504
|
const rawServers = parsed.mcpServers;
|
|
@@ -17049,8 +17845,8 @@ var ClaudeAgentClient = class {
|
|
|
17049
17845
|
return getClaudeModels();
|
|
17050
17846
|
}
|
|
17051
17847
|
async listPersistedAgents(options) {
|
|
17052
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR ??
|
|
17053
|
-
const projectsRoot =
|
|
17848
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path8.join(os2.homedir(), ".claude");
|
|
17849
|
+
const projectsRoot = path8.join(configDir, "projects");
|
|
17054
17850
|
if (!await pathExists2(projectsRoot)) {
|
|
17055
17851
|
return [];
|
|
17056
17852
|
}
|
|
@@ -17271,9 +18067,10 @@ var ClaudeAgentSession = class {
|
|
|
17271
18067
|
this.userMessageIds = [];
|
|
17272
18068
|
this.recentStderr = "";
|
|
17273
18069
|
this.closed = false;
|
|
18070
|
+
this.fastModeEnabled = false;
|
|
17274
18071
|
this.handlePermissionRequest = async (toolName, input, options) => {
|
|
17275
18072
|
this.maybeRewritePlanFilename(toolName, input);
|
|
17276
|
-
const requestId = `permission-${
|
|
18073
|
+
const requestId = `permission-${randomUUID2()}`;
|
|
17277
18074
|
const kind = resolvePermissionKind(toolName, input);
|
|
17278
18075
|
if (kind === "tool") {
|
|
17279
18076
|
if (this.currentMode === "bypassPermissions") {
|
|
@@ -17369,10 +18166,17 @@ var ClaudeAgentSession = class {
|
|
|
17369
18166
|
if (this.currentMode !== "plan") {
|
|
17370
18167
|
this.planResumeMode = this.currentMode;
|
|
17371
18168
|
}
|
|
18169
|
+
this.fastModeEnabled = Boolean(config.featureValues?.fast_mode);
|
|
17372
18170
|
}
|
|
17373
18171
|
get id() {
|
|
17374
18172
|
return this.claudeSessionId;
|
|
17375
18173
|
}
|
|
18174
|
+
get features() {
|
|
18175
|
+
return buildClaudeFeatures({
|
|
18176
|
+
modelId: this.config.model,
|
|
18177
|
+
fastModeEnabled: this.fastModeEnabled
|
|
18178
|
+
});
|
|
18179
|
+
}
|
|
17376
18180
|
async getRuntimeInfo() {
|
|
17377
18181
|
if (this.cachedRuntimeInfo) {
|
|
17378
18182
|
return { ...this.cachedRuntimeInfo };
|
|
@@ -17611,6 +18415,41 @@ var ClaudeAgentSession = class {
|
|
|
17611
18415
|
}
|
|
17612
18416
|
this.queryRestartNeeded = true;
|
|
17613
18417
|
}
|
|
18418
|
+
async setFeature(featureId, value) {
|
|
18419
|
+
if (featureId === "fast_mode") {
|
|
18420
|
+
const next = Boolean(value);
|
|
18421
|
+
if (next === this.fastModeEnabled) {
|
|
18422
|
+
return;
|
|
18423
|
+
}
|
|
18424
|
+
this.fastModeEnabled = next;
|
|
18425
|
+
this.config.featureValues = {
|
|
18426
|
+
...this.config.featureValues ?? {},
|
|
18427
|
+
fast_mode: next
|
|
18428
|
+
};
|
|
18429
|
+
this.cachedRuntimeInfo = null;
|
|
18430
|
+
if (this.query?.applyFlagSettings) {
|
|
18431
|
+
await this.query.applyFlagSettings({ fastMode: next });
|
|
18432
|
+
const queryWithGetSettings = this.query;
|
|
18433
|
+
if (typeof queryWithGetSettings.getSettings === "function") {
|
|
18434
|
+
try {
|
|
18435
|
+
const effective = await queryWithGetSettings.getSettings();
|
|
18436
|
+
const observedFastMode = effective && typeof effective === "object" && "fastMode" in effective && typeof effective.fastMode === "boolean" ? effective.fastMode : null;
|
|
18437
|
+
this.logger.info(
|
|
18438
|
+
{ featureId, requested: next, observed: observedFastMode },
|
|
18439
|
+
"claude: fast_mode read-back from SDK"
|
|
18440
|
+
);
|
|
18441
|
+
} catch (error) {
|
|
18442
|
+
this.logger.warn(
|
|
18443
|
+
{ err: error, featureId },
|
|
18444
|
+
"claude: fast_mode getSettings read-back failed"
|
|
18445
|
+
);
|
|
18446
|
+
}
|
|
18447
|
+
}
|
|
18448
|
+
}
|
|
18449
|
+
return;
|
|
18450
|
+
}
|
|
18451
|
+
throw new Error(`Unknown feature: ${featureId}`);
|
|
18452
|
+
}
|
|
17614
18453
|
getPendingPermissions() {
|
|
17615
18454
|
return Array.from(this.pendingPermissions.values()).map((entry) => entry.request);
|
|
17616
18455
|
}
|
|
@@ -17862,12 +18701,12 @@ var ClaudeAgentSession = class {
|
|
|
17862
18701
|
return [];
|
|
17863
18702
|
}
|
|
17864
18703
|
const historyPath = this.resolveHistoryPath(this.claudeSessionId);
|
|
17865
|
-
if (!historyPath || !
|
|
18704
|
+
if (!historyPath || !fs3.existsSync(historyPath)) {
|
|
17866
18705
|
return [];
|
|
17867
18706
|
}
|
|
17868
18707
|
try {
|
|
17869
18708
|
const ids = [];
|
|
17870
|
-
const content =
|
|
18709
|
+
const content = fs3.readFileSync(historyPath, "utf8");
|
|
17871
18710
|
for (const line of content.split(/\n+/)) {
|
|
17872
18711
|
const trimmed = line.trim();
|
|
17873
18712
|
if (!trimmed) continue;
|
|
@@ -18013,6 +18852,10 @@ var ClaudeAgentSession = class {
|
|
|
18013
18852
|
...this.claudeSessionId ? { resume: this.claudeSessionId } : {},
|
|
18014
18853
|
...thinking ? { thinking } : {},
|
|
18015
18854
|
...effort ? { effort } : {},
|
|
18855
|
+
// Initial-spawn path for fast mode: bake the flag into the new
|
|
18856
|
+
// process's flag-settings layer. Mid-session toggles go through
|
|
18857
|
+
// setFeature → query.applyFlagSettings instead (no restart needed).
|
|
18858
|
+
...this.fastModeEnabled ? { settings: { fastMode: true } } : {},
|
|
18016
18859
|
...this.config.extra?.claude
|
|
18017
18860
|
};
|
|
18018
18861
|
if (this.config.mcpServers) {
|
|
@@ -18060,7 +18903,9 @@ var ClaudeAgentSession = class {
|
|
|
18060
18903
|
if (Array.isArray(prompt)) {
|
|
18061
18904
|
for (const chunk of prompt) {
|
|
18062
18905
|
if (chunk.type === "text") {
|
|
18063
|
-
|
|
18906
|
+
if (chunk.text.length > 0) {
|
|
18907
|
+
content.push({ type: "text", text: chunk.text });
|
|
18908
|
+
}
|
|
18064
18909
|
} else if (chunk.type === "image") {
|
|
18065
18910
|
content.push({
|
|
18066
18911
|
type: "image",
|
|
@@ -18071,13 +18916,19 @@ var ClaudeAgentSession = class {
|
|
|
18071
18916
|
}
|
|
18072
18917
|
});
|
|
18073
18918
|
} else if (chunk.type === "github_pr" || chunk.type === "github_issue" || chunk.type === "gitlab_mr" || chunk.type === "gitlab_issue") {
|
|
18074
|
-
|
|
18919
|
+
const rendered = renderPromptAttachmentAsText(chunk);
|
|
18920
|
+
if (rendered.length > 0) {
|
|
18921
|
+
content.push({ type: "text", text: rendered });
|
|
18922
|
+
}
|
|
18075
18923
|
}
|
|
18076
18924
|
}
|
|
18077
|
-
} else {
|
|
18925
|
+
} else if (prompt.length > 0) {
|
|
18078
18926
|
content.push({ type: "text", text: prompt });
|
|
18079
18927
|
}
|
|
18080
|
-
|
|
18928
|
+
if (content.length === 0) {
|
|
18929
|
+
content.push({ type: "text", text: "(empty message)" });
|
|
18930
|
+
}
|
|
18931
|
+
const messageId = randomUUID2();
|
|
18081
18932
|
this.rememberUserMessageId(messageId);
|
|
18082
18933
|
return {
|
|
18083
18934
|
type: "user",
|
|
@@ -18610,6 +19461,43 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18610
19461
|
}
|
|
18611
19462
|
break;
|
|
18612
19463
|
}
|
|
19464
|
+
case "rate_limit_event": {
|
|
19465
|
+
const info = message.rate_limit_info;
|
|
19466
|
+
if (info && typeof info === "object") {
|
|
19467
|
+
const { rateLimitType, utilization, resetsAt } = info;
|
|
19468
|
+
const VALID_TYPES = /* @__PURE__ */ new Set([
|
|
19469
|
+
"five_hour",
|
|
19470
|
+
"seven_day",
|
|
19471
|
+
"seven_day_opus",
|
|
19472
|
+
"seven_day_sonnet",
|
|
19473
|
+
"overage"
|
|
19474
|
+
]);
|
|
19475
|
+
if (typeof rateLimitType === "string" && VALID_TYPES.has(rateLimitType) && typeof utilization === "number") {
|
|
19476
|
+
const rateLimit = {
|
|
19477
|
+
rateLimitType,
|
|
19478
|
+
utilization
|
|
19479
|
+
};
|
|
19480
|
+
if (typeof resetsAt === "number") {
|
|
19481
|
+
rateLimit.resetsAt = resetsAt;
|
|
19482
|
+
}
|
|
19483
|
+
const existingLimits = this.lastRateLimits ?? [];
|
|
19484
|
+
const updated = existingLimits.filter((l) => l.rateLimitType !== rateLimitType);
|
|
19485
|
+
updated.push(rateLimit);
|
|
19486
|
+
this.lastRateLimits = updated;
|
|
19487
|
+
const usage = {
|
|
19488
|
+
rateLimits: updated
|
|
19489
|
+
};
|
|
19490
|
+
if (this.lastContextWindowMaxTokens !== void 0) {
|
|
19491
|
+
usage.contextWindowMaxTokens = this.lastContextWindowMaxTokens;
|
|
19492
|
+
}
|
|
19493
|
+
if (this.lastContextWindowUsedTokens !== void 0) {
|
|
19494
|
+
usage.contextWindowUsedTokens = this.lastContextWindowUsedTokens;
|
|
19495
|
+
}
|
|
19496
|
+
events.push({ type: "usage_updated", provider: "claude", usage });
|
|
19497
|
+
}
|
|
19498
|
+
}
|
|
19499
|
+
break;
|
|
19500
|
+
}
|
|
18613
19501
|
default:
|
|
18614
19502
|
break;
|
|
18615
19503
|
}
|
|
@@ -18718,6 +19606,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18718
19606
|
outputTokens: message.usage.output_tokens,
|
|
18719
19607
|
totalCostUsd: message.total_cost_usd
|
|
18720
19608
|
};
|
|
19609
|
+
if (this.lastRateLimits !== void 0) {
|
|
19610
|
+
usage.rateLimits = this.lastRateLimits;
|
|
19611
|
+
}
|
|
18721
19612
|
const contextWindowMaxTokens = extractContextWindowSize(modelUsage ?? message.modelUsage);
|
|
18722
19613
|
if (contextWindowMaxTokens !== void 0) {
|
|
18723
19614
|
this.lastContextWindowMaxTokens = contextWindowMaxTokens;
|
|
@@ -18790,9 +19681,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18790
19681
|
if (typeof filePath !== "string" || typeof content !== "string") {
|
|
18791
19682
|
return;
|
|
18792
19683
|
}
|
|
18793
|
-
const dir =
|
|
18794
|
-
const dirName =
|
|
18795
|
-
const baseName =
|
|
19684
|
+
const dir = path8.dirname(filePath);
|
|
19685
|
+
const dirName = path8.basename(dir);
|
|
19686
|
+
const baseName = path8.basename(filePath);
|
|
18796
19687
|
if (dirName !== ".plans" || !baseName.endsWith(".md")) {
|
|
18797
19688
|
return;
|
|
18798
19689
|
}
|
|
@@ -18811,7 +19702,7 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18811
19702
|
const newPath = resolvePlanFilename({
|
|
18812
19703
|
originalPath: filePath,
|
|
18813
19704
|
newSlug: slug,
|
|
18814
|
-
existsSync:
|
|
19705
|
+
existsSync: fs3.existsSync
|
|
18815
19706
|
});
|
|
18816
19707
|
if (newPath === filePath) {
|
|
18817
19708
|
return;
|
|
@@ -18899,10 +19790,10 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18899
19790
|
loadPersistedHistory(sessionId) {
|
|
18900
19791
|
try {
|
|
18901
19792
|
const historyPath = this.resolveHistoryPath(sessionId);
|
|
18902
|
-
if (!historyPath || !
|
|
19793
|
+
if (!historyPath || !fs3.existsSync(historyPath)) {
|
|
18903
19794
|
return;
|
|
18904
19795
|
}
|
|
18905
|
-
this.ingestPersistedHistory(
|
|
19796
|
+
this.ingestPersistedHistory(fs3.readFileSync(historyPath, "utf8"));
|
|
18906
19797
|
} catch (error) {
|
|
18907
19798
|
}
|
|
18908
19799
|
}
|
|
@@ -18945,9 +19836,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
18945
19836
|
const cwd = this.config.cwd;
|
|
18946
19837
|
if (!cwd) return null;
|
|
18947
19838
|
const sanitized = sanitizeClaudeProjectPath(cwd);
|
|
18948
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR ??
|
|
18949
|
-
const dir =
|
|
18950
|
-
return
|
|
19839
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path8.join(os2.homedir(), ".claude");
|
|
19840
|
+
const dir = path8.join(configDir, "projects", sanitized);
|
|
19841
|
+
return path8.join(dir, `${sessionId}.jsonl`);
|
|
18951
19842
|
}
|
|
18952
19843
|
convertHistoryEntry(entry) {
|
|
18953
19844
|
return convertClaudeHistoryEntry(entry, (content) => this.mapBlocksToTimeline(content));
|
|
@@ -19087,10 +19978,10 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
19087
19978
|
return void 0;
|
|
19088
19979
|
}
|
|
19089
19980
|
const server = entry?.server ?? block.server ?? "tool";
|
|
19090
|
-
const
|
|
19981
|
+
const tool2 = entry?.name ?? block.tool_name ?? "tool";
|
|
19091
19982
|
const content = coerceToolResultContentToString(block.content);
|
|
19092
19983
|
const input = entry?.input;
|
|
19093
|
-
const structured = this.buildStructuredToolResult(server,
|
|
19984
|
+
const structured = this.buildStructuredToolResult(server, tool2, content, input);
|
|
19094
19985
|
if (structured) {
|
|
19095
19986
|
return structured;
|
|
19096
19987
|
}
|
|
@@ -19107,9 +19998,9 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
19107
19998
|
}
|
|
19108
19999
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
19109
20000
|
}
|
|
19110
|
-
buildStructuredToolResult(server,
|
|
20001
|
+
buildStructuredToolResult(server, tool2, output, input) {
|
|
19111
20002
|
const normalizedServer = server.toLowerCase();
|
|
19112
|
-
const normalizedTool =
|
|
20003
|
+
const normalizedTool = tool2.toLowerCase();
|
|
19113
20004
|
if (normalizedServer.includes("bash") || normalizedServer.includes("shell") || normalizedServer.includes("command") || normalizedTool.includes("bash") || normalizedTool.includes("shell") || normalizedTool.includes("command") || input && (typeof input.command === "string" || Array.isArray(input.command))) {
|
|
19114
20005
|
const command = this.extractCommandText(input ?? {}) ?? "command";
|
|
19115
20006
|
return {
|
|
@@ -19409,7 +20300,7 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
19409
20300
|
}
|
|
19410
20301
|
detectFileKind(filePath) {
|
|
19411
20302
|
try {
|
|
19412
|
-
return
|
|
20303
|
+
return fs3.existsSync(filePath) ? "update" : "add";
|
|
19413
20304
|
} catch {
|
|
19414
20305
|
return "update";
|
|
19415
20306
|
}
|
|
@@ -19420,8 +20311,8 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
19420
20311
|
}
|
|
19421
20312
|
const cwd = this.config.cwd;
|
|
19422
20313
|
if (cwd && target.startsWith(cwd)) {
|
|
19423
|
-
const relative =
|
|
19424
|
-
return relative.length > 0 ? relative :
|
|
20314
|
+
const relative = path8.relative(cwd, target);
|
|
20315
|
+
return relative.length > 0 ? relative : path8.basename(target);
|
|
19425
20316
|
}
|
|
19426
20317
|
return target;
|
|
19427
20318
|
}
|
|
@@ -19610,7 +20501,7 @@ async function collectRecentClaudeSessions(root, limit) {
|
|
|
19610
20501
|
}
|
|
19611
20502
|
const candidates = [];
|
|
19612
20503
|
for (const dirName of projectDirs) {
|
|
19613
|
-
const projectPath =
|
|
20504
|
+
const projectPath = path8.join(root, dirName);
|
|
19614
20505
|
let stats;
|
|
19615
20506
|
try {
|
|
19616
20507
|
stats = await fsPromises.stat(projectPath);
|
|
@@ -19630,7 +20521,7 @@ async function collectRecentClaudeSessions(root, limit) {
|
|
|
19630
20521
|
if (!file.endsWith(".jsonl")) {
|
|
19631
20522
|
continue;
|
|
19632
20523
|
}
|
|
19633
|
-
const fullPath =
|
|
20524
|
+
const fullPath = path8.join(projectPath, file);
|
|
19634
20525
|
try {
|
|
19635
20526
|
const fileStats = await fsPromises.stat(fullPath);
|
|
19636
20527
|
candidates.push({ path: fullPath, mtime: fileStats.mtime });
|
|
@@ -19805,17 +20696,17 @@ function unescapePartialJsonString(buffer, start) {
|
|
|
19805
20696
|
|
|
19806
20697
|
// ../server/src/server/agent/providers/codex-app-server-agent.ts
|
|
19807
20698
|
import { execSync as execSync2 } from "node:child_process";
|
|
19808
|
-
import { randomUUID as
|
|
19809
|
-
import
|
|
20699
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
20700
|
+
import fs5 from "node:fs/promises";
|
|
19810
20701
|
import os4 from "node:os";
|
|
19811
|
-
import
|
|
20702
|
+
import path10 from "node:path";
|
|
19812
20703
|
import readline from "node:readline";
|
|
19813
20704
|
import { z as z25 } from "zod";
|
|
19814
20705
|
|
|
19815
20706
|
// ../server/src/server/agent/providers/codex-rollout-timeline.ts
|
|
19816
|
-
import { promises as
|
|
20707
|
+
import { promises as fs4 } from "node:fs";
|
|
19817
20708
|
import os3 from "node:os";
|
|
19818
|
-
import
|
|
20709
|
+
import path9 from "node:path";
|
|
19819
20710
|
import { z as z24 } from "zod";
|
|
19820
20711
|
|
|
19821
20712
|
// ../server/src/server/agent/providers/codex/tool-call-mapper.ts
|
|
@@ -20267,14 +21158,14 @@ function codexApplyPatchToUnifiedDiff(text) {
|
|
|
20267
21158
|
for (const line of lines) {
|
|
20268
21159
|
const directive = parseCodexApplyPatchDirective(line);
|
|
20269
21160
|
if (directive) {
|
|
20270
|
-
const
|
|
20271
|
-
if (
|
|
21161
|
+
const path26 = normalizeDiffHeaderPath(directive.path);
|
|
21162
|
+
if (path26.length > 0) {
|
|
20272
21163
|
if (output.length > 0 && output[output.length - 1] !== "") {
|
|
20273
21164
|
output.push("");
|
|
20274
21165
|
}
|
|
20275
|
-
const left = directive.kind === "add" ? "/dev/null" : `a/${
|
|
20276
|
-
const right = directive.kind === "delete" ? "/dev/null" : `b/${
|
|
20277
|
-
output.push(`diff --git a/${
|
|
21166
|
+
const left = directive.kind === "add" ? "/dev/null" : `a/${path26}`;
|
|
21167
|
+
const right = directive.kind === "delete" ? "/dev/null" : `b/${path26}`;
|
|
21168
|
+
output.push(`diff --git a/${path26} b/${path26}`);
|
|
20278
21169
|
output.push(`--- ${left}`);
|
|
20279
21170
|
output.push(`+++ ${right}`);
|
|
20280
21171
|
sawDiffContent = true;
|
|
@@ -20344,9 +21235,9 @@ function asEditTextFields(text) {
|
|
|
20344
21235
|
function normalizeRolloutEditInput(input) {
|
|
20345
21236
|
if (typeof input === "string") {
|
|
20346
21237
|
const textFields2 = asEditTextFields(input);
|
|
20347
|
-
const
|
|
21238
|
+
const path26 = extractPatchPrimaryFilePath(input);
|
|
20348
21239
|
return {
|
|
20349
|
-
...
|
|
21240
|
+
...path26 ? { path: path26 } : {},
|
|
20350
21241
|
...textFields2.unifiedDiff ? { patch: textFields2.unifiedDiff } : {},
|
|
20351
21242
|
...textFields2.newString ? { content: textFields2.newString } : {}
|
|
20352
21243
|
};
|
|
@@ -20423,8 +21314,8 @@ function resolveStatus(rawStatus, error, output) {
|
|
|
20423
21314
|
}
|
|
20424
21315
|
return output !== null && output !== void 0 ? "completed" : "running";
|
|
20425
21316
|
}
|
|
20426
|
-
function buildMcpToolName(server,
|
|
20427
|
-
const trimmedTool =
|
|
21317
|
+
function buildMcpToolName(server, tool2) {
|
|
21318
|
+
const trimmedTool = tool2.trim();
|
|
20428
21319
|
if (!trimmedTool) {
|
|
20429
21320
|
return "tool";
|
|
20430
21321
|
}
|
|
@@ -20494,12 +21385,12 @@ function parseFileChangeDiff(entry) {
|
|
|
20494
21385
|
]);
|
|
20495
21386
|
}
|
|
20496
21387
|
function toFileChangeEntry(entry, options, fallbackPath) {
|
|
20497
|
-
const
|
|
20498
|
-
if (!
|
|
21388
|
+
const path26 = parseFileChangePath(entry, options, fallbackPath);
|
|
21389
|
+
if (!path26) {
|
|
20499
21390
|
return null;
|
|
20500
21391
|
}
|
|
20501
21392
|
return {
|
|
20502
|
-
path:
|
|
21393
|
+
path: path26,
|
|
20503
21394
|
kind: parseFileChangeKind(entry),
|
|
20504
21395
|
diff: parseFileChangeDiff(entry)
|
|
20505
21396
|
};
|
|
@@ -20521,12 +21412,12 @@ function parseFileChangeEntries(changes, options) {
|
|
|
20521
21412
|
if (singleEntry) {
|
|
20522
21413
|
return [singleEntry];
|
|
20523
21414
|
}
|
|
20524
|
-
return Object.entries(changes).map(([
|
|
21415
|
+
return Object.entries(changes).map(([path26, value]) => {
|
|
20525
21416
|
if (isRecord2(value)) {
|
|
20526
|
-
return toFileChangeEntry(value, options,
|
|
21417
|
+
return toFileChangeEntry(value, options, path26);
|
|
20527
21418
|
}
|
|
20528
21419
|
if (typeof value === "string") {
|
|
20529
|
-
const normalizedPath = normalizeCodexFilePath(
|
|
21420
|
+
const normalizedPath = normalizeCodexFilePath(path26.trim(), options?.cwd);
|
|
20530
21421
|
if (!normalizedPath) {
|
|
20531
21422
|
return null;
|
|
20532
21423
|
}
|
|
@@ -20594,11 +21485,11 @@ function mapFileChangeItem(item, options) {
|
|
|
20594
21485
|
};
|
|
20595
21486
|
}
|
|
20596
21487
|
function mapMcpToolCallItem(item, options) {
|
|
20597
|
-
const
|
|
20598
|
-
if (!
|
|
21488
|
+
const tool2 = item.tool.trim();
|
|
21489
|
+
if (!tool2) {
|
|
20599
21490
|
return null;
|
|
20600
21491
|
}
|
|
20601
|
-
const name = buildMcpToolName(item.server,
|
|
21492
|
+
const name = buildMcpToolName(item.server, tool2);
|
|
20602
21493
|
const input = item.arguments ?? null;
|
|
20603
21494
|
const output = item.result ?? null;
|
|
20604
21495
|
const error = item.error ?? null;
|
|
@@ -20688,8 +21579,8 @@ function resolveCodexSessionRoot() {
|
|
|
20688
21579
|
if (process.env.CODEX_SESSION_DIR) {
|
|
20689
21580
|
return process.env.CODEX_SESSION_DIR;
|
|
20690
21581
|
}
|
|
20691
|
-
const codexHome = process.env.CODEX_HOME ??
|
|
20692
|
-
return
|
|
21582
|
+
const codexHome = process.env.CODEX_HOME ?? path9.join(os3.homedir(), ".codex");
|
|
21583
|
+
return path9.join(codexHome, "sessions");
|
|
20693
21584
|
}
|
|
20694
21585
|
async function findRolloutFile(threadId, root) {
|
|
20695
21586
|
const stack = [{ dir: root, depth: 0 }];
|
|
@@ -20697,12 +21588,12 @@ async function findRolloutFile(threadId, root) {
|
|
|
20697
21588
|
const { dir, depth } = stack.pop();
|
|
20698
21589
|
let entries;
|
|
20699
21590
|
try {
|
|
20700
|
-
entries = await
|
|
21591
|
+
entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
20701
21592
|
} catch {
|
|
20702
21593
|
continue;
|
|
20703
21594
|
}
|
|
20704
21595
|
for (const entry of entries) {
|
|
20705
|
-
const entryPath =
|
|
21596
|
+
const entryPath = path9.join(dir, entry.name);
|
|
20706
21597
|
if (entry.isFile()) {
|
|
20707
21598
|
const matchesThread = entry.name.includes(threadId);
|
|
20708
21599
|
const matchesPrefix = entry.name.startsWith("rollout-");
|
|
@@ -21062,7 +21953,7 @@ function dedupeMirroredTextTimelineItems(timeline) {
|
|
|
21062
21953
|
return deduped;
|
|
21063
21954
|
}
|
|
21064
21955
|
async function parseRolloutFile(filePath) {
|
|
21065
|
-
const content = await
|
|
21956
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
21066
21957
|
const trimmed = content.trim();
|
|
21067
21958
|
if (!trimmed) {
|
|
21068
21959
|
return [];
|
|
@@ -21115,7 +22006,7 @@ async function loadCodexPersistedTimeline(sessionId, options, logger) {
|
|
|
21115
22006
|
const rolloutPath = options?.rolloutPath ?? null;
|
|
21116
22007
|
if (rolloutPath) {
|
|
21117
22008
|
try {
|
|
21118
|
-
const stat5 = await
|
|
22009
|
+
const stat5 = await fs4.stat(rolloutPath);
|
|
21119
22010
|
if (stat5.isFile()) {
|
|
21120
22011
|
const timeline = await parseRolloutFile(rolloutPath);
|
|
21121
22012
|
if (timeline.length > 0) {
|
|
@@ -21271,16 +22162,16 @@ function isObjectSchemaNode(schema) {
|
|
|
21271
22162
|
const type = schema.type;
|
|
21272
22163
|
return isSchemaRecord(schema.properties) || type === "object" || Array.isArray(type) && type.includes("object");
|
|
21273
22164
|
}
|
|
21274
|
-
function normalizeCodexOutputSchemaNode(schema,
|
|
22165
|
+
function normalizeCodexOutputSchemaNode(schema, path26) {
|
|
21275
22166
|
if (Array.isArray(schema)) {
|
|
21276
|
-
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${
|
|
22167
|
+
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${path26}[${index}]`));
|
|
21277
22168
|
}
|
|
21278
22169
|
if (!isSchemaRecord(schema)) {
|
|
21279
22170
|
return schema;
|
|
21280
22171
|
}
|
|
21281
22172
|
const normalized = {};
|
|
21282
22173
|
for (const [key, value] of Object.entries(schema)) {
|
|
21283
|
-
normalized[key] = normalizeCodexOutputSchemaNode(value, `${
|
|
22174
|
+
normalized[key] = normalizeCodexOutputSchemaNode(value, `${path26}.${key}`);
|
|
21284
22175
|
}
|
|
21285
22176
|
if (!isObjectSchemaNode(normalized)) {
|
|
21286
22177
|
return normalized;
|
|
@@ -21289,7 +22180,7 @@ function normalizeCodexOutputSchemaNode(schema, path24) {
|
|
|
21289
22180
|
normalized.additionalProperties = false;
|
|
21290
22181
|
} else if (normalized.additionalProperties !== false) {
|
|
21291
22182
|
throw new Error(
|
|
21292
|
-
`Codex structured outputs require ${
|
|
22183
|
+
`Codex structured outputs require ${path26} to set additionalProperties to false for object schemas.`
|
|
21293
22184
|
);
|
|
21294
22185
|
}
|
|
21295
22186
|
const properties = isSchemaRecord(normalized.properties) ? normalized.properties : null;
|
|
@@ -21330,7 +22221,7 @@ async function resolveCodexLaunchPrefix(runtimeSettings) {
|
|
|
21330
22221
|
return resolveProviderCommandPrefix(runtimeSettings?.command, resolveCodexBinary);
|
|
21331
22222
|
}
|
|
21332
22223
|
function resolveCodexHomeDir() {
|
|
21333
|
-
return process.env.CODEX_HOME ??
|
|
22224
|
+
return process.env.CODEX_HOME ?? path10.join(os4.homedir(), ".codex");
|
|
21334
22225
|
}
|
|
21335
22226
|
function tokenizeCommandArgs(args) {
|
|
21336
22227
|
const tokens = [];
|
|
@@ -21410,10 +22301,10 @@ function parseFrontMatter(markdown) {
|
|
|
21410
22301
|
}
|
|
21411
22302
|
async function listCodexCustomPrompts() {
|
|
21412
22303
|
const codexHome = resolveCodexHomeDir();
|
|
21413
|
-
const promptsDir =
|
|
22304
|
+
const promptsDir = path10.join(codexHome, "prompts");
|
|
21414
22305
|
let entries;
|
|
21415
22306
|
try {
|
|
21416
|
-
entries = await
|
|
22307
|
+
entries = await fs5.readdir(promptsDir, { withFileTypes: true });
|
|
21417
22308
|
} catch {
|
|
21418
22309
|
return [];
|
|
21419
22310
|
}
|
|
@@ -21429,10 +22320,10 @@ async function listCodexCustomPrompts() {
|
|
|
21429
22320
|
if (!name) {
|
|
21430
22321
|
continue;
|
|
21431
22322
|
}
|
|
21432
|
-
const fullPath =
|
|
22323
|
+
const fullPath = path10.join(promptsDir, entry.name);
|
|
21433
22324
|
let content;
|
|
21434
22325
|
try {
|
|
21435
|
-
content = await
|
|
22326
|
+
content = await fs5.readFile(fullPath, "utf8");
|
|
21436
22327
|
} catch {
|
|
21437
22328
|
continue;
|
|
21438
22329
|
}
|
|
@@ -21449,7 +22340,7 @@ async function listCodexCustomPrompts() {
|
|
|
21449
22340
|
}
|
|
21450
22341
|
async function listCodexSkills(cwd) {
|
|
21451
22342
|
const candidates = [];
|
|
21452
|
-
candidates.push(
|
|
22343
|
+
candidates.push(path10.join(cwd, ".codex", "skills"));
|
|
21453
22344
|
const repoRoot = (() => {
|
|
21454
22345
|
try {
|
|
21455
22346
|
const output = execSync2("git rev-parse --show-toplevel", {
|
|
@@ -21464,15 +22355,15 @@ async function listCodexSkills(cwd) {
|
|
|
21464
22355
|
}
|
|
21465
22356
|
})();
|
|
21466
22357
|
if (repoRoot) {
|
|
21467
|
-
candidates.push(
|
|
21468
|
-
candidates.push(
|
|
22358
|
+
candidates.push(path10.join(path10.dirname(cwd), ".codex", "skills"));
|
|
22359
|
+
candidates.push(path10.join(repoRoot, ".codex", "skills"));
|
|
21469
22360
|
}
|
|
21470
|
-
candidates.push(
|
|
22361
|
+
candidates.push(path10.join(resolveCodexHomeDir(), "skills"));
|
|
21471
22362
|
const commandsByName = /* @__PURE__ */ new Map();
|
|
21472
22363
|
for (const dir of candidates) {
|
|
21473
22364
|
let entries;
|
|
21474
22365
|
try {
|
|
21475
|
-
entries = await
|
|
22366
|
+
entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
21476
22367
|
} catch {
|
|
21477
22368
|
continue;
|
|
21478
22369
|
}
|
|
@@ -21480,11 +22371,11 @@ async function listCodexSkills(cwd) {
|
|
|
21480
22371
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
21481
22372
|
continue;
|
|
21482
22373
|
}
|
|
21483
|
-
const skillDir =
|
|
21484
|
-
const skillPath =
|
|
22374
|
+
const skillDir = path10.join(dir, entry.name);
|
|
22375
|
+
const skillPath = path10.join(skillDir, "SKILL.md");
|
|
21485
22376
|
let content;
|
|
21486
22377
|
try {
|
|
21487
|
-
content = await
|
|
22378
|
+
content = await fs5.readFile(skillPath, "utf8");
|
|
21488
22379
|
} catch {
|
|
21489
22380
|
continue;
|
|
21490
22381
|
}
|
|
@@ -21559,6 +22450,8 @@ function toCodexMcpConfig(config) {
|
|
|
21559
22450
|
url: config.url,
|
|
21560
22451
|
http_headers: config.headers
|
|
21561
22452
|
};
|
|
22453
|
+
case "sdk":
|
|
22454
|
+
return null;
|
|
21562
22455
|
}
|
|
21563
22456
|
}
|
|
21564
22457
|
var CodexAppServerClient = class {
|
|
@@ -22051,8 +22944,8 @@ function parseCodexPatchChanges(changes) {
|
|
|
22051
22944
|
}
|
|
22052
22945
|
];
|
|
22053
22946
|
}
|
|
22054
|
-
return Object.entries(recordChanges).map(([
|
|
22055
|
-
const normalizedPath =
|
|
22947
|
+
return Object.entries(recordChanges).map(([path26, value]) => {
|
|
22948
|
+
const normalizedPath = path26.trim();
|
|
22056
22949
|
if (!normalizedPath) {
|
|
22057
22950
|
return null;
|
|
22058
22951
|
}
|
|
@@ -22790,13 +23683,13 @@ var CodexNotificationSchema = z25.union([
|
|
|
22790
23683
|
)
|
|
22791
23684
|
]);
|
|
22792
23685
|
async function writeImageAttachment(mimeType, data) {
|
|
22793
|
-
const attachmentsDir =
|
|
22794
|
-
await
|
|
23686
|
+
const attachmentsDir = path10.join(os4.tmpdir(), CODEX_IMAGE_ATTACHMENT_DIR);
|
|
23687
|
+
await fs5.mkdir(attachmentsDir, { recursive: true });
|
|
22795
23688
|
const normalized = normalizeImageData(mimeType, data);
|
|
22796
23689
|
const extension = getImageExtension(normalized.mimeType);
|
|
22797
|
-
const filename = `${
|
|
22798
|
-
const filePath =
|
|
22799
|
-
await
|
|
23690
|
+
const filename = `${randomUUID3()}.${extension}`;
|
|
23691
|
+
const filePath = path10.join(attachmentsDir, filename);
|
|
23692
|
+
await fs5.writeFile(filePath, Buffer.from(normalized.data, "base64"));
|
|
22800
23693
|
return filePath;
|
|
22801
23694
|
}
|
|
22802
23695
|
async function readCodexConfiguredDefaults(client, logger) {
|
|
@@ -23076,7 +23969,7 @@ var CodexAppServerAgentSession = class {
|
|
|
23076
23969
|
};
|
|
23077
23970
|
}
|
|
23078
23971
|
emitSyntheticPlanApprovalRequest(planText) {
|
|
23079
|
-
const requestId = `permission-${
|
|
23972
|
+
const requestId = `permission-${randomUUID3()}`;
|
|
23080
23973
|
const request = {
|
|
23081
23974
|
id: requestId,
|
|
23082
23975
|
provider: CODEX_PROVIDER,
|
|
@@ -23233,8 +24126,8 @@ var CodexAppServerAgentSession = class {
|
|
|
23233
24126
|
if (commandName.startsWith("prompts:")) {
|
|
23234
24127
|
const promptName = commandName.slice("prompts:".length);
|
|
23235
24128
|
const codexHome = resolveCodexHomeDir();
|
|
23236
|
-
const promptPath =
|
|
23237
|
-
const raw = await
|
|
24129
|
+
const promptPath = path10.join(codexHome, "prompts", `${promptName}.md`);
|
|
24130
|
+
const raw = await fs5.readFile(promptPath, "utf8");
|
|
23238
24131
|
const parsed = parseFrontMatter(raw);
|
|
23239
24132
|
return expandCodexCustomPrompt(parsed.body, args);
|
|
23240
24133
|
}
|
|
@@ -23701,7 +24594,8 @@ var CodexAppServerAgentSession = class {
|
|
|
23701
24594
|
if (this.config.mcpServers) {
|
|
23702
24595
|
const mcpServers = {};
|
|
23703
24596
|
for (const [name, serverConfig] of Object.entries(this.config.mcpServers)) {
|
|
23704
|
-
|
|
24597
|
+
const codexConfig = toCodexMcpConfig(serverConfig);
|
|
24598
|
+
if (codexConfig) mcpServers[name] = codexConfig;
|
|
23705
24599
|
}
|
|
23706
24600
|
innerConfig.mcp_servers = mcpServers;
|
|
23707
24601
|
}
|
|
@@ -24465,9 +25359,9 @@ var CodexAppServerAgentClient = class {
|
|
|
24465
25359
|
};
|
|
24466
25360
|
|
|
24467
25361
|
// ../server/src/server/agent/providers/acp-agent.ts
|
|
24468
|
-
import { randomUUID as
|
|
24469
|
-
import
|
|
24470
|
-
import
|
|
25362
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
25363
|
+
import fs6 from "node:fs/promises";
|
|
25364
|
+
import path11 from "node:path";
|
|
24471
25365
|
import { Readable, Writable } from "node:stream";
|
|
24472
25366
|
import {
|
|
24473
25367
|
ClientSideConnection,
|
|
@@ -24764,12 +25658,12 @@ ${stderr}` : String(error)));
|
|
|
24764
25658
|
async sessionUpdate() {
|
|
24765
25659
|
},
|
|
24766
25660
|
async readTextFile(params) {
|
|
24767
|
-
const content = await
|
|
25661
|
+
const content = await fs6.readFile(params.path, "utf8");
|
|
24768
25662
|
return { content };
|
|
24769
25663
|
},
|
|
24770
25664
|
async writeTextFile(params) {
|
|
24771
|
-
await
|
|
24772
|
-
await
|
|
25665
|
+
await fs6.mkdir(path11.dirname(params.path), { recursive: true });
|
|
25666
|
+
await fs6.writeFile(params.path, params.content, "utf8");
|
|
24773
25667
|
return {};
|
|
24774
25668
|
},
|
|
24775
25669
|
async createTerminal() {
|
|
@@ -24991,8 +25885,8 @@ var ACPAgentSession = class {
|
|
|
24991
25885
|
if (this.activeForegroundTurnId) {
|
|
24992
25886
|
throw new Error("A foreground turn is already active");
|
|
24993
25887
|
}
|
|
24994
|
-
const turnId =
|
|
24995
|
-
const messageId =
|
|
25888
|
+
const turnId = randomUUID4();
|
|
25889
|
+
const messageId = randomUUID4();
|
|
24996
25890
|
this.activeForegroundTurnId = turnId;
|
|
24997
25891
|
this.suppressUserEchoMessageId = messageId;
|
|
24998
25892
|
this.suppressUserEchoText = extractPromptText(prompt);
|
|
@@ -25293,7 +26187,7 @@ var ACPAgentSession = class {
|
|
|
25293
26187
|
}
|
|
25294
26188
|
} : { outcome: { outcome: "cancelled" } };
|
|
25295
26189
|
}
|
|
25296
|
-
const requestId =
|
|
26190
|
+
const requestId = randomUUID4();
|
|
25297
26191
|
let toolSnapshot = this.toolCalls.get(params.toolCall.toolCallId) ?? mergeToolSnapshot(params.toolCall.toolCallId, params.toolCall);
|
|
25298
26192
|
if (this.toolSnapshotTransformer) {
|
|
25299
26193
|
toolSnapshot = this.toolSnapshotTransformer(toolSnapshot);
|
|
@@ -25334,7 +26228,7 @@ var ACPAgentSession = class {
|
|
|
25334
26228
|
}
|
|
25335
26229
|
}
|
|
25336
26230
|
async readTextFile(params) {
|
|
25337
|
-
const raw = await
|
|
26231
|
+
const raw = await fs6.readFile(params.path, "utf8");
|
|
25338
26232
|
if (!params.line && !params.limit) {
|
|
25339
26233
|
return { content: raw };
|
|
25340
26234
|
}
|
|
@@ -25344,12 +26238,12 @@ var ACPAgentSession = class {
|
|
|
25344
26238
|
return { content: lines.slice(start, end).join("\n") };
|
|
25345
26239
|
}
|
|
25346
26240
|
async writeTextFile(params) {
|
|
25347
|
-
await
|
|
25348
|
-
await
|
|
26241
|
+
await fs6.mkdir(path11.dirname(params.path), { recursive: true });
|
|
26242
|
+
await fs6.writeFile(params.path, params.content, "utf8");
|
|
25349
26243
|
return {};
|
|
25350
26244
|
}
|
|
25351
26245
|
async createTerminal(params) {
|
|
25352
|
-
const terminalId =
|
|
26246
|
+
const terminalId = randomUUID4();
|
|
25353
26247
|
const env = Object.fromEntries(
|
|
25354
26248
|
(params.env ?? []).map((entry2) => [entry2.name, entry2.value])
|
|
25355
26249
|
);
|
|
@@ -25747,9 +26641,10 @@ function normalizeMcpServers(servers) {
|
|
|
25747
26641
|
if (!servers) {
|
|
25748
26642
|
return [];
|
|
25749
26643
|
}
|
|
25750
|
-
|
|
26644
|
+
const out = [];
|
|
26645
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
25751
26646
|
if (config.type === "stdio") {
|
|
25752
|
-
|
|
26647
|
+
out.push({
|
|
25753
26648
|
name,
|
|
25754
26649
|
command: config.command,
|
|
25755
26650
|
args: config.args ?? [],
|
|
@@ -25757,29 +26652,35 @@ function normalizeMcpServers(servers) {
|
|
|
25757
26652
|
name: envName,
|
|
25758
26653
|
value
|
|
25759
26654
|
}))
|
|
25760
|
-
};
|
|
26655
|
+
});
|
|
26656
|
+
continue;
|
|
25761
26657
|
}
|
|
25762
26658
|
if (config.type === "http") {
|
|
25763
|
-
|
|
26659
|
+
out.push({
|
|
25764
26660
|
type: "http",
|
|
25765
26661
|
name,
|
|
25766
26662
|
url: config.url,
|
|
25767
26663
|
headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
|
|
25768
26664
|
name: headerName,
|
|
25769
|
-
value
|
|
26665
|
+
value: String(value)
|
|
25770
26666
|
}))
|
|
25771
|
-
};
|
|
26667
|
+
});
|
|
26668
|
+
continue;
|
|
25772
26669
|
}
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
26670
|
+
if (config.type === "sse") {
|
|
26671
|
+
out.push({
|
|
26672
|
+
type: "sse",
|
|
26673
|
+
name,
|
|
26674
|
+
url: config.url,
|
|
26675
|
+
headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
|
|
26676
|
+
name: headerName,
|
|
26677
|
+
value: String(value)
|
|
26678
|
+
}))
|
|
26679
|
+
});
|
|
26680
|
+
continue;
|
|
26681
|
+
}
|
|
26682
|
+
}
|
|
26683
|
+
return out;
|
|
25783
26684
|
}
|
|
25784
26685
|
function toACPContentBlocks(prompt) {
|
|
25785
26686
|
if (typeof prompt === "string") {
|
|
@@ -26610,12 +27511,15 @@ function toOpenCodeMcpConfig(config) {
|
|
|
26610
27511
|
enabled: true
|
|
26611
27512
|
};
|
|
26612
27513
|
}
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
|
|
26616
|
-
|
|
26617
|
-
|
|
26618
|
-
|
|
27514
|
+
if (config.type === "http" || config.type === "sse") {
|
|
27515
|
+
return {
|
|
27516
|
+
type: "remote",
|
|
27517
|
+
url: config.url,
|
|
27518
|
+
...config.headers ? { headers: config.headers } : {},
|
|
27519
|
+
enabled: true
|
|
27520
|
+
};
|
|
27521
|
+
}
|
|
27522
|
+
return null;
|
|
26619
27523
|
}
|
|
26620
27524
|
function stringifyUnknownError(error) {
|
|
26621
27525
|
if (typeof error === "string") {
|
|
@@ -27558,7 +28462,7 @@ function translateOpenCodeEvent(event, state) {
|
|
|
27558
28462
|
break;
|
|
27559
28463
|
}
|
|
27560
28464
|
const metadata = readOpenCodeRecord(event.properties.metadata);
|
|
27561
|
-
const
|
|
28465
|
+
const tool2 = readOpenCodeRecord(event.properties.tool);
|
|
27562
28466
|
const patterns = Array.isArray(event.properties.patterns) ? event.properties.patterns.filter((value) => typeof value === "string") : [];
|
|
27563
28467
|
const command = readPermissionField(metadata, PERMISSION_COMMAND_KEYS);
|
|
27564
28468
|
const cwd = readPermissionField(metadata, PERMISSION_CWD_KEYS);
|
|
@@ -27566,7 +28470,7 @@ function translateOpenCodeEvent(event, state) {
|
|
|
27566
28470
|
const input = buildOpenCodePermissionInput({
|
|
27567
28471
|
patterns,
|
|
27568
28472
|
metadata,
|
|
27569
|
-
tool,
|
|
28473
|
+
tool: tool2,
|
|
27570
28474
|
command
|
|
27571
28475
|
});
|
|
27572
28476
|
const detail = buildOpenCodePermissionDetail({
|
|
@@ -28314,6 +29218,7 @@ var OpenCodeAgentSession = class {
|
|
|
28314
29218
|
async configureMcpServers(mcpServers) {
|
|
28315
29219
|
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
28316
29220
|
const mappedConfig = toOpenCodeMcpConfig(serverConfig);
|
|
29221
|
+
if (!mappedConfig) continue;
|
|
28317
29222
|
await this.registerMcpServer(name, mappedConfig);
|
|
28318
29223
|
}
|
|
28319
29224
|
}
|
|
@@ -28392,9 +29297,9 @@ var OpenCodeAgentSession = class {
|
|
|
28392
29297
|
};
|
|
28393
29298
|
|
|
28394
29299
|
// ../server/src/server/agent/providers/pi-direct-agent.ts
|
|
28395
|
-
import { randomUUID as
|
|
29300
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28396
29301
|
import { existsSync as existsSync9 } from "node:fs";
|
|
28397
|
-
import { join as
|
|
29302
|
+
import { join as join9 } from "node:path";
|
|
28398
29303
|
import { homedir } from "node:os";
|
|
28399
29304
|
import {
|
|
28400
29305
|
AuthStorage,
|
|
@@ -29165,7 +30070,7 @@ var PiDirectAgentSession = class {
|
|
|
29165
30070
|
throw new Error("A Pi turn is already active");
|
|
29166
30071
|
}
|
|
29167
30072
|
const payload = convertPromptInput(prompt);
|
|
29168
|
-
const turnId =
|
|
30073
|
+
const turnId = randomUUID5();
|
|
29169
30074
|
this.activeTurnId = turnId;
|
|
29170
30075
|
void this.session.prompt(payload.text, payload.images ? { images: payload.images } : void 0).catch((error) => {
|
|
29171
30076
|
const failedTurnId = this.activeTurnId ?? turnId;
|
|
@@ -29400,7 +30305,7 @@ var PiDirectAgentClient = class {
|
|
|
29400
30305
|
} else if (!await isCommandAvailable(PI_BINARY_COMMAND)) {
|
|
29401
30306
|
return false;
|
|
29402
30307
|
}
|
|
29403
|
-
return Boolean(process.env.OPENAI_API_KEY) || Boolean(process.env.ANTHROPIC_API_KEY) || Boolean(process.env.OPENROUTER_API_KEY) || existsSync9(
|
|
30308
|
+
return Boolean(process.env.OPENAI_API_KEY) || Boolean(process.env.ANTHROPIC_API_KEY) || Boolean(process.env.OPENROUTER_API_KEY) || existsSync9(join9(homedir(), ".pi", "agent", "auth.json"));
|
|
29404
30309
|
}
|
|
29405
30310
|
async getDiagnostic() {
|
|
29406
30311
|
try {
|
|
@@ -29408,7 +30313,7 @@ var PiDirectAgentClient = class {
|
|
|
29408
30313
|
const binaryOverride = this.runtimeSettings?.command;
|
|
29409
30314
|
const binary = binaryOverride?.mode === "replace" && binaryOverride.argv[0] ? binaryOverride.argv[0] : await findExecutable(PI_BINARY_COMMAND);
|
|
29410
30315
|
const version = binary ? await resolveBinaryVersion(binary) : "unknown";
|
|
29411
|
-
const authConfigPath =
|
|
30316
|
+
const authConfigPath = join9(homedir(), ".pi", "agent", "auth.json");
|
|
29412
30317
|
let modelsValue = "Not checked";
|
|
29413
30318
|
let status = formatDiagnosticStatus(available);
|
|
29414
30319
|
if (available) {
|
|
@@ -29801,8 +30706,8 @@ function buildZodValidator(schema, schemaName) {
|
|
|
29801
30706
|
return { ok: true, value: result.data };
|
|
29802
30707
|
}
|
|
29803
30708
|
const errors = result.error.issues.map((issue) => {
|
|
29804
|
-
const
|
|
29805
|
-
return `${
|
|
30709
|
+
const path26 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
30710
|
+
return `${path26}: ${issue.message}`;
|
|
29806
30711
|
});
|
|
29807
30712
|
return { ok: false, errors };
|
|
29808
30713
|
}
|
|
@@ -29820,9 +30725,9 @@ function buildJsonSchemaValidator(schema) {
|
|
|
29820
30725
|
return { ok: true, value };
|
|
29821
30726
|
}
|
|
29822
30727
|
const errors = (validate.errors ?? []).map((error) => {
|
|
29823
|
-
const
|
|
30728
|
+
const path26 = error.instancePath && error.instancePath.length > 0 ? error.instancePath : "(root)";
|
|
29824
30729
|
const message = error.message ?? "is invalid";
|
|
29825
|
-
return `${
|
|
30730
|
+
return `${path26}: ${message}`;
|
|
29826
30731
|
});
|
|
29827
30732
|
return { ok: false, errors };
|
|
29828
30733
|
}
|
|
@@ -30707,8 +31612,8 @@ function isVoicePermissionAllowed(request) {
|
|
|
30707
31612
|
}
|
|
30708
31613
|
|
|
30709
31614
|
// ../server/src/server/file-explorer/service.ts
|
|
30710
|
-
import { promises as
|
|
30711
|
-
import
|
|
31615
|
+
import { promises as fs7 } from "fs";
|
|
31616
|
+
import path12 from "path";
|
|
30712
31617
|
|
|
30713
31618
|
// ../server/src/server/path-utils.ts
|
|
30714
31619
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -30755,14 +31660,14 @@ async function listDirectoryEntries({
|
|
|
30755
31660
|
relativePath = "."
|
|
30756
31661
|
}) {
|
|
30757
31662
|
const directoryPath = await resolveScopedPath({ root, relativePath });
|
|
30758
|
-
const stats = await
|
|
31663
|
+
const stats = await fs7.stat(directoryPath);
|
|
30759
31664
|
if (!stats.isDirectory()) {
|
|
30760
31665
|
throw new Error("Requested path is not a directory");
|
|
30761
31666
|
}
|
|
30762
|
-
const dirents = await
|
|
31667
|
+
const dirents = await fs7.readdir(directoryPath, { withFileTypes: true });
|
|
30763
31668
|
const entriesWithNulls = await Promise.all(
|
|
30764
31669
|
dirents.map(async (dirent) => {
|
|
30765
|
-
const targetPath =
|
|
31670
|
+
const targetPath = path12.join(directoryPath, dirent.name);
|
|
30766
31671
|
const kind = dirent.isDirectory() ? "directory" : "file";
|
|
30767
31672
|
try {
|
|
30768
31673
|
return await buildEntryPayload({
|
|
@@ -30797,18 +31702,18 @@ async function readExplorerFile({
|
|
|
30797
31702
|
relativePath
|
|
30798
31703
|
}) {
|
|
30799
31704
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
30800
|
-
const stats = await
|
|
31705
|
+
const stats = await fs7.stat(filePath);
|
|
30801
31706
|
if (!stats.isFile()) {
|
|
30802
31707
|
throw new Error("Requested path is not a file");
|
|
30803
31708
|
}
|
|
30804
|
-
const ext =
|
|
31709
|
+
const ext = path12.extname(filePath).toLowerCase();
|
|
30805
31710
|
const basePayload = {
|
|
30806
31711
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
30807
31712
|
size: stats.size,
|
|
30808
31713
|
modifiedAt: stats.mtime.toISOString()
|
|
30809
31714
|
};
|
|
30810
31715
|
if (ext in IMAGE_MIME_TYPES) {
|
|
30811
|
-
const buffer2 = await
|
|
31716
|
+
const buffer2 = await fs7.readFile(filePath);
|
|
30812
31717
|
return {
|
|
30813
31718
|
...basePayload,
|
|
30814
31719
|
kind: "image",
|
|
@@ -30817,7 +31722,7 @@ async function readExplorerFile({
|
|
|
30817
31722
|
mimeType: IMAGE_MIME_TYPES[ext]
|
|
30818
31723
|
};
|
|
30819
31724
|
}
|
|
30820
|
-
const buffer = await
|
|
31725
|
+
const buffer = await fs7.readFile(filePath);
|
|
30821
31726
|
if (isLikelyBinary(buffer)) {
|
|
30822
31727
|
return {
|
|
30823
31728
|
...basePayload,
|
|
@@ -30839,34 +31744,34 @@ async function writeTextFile({
|
|
|
30839
31744
|
relativePath,
|
|
30840
31745
|
content
|
|
30841
31746
|
}) {
|
|
30842
|
-
const ext =
|
|
31747
|
+
const ext = path12.extname(relativePath).toLowerCase();
|
|
30843
31748
|
if (ext in IMAGE_MIME_TYPES) {
|
|
30844
31749
|
throw new Error(`Refusing to write '${relativePath}': binary/image file`);
|
|
30845
31750
|
}
|
|
30846
31751
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
30847
31752
|
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
30848
|
-
await
|
|
30849
|
-
await
|
|
31753
|
+
await fs7.writeFile(tempPath, content, "utf8");
|
|
31754
|
+
await fs7.rename(tempPath, filePath);
|
|
30850
31755
|
}
|
|
30851
31756
|
async function deleteFile({ root, relativePath }) {
|
|
30852
|
-
const ext =
|
|
31757
|
+
const ext = path12.extname(relativePath).toLowerCase();
|
|
30853
31758
|
if (ext !== ".md") {
|
|
30854
31759
|
throw new Error(`Refusing to delete '${relativePath}': only .md files allowed`);
|
|
30855
31760
|
}
|
|
30856
31761
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
30857
|
-
const stats = await
|
|
31762
|
+
const stats = await fs7.stat(filePath);
|
|
30858
31763
|
if (!stats.isFile()) {
|
|
30859
31764
|
throw new Error("Requested path is not a file");
|
|
30860
31765
|
}
|
|
30861
|
-
await
|
|
31766
|
+
await fs7.unlink(filePath);
|
|
30862
31767
|
}
|
|
30863
31768
|
async function deleteEntry({ root, relativePath }) {
|
|
30864
31769
|
const entryPath = await resolveScopedPath({ root, relativePath });
|
|
30865
|
-
await
|
|
31770
|
+
await fs7.rm(entryPath, { recursive: true, force: false });
|
|
30866
31771
|
}
|
|
30867
31772
|
async function createDirectory({ root, relativePath }) {
|
|
30868
31773
|
const dirPath = await resolveScopedPath({ root, relativePath });
|
|
30869
|
-
await
|
|
31774
|
+
await fs7.mkdir(dirPath, { recursive: true });
|
|
30870
31775
|
}
|
|
30871
31776
|
async function moveEntry({
|
|
30872
31777
|
root,
|
|
@@ -30875,22 +31780,22 @@ async function moveEntry({
|
|
|
30875
31780
|
}) {
|
|
30876
31781
|
const src = await resolveScopedPath({ root, relativePath: sourcePath });
|
|
30877
31782
|
const dest = await resolveScopedPath({ root, relativePath: destinationPath });
|
|
30878
|
-
await
|
|
31783
|
+
await fs7.rename(src, dest);
|
|
30879
31784
|
}
|
|
30880
31785
|
async function getDownloadableFileInfo({ root, relativePath }) {
|
|
30881
31786
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
30882
|
-
const stats = await
|
|
31787
|
+
const stats = await fs7.stat(filePath);
|
|
30883
31788
|
if (!stats.isFile()) {
|
|
30884
31789
|
throw new Error("Requested path is not a file");
|
|
30885
31790
|
}
|
|
30886
|
-
const ext =
|
|
31791
|
+
const ext = path12.extname(filePath).toLowerCase();
|
|
30887
31792
|
let mimeType = "application/octet-stream";
|
|
30888
31793
|
if (ext in IMAGE_MIME_TYPES) {
|
|
30889
31794
|
mimeType = IMAGE_MIME_TYPES[ext] ?? mimeType;
|
|
30890
31795
|
} else if (ext in FONT_MIME_TYPES) {
|
|
30891
31796
|
mimeType = FONT_MIME_TYPES[ext] ?? mimeType;
|
|
30892
31797
|
} else {
|
|
30893
|
-
const handle = await
|
|
31798
|
+
const handle = await fs7.open(filePath, "r");
|
|
30894
31799
|
const sample = Buffer.alloc(8192);
|
|
30895
31800
|
try {
|
|
30896
31801
|
const { bytesRead } = await handle.read(sample, 0, sample.length, 0);
|
|
@@ -30905,23 +31810,23 @@ async function getDownloadableFileInfo({ root, relativePath }) {
|
|
|
30905
31810
|
return {
|
|
30906
31811
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
30907
31812
|
absolutePath: filePath,
|
|
30908
|
-
fileName:
|
|
31813
|
+
fileName: path12.basename(filePath),
|
|
30909
31814
|
mimeType,
|
|
30910
31815
|
size: stats.size
|
|
30911
31816
|
};
|
|
30912
31817
|
}
|
|
30913
31818
|
async function resolveScopedPath({ root, relativePath = "." }) {
|
|
30914
|
-
const normalizedRoot =
|
|
31819
|
+
const normalizedRoot = path12.resolve(root);
|
|
30915
31820
|
const requestedPath = resolvePathFromBase(normalizedRoot, relativePath);
|
|
30916
|
-
const relative =
|
|
30917
|
-
if (relative !== "" && (relative.startsWith("..") ||
|
|
31821
|
+
const relative = path12.relative(normalizedRoot, requestedPath);
|
|
31822
|
+
if (relative !== "" && (relative.startsWith("..") || path12.isAbsolute(relative))) {
|
|
30918
31823
|
throw new Error("Access outside of workspace is not allowed");
|
|
30919
31824
|
}
|
|
30920
|
-
const realRoot = await
|
|
31825
|
+
const realRoot = await fs7.realpath(normalizedRoot);
|
|
30921
31826
|
try {
|
|
30922
|
-
const realPath = await
|
|
30923
|
-
const realRelative =
|
|
30924
|
-
if (realRelative !== "" && (realRelative.startsWith("..") ||
|
|
31827
|
+
const realPath = await fs7.realpath(requestedPath);
|
|
31828
|
+
const realRelative = path12.relative(realRoot, realPath);
|
|
31829
|
+
if (realRelative !== "" && (realRelative.startsWith("..") || path12.isAbsolute(realRelative))) {
|
|
30925
31830
|
throw new Error("Access outside of workspace is not allowed");
|
|
30926
31831
|
}
|
|
30927
31832
|
return requestedPath;
|
|
@@ -30938,7 +31843,7 @@ async function buildEntryPayload({
|
|
|
30938
31843
|
name,
|
|
30939
31844
|
kind
|
|
30940
31845
|
}) {
|
|
30941
|
-
const stats = await
|
|
31846
|
+
const stats = await fs7.stat(targetPath);
|
|
30942
31847
|
return {
|
|
30943
31848
|
name,
|
|
30944
31849
|
path: normalizeRelativePath({ root, targetPath }),
|
|
@@ -30952,10 +31857,10 @@ function isMissingEntryError(error) {
|
|
|
30952
31857
|
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
30953
31858
|
}
|
|
30954
31859
|
function normalizeRelativePath({ root, targetPath }) {
|
|
30955
|
-
const normalizedRoot =
|
|
30956
|
-
const normalizedTarget =
|
|
30957
|
-
const relative =
|
|
30958
|
-
return relative === "" ? "." : relative.split(
|
|
31860
|
+
const normalizedRoot = path12.resolve(root);
|
|
31861
|
+
const normalizedTarget = path12.resolve(targetPath);
|
|
31862
|
+
const relative = path12.relative(normalizedRoot, normalizedTarget);
|
|
31863
|
+
return relative === "" ? "." : relative.split(path12.sep).join("/");
|
|
30959
31864
|
}
|
|
30960
31865
|
function textMimeTypeForExtension(ext) {
|
|
30961
31866
|
return TEXT_MIME_TYPES[ext] ?? DEFAULT_TEXT_MIME_TYPE;
|
|
@@ -30982,7 +31887,7 @@ function isLikelyBinary(buffer) {
|
|
|
30982
31887
|
|
|
30983
31888
|
// ../server/src/utils/project-icon.ts
|
|
30984
31889
|
import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
30985
|
-
import { extname as
|
|
31890
|
+
import { extname as extname3, join as join10 } from "path";
|
|
30986
31891
|
var ICON_PATTERNS = [
|
|
30987
31892
|
"favicon.ico",
|
|
30988
31893
|
"favicon.png",
|
|
@@ -31096,7 +32001,7 @@ function isSquareImage(buffer, mimeType) {
|
|
|
31096
32001
|
return dimensions.width === dimensions.height;
|
|
31097
32002
|
}
|
|
31098
32003
|
function getMimeType(filename) {
|
|
31099
|
-
const ext =
|
|
32004
|
+
const ext = extname3(filename).toLowerCase();
|
|
31100
32005
|
switch (ext) {
|
|
31101
32006
|
case ".ico":
|
|
31102
32007
|
return "image/x-icon";
|
|
@@ -31132,7 +32037,7 @@ async function findIconInDir(dir, patterns) {
|
|
|
31132
32037
|
for (const pattern of patterns) {
|
|
31133
32038
|
for (const entry of entries) {
|
|
31134
32039
|
if (matchesPattern(entry, pattern)) {
|
|
31135
|
-
const fullPath =
|
|
32040
|
+
const fullPath = join10(dir, entry);
|
|
31136
32041
|
try {
|
|
31137
32042
|
const stats = await stat2(fullPath);
|
|
31138
32043
|
if (stats.isFile()) {
|
|
@@ -31163,7 +32068,7 @@ async function searchDirRecursively(dir, patterns, ignoredDirs, maxDepth, curren
|
|
|
31163
32068
|
if (ignoredDirs.has(entry)) {
|
|
31164
32069
|
continue;
|
|
31165
32070
|
}
|
|
31166
|
-
const fullPath =
|
|
32071
|
+
const fullPath = join10(dir, entry);
|
|
31167
32072
|
try {
|
|
31168
32073
|
const stats = await stat2(fullPath);
|
|
31169
32074
|
if (stats.isDirectory()) {
|
|
@@ -31186,7 +32091,7 @@ async function searchDirRecursively(dir, patterns, ignoredDirs, maxDepth, curren
|
|
|
31186
32091
|
async function findProjectIcon(projectDir, maxDepth = 3) {
|
|
31187
32092
|
const ignoredDirsSet = new Set(IGNORED_DIRS);
|
|
31188
32093
|
for (const priorityDir of PRIORITY_DIRS) {
|
|
31189
|
-
const priorityPath =
|
|
32094
|
+
const priorityPath = join10(projectDir, priorityDir);
|
|
31190
32095
|
try {
|
|
31191
32096
|
const stats = await stat2(priorityPath);
|
|
31192
32097
|
if (stats.isDirectory()) {
|
|
@@ -31204,7 +32109,7 @@ async function findProjectIcon(projectDir, maxDepth = 3) {
|
|
|
31204
32109
|
}
|
|
31205
32110
|
}
|
|
31206
32111
|
for (const monoDir of MONOREPO_PACKAGE_DIRS) {
|
|
31207
|
-
const monoPath =
|
|
32112
|
+
const monoPath = join10(projectDir, monoDir);
|
|
31208
32113
|
let packageEntries;
|
|
31209
32114
|
try {
|
|
31210
32115
|
packageEntries = await readdir(monoPath);
|
|
@@ -31212,7 +32117,7 @@ async function findProjectIcon(projectDir, maxDepth = 3) {
|
|
|
31212
32117
|
continue;
|
|
31213
32118
|
}
|
|
31214
32119
|
for (const packageName of packageEntries) {
|
|
31215
|
-
const packagePath =
|
|
32120
|
+
const packagePath = join10(monoPath, packageName);
|
|
31216
32121
|
try {
|
|
31217
32122
|
const packageStats = await stat2(packagePath);
|
|
31218
32123
|
if (!packageStats.isDirectory()) continue;
|
|
@@ -31220,7 +32125,7 @@ async function findProjectIcon(projectDir, maxDepth = 3) {
|
|
|
31220
32125
|
continue;
|
|
31221
32126
|
}
|
|
31222
32127
|
for (const priorityDir of PRIORITY_DIRS) {
|
|
31223
|
-
const priorityPath =
|
|
32128
|
+
const priorityPath = join10(packagePath, priorityDir);
|
|
31224
32129
|
try {
|
|
31225
32130
|
const priorityStats = await stat2(priorityPath);
|
|
31226
32131
|
if (priorityStats.isDirectory()) {
|
|
@@ -31270,7 +32175,7 @@ async function findDirRecursively(dir, maxDepth = 2, currentDepth = 0) {
|
|
|
31270
32175
|
if (ignoredDirsSet.has(entry) || priorityDirsSet.has(entry)) {
|
|
31271
32176
|
continue;
|
|
31272
32177
|
}
|
|
31273
|
-
const fullPath =
|
|
32178
|
+
const fullPath = join10(dir, entry);
|
|
31274
32179
|
try {
|
|
31275
32180
|
const stats = await stat2(fullPath);
|
|
31276
32181
|
if (stats.isDirectory()) {
|
|
@@ -31309,64 +32214,64 @@ async function getProjectIcon(projectDir) {
|
|
|
31309
32214
|
|
|
31310
32215
|
// ../server/src/utils/path.ts
|
|
31311
32216
|
import os5 from "os";
|
|
31312
|
-
function expandTilde(
|
|
31313
|
-
if (
|
|
32217
|
+
function expandTilde(path26) {
|
|
32218
|
+
if (path26.startsWith("~/")) {
|
|
31314
32219
|
const homeDir3 = process.env.HOME || os5.homedir();
|
|
31315
|
-
return
|
|
32220
|
+
return path26.replace("~", homeDir3);
|
|
31316
32221
|
}
|
|
31317
|
-
if (
|
|
32222
|
+
if (path26 === "~") {
|
|
31318
32223
|
return process.env.HOME || os5.homedir();
|
|
31319
32224
|
}
|
|
31320
|
-
return
|
|
32225
|
+
return path26;
|
|
31321
32226
|
}
|
|
31322
32227
|
|
|
31323
32228
|
// ../server/src/server/skills/scanner.ts
|
|
31324
|
-
import
|
|
32229
|
+
import fs8 from "node:fs/promises";
|
|
31325
32230
|
import os6 from "node:os";
|
|
31326
|
-
import
|
|
32231
|
+
import path13 from "node:path";
|
|
31327
32232
|
var NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
31328
32233
|
function homeDir() {
|
|
31329
32234
|
return process.env.HOME || os6.homedir();
|
|
31330
32235
|
}
|
|
31331
32236
|
function codexHomeDir() {
|
|
31332
|
-
return process.env.CODEX_HOME ||
|
|
32237
|
+
return process.env.CODEX_HOME || path13.join(homeDir(), ".codex");
|
|
31333
32238
|
}
|
|
31334
32239
|
function resolveScopeDir(provider, scope, workspaceRoot) {
|
|
31335
32240
|
if (scope === "codex-prompts") {
|
|
31336
32241
|
if (provider !== "codex") {
|
|
31337
32242
|
throw new Error(`Scope "codex-prompts" is only valid for provider "codex"`);
|
|
31338
32243
|
}
|
|
31339
|
-
return
|
|
32244
|
+
return path13.join(codexHomeDir(), "prompts");
|
|
31340
32245
|
}
|
|
31341
32246
|
if (scope === "project") {
|
|
31342
32247
|
if (!workspaceRoot) {
|
|
31343
32248
|
throw new Error(`workspaceRoot is required for scope "project"`);
|
|
31344
32249
|
}
|
|
31345
32250
|
const dotDir = provider === "claude" ? ".claude" : ".codex";
|
|
31346
|
-
return
|
|
32251
|
+
return path13.join(workspaceRoot, dotDir, "skills");
|
|
31347
32252
|
}
|
|
31348
32253
|
if (provider === "claude") {
|
|
31349
|
-
return
|
|
32254
|
+
return path13.join(homeDir(), ".claude", "skills");
|
|
31350
32255
|
}
|
|
31351
|
-
return
|
|
32256
|
+
return path13.join(codexHomeDir(), "skills");
|
|
31352
32257
|
}
|
|
31353
32258
|
function allowedRoots(workspaceRoot) {
|
|
31354
32259
|
const roots = [
|
|
31355
|
-
|
|
31356
|
-
|
|
31357
|
-
|
|
32260
|
+
path13.join(homeDir(), ".claude", "skills"),
|
|
32261
|
+
path13.join(codexHomeDir(), "skills"),
|
|
32262
|
+
path13.join(codexHomeDir(), "prompts")
|
|
31358
32263
|
];
|
|
31359
32264
|
if (workspaceRoot) {
|
|
31360
|
-
roots.push(
|
|
31361
|
-
roots.push(
|
|
32265
|
+
roots.push(path13.join(workspaceRoot, ".claude", "skills"));
|
|
32266
|
+
roots.push(path13.join(workspaceRoot, ".codex", "skills"));
|
|
31362
32267
|
}
|
|
31363
|
-
return roots.map((r) =>
|
|
32268
|
+
return roots.map((r) => path13.resolve(r));
|
|
31364
32269
|
}
|
|
31365
32270
|
function isInsideAllowedRoot(absPath, workspaceRoot) {
|
|
31366
|
-
const resolved =
|
|
32271
|
+
const resolved = path13.resolve(absPath);
|
|
31367
32272
|
for (const root of allowedRoots(workspaceRoot)) {
|
|
31368
|
-
const rel =
|
|
31369
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
32273
|
+
const rel = path13.relative(root, resolved);
|
|
32274
|
+
if (rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel)) {
|
|
31370
32275
|
return true;
|
|
31371
32276
|
}
|
|
31372
32277
|
}
|
|
@@ -31486,7 +32391,7 @@ async function listSkills(args) {
|
|
|
31486
32391
|
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
31487
32392
|
let entries;
|
|
31488
32393
|
try {
|
|
31489
|
-
entries = await
|
|
32394
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
31490
32395
|
} catch {
|
|
31491
32396
|
return [];
|
|
31492
32397
|
}
|
|
@@ -31497,7 +32402,7 @@ async function listSkills(args) {
|
|
|
31497
32402
|
if (!entry.name.endsWith(".md")) continue;
|
|
31498
32403
|
const name = entry.name.slice(0, -".md".length);
|
|
31499
32404
|
if (!name) continue;
|
|
31500
|
-
const fullPath =
|
|
32405
|
+
const fullPath = path13.join(dir, entry.name);
|
|
31501
32406
|
const stat5 = await safeStat(fullPath);
|
|
31502
32407
|
if (!stat5) continue;
|
|
31503
32408
|
const description = await readDescriptionSafely(fullPath);
|
|
@@ -31515,8 +32420,8 @@ async function listSkills(args) {
|
|
|
31515
32420
|
} else {
|
|
31516
32421
|
for (const entry of entries) {
|
|
31517
32422
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
31518
|
-
const skillDir =
|
|
31519
|
-
const skillPath =
|
|
32423
|
+
const skillDir = path13.join(dir, entry.name);
|
|
32424
|
+
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
31520
32425
|
const stat5 = await safeStat(skillPath);
|
|
31521
32426
|
if (!stat5) continue;
|
|
31522
32427
|
const description = await readDescriptionSafely(skillPath);
|
|
@@ -31537,7 +32442,7 @@ async function listSkills(args) {
|
|
|
31537
32442
|
}
|
|
31538
32443
|
async function safeStat(filePath) {
|
|
31539
32444
|
try {
|
|
31540
|
-
const s = await
|
|
32445
|
+
const s = await fs8.stat(filePath);
|
|
31541
32446
|
return { mtime: s.mtime, size: s.size };
|
|
31542
32447
|
} catch {
|
|
31543
32448
|
return null;
|
|
@@ -31545,7 +32450,7 @@ async function safeStat(filePath) {
|
|
|
31545
32450
|
}
|
|
31546
32451
|
async function readDescriptionSafely(filePath) {
|
|
31547
32452
|
try {
|
|
31548
|
-
const text = await
|
|
32453
|
+
const text = await fs8.readFile(filePath, "utf8");
|
|
31549
32454
|
return readDescription(text);
|
|
31550
32455
|
} catch {
|
|
31551
32456
|
return "";
|
|
@@ -31561,11 +32466,11 @@ async function createSkill(args) {
|
|
|
31561
32466
|
throw new Error(`Skill name must not contain path separators or "..".`);
|
|
31562
32467
|
}
|
|
31563
32468
|
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
31564
|
-
await
|
|
32469
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
31565
32470
|
if (args.scope === "codex-prompts") {
|
|
31566
|
-
const filePath2 =
|
|
32471
|
+
const filePath2 = path13.join(dir, `${args.name}.md`);
|
|
31567
32472
|
try {
|
|
31568
|
-
await
|
|
32473
|
+
await fs8.access(filePath2);
|
|
31569
32474
|
throw new Error(`A prompt named "${args.name}" already exists at ${filePath2}`);
|
|
31570
32475
|
} catch (err) {
|
|
31571
32476
|
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
@@ -31576,13 +32481,13 @@ async function createSkill(args) {
|
|
|
31576
32481
|
}
|
|
31577
32482
|
}
|
|
31578
32483
|
const initial2 = buildStarterPrompt(args.name);
|
|
31579
|
-
await
|
|
32484
|
+
await fs8.writeFile(filePath2, initial2, "utf8");
|
|
31580
32485
|
return { path: filePath2 };
|
|
31581
32486
|
}
|
|
31582
|
-
const skillDir =
|
|
32487
|
+
const skillDir = path13.join(dir, args.name);
|
|
31583
32488
|
let dirExists = false;
|
|
31584
32489
|
try {
|
|
31585
|
-
const stat5 = await
|
|
32490
|
+
const stat5 = await fs8.stat(skillDir);
|
|
31586
32491
|
dirExists = stat5.isDirectory();
|
|
31587
32492
|
} catch {
|
|
31588
32493
|
dirExists = false;
|
|
@@ -31590,10 +32495,10 @@ async function createSkill(args) {
|
|
|
31590
32495
|
if (dirExists) {
|
|
31591
32496
|
throw new Error(`A skill named "${args.name}" already exists at ${skillDir}`);
|
|
31592
32497
|
}
|
|
31593
|
-
await
|
|
31594
|
-
const filePath =
|
|
32498
|
+
await fs8.mkdir(skillDir, { recursive: true });
|
|
32499
|
+
const filePath = path13.join(skillDir, "SKILL.md");
|
|
31595
32500
|
const initial = buildStarterSkill(args.name);
|
|
31596
|
-
await
|
|
32501
|
+
await fs8.writeFile(filePath, initial, "utf8");
|
|
31597
32502
|
return { path: filePath };
|
|
31598
32503
|
}
|
|
31599
32504
|
function buildStarterSkill(name) {
|
|
@@ -31620,7 +32525,7 @@ Body of the prompt. Use \`$1\`, \`$2\`, ... or \`$ARGUMENTS\` for parameter expa
|
|
|
31620
32525
|
`;
|
|
31621
32526
|
}
|
|
31622
32527
|
async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
31623
|
-
if (!
|
|
32528
|
+
if (!path13.isAbsolute(args.path)) {
|
|
31624
32529
|
throw new Error(`writeSkillFrontmatter expects an absolute path; got "${args.path}"`);
|
|
31625
32530
|
}
|
|
31626
32531
|
if (!isInsideAllowedRoot(args.path, workspaceRoot)) {
|
|
@@ -31628,7 +32533,7 @@ async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
|
31628
32533
|
}
|
|
31629
32534
|
let original;
|
|
31630
32535
|
try {
|
|
31631
|
-
original = await
|
|
32536
|
+
original = await fs8.readFile(args.path, "utf8");
|
|
31632
32537
|
} catch (err) {
|
|
31633
32538
|
throw new Error(
|
|
31634
32539
|
`Failed to read skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -31641,12 +32546,12 @@ async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
|
31641
32546
|
${parsed.body}` : `${nextFrontmatter}
|
|
31642
32547
|
|
|
31643
32548
|
${original}`;
|
|
31644
|
-
await
|
|
32549
|
+
await fs8.writeFile(args.path, nextContent, "utf8");
|
|
31645
32550
|
}
|
|
31646
32551
|
|
|
31647
32552
|
// ../server/src/utils/directory-suggestions.ts
|
|
31648
32553
|
import { readdir as readdir2, realpath, stat as stat3 } from "node:fs/promises";
|
|
31649
|
-
import
|
|
32554
|
+
import path14 from "node:path";
|
|
31650
32555
|
var DEFAULT_LIMIT = 30;
|
|
31651
32556
|
var MAX_LIMIT = 100;
|
|
31652
32557
|
var DEFAULT_MAX_DEPTH = 6;
|
|
@@ -31732,7 +32637,7 @@ function normalizeLimit(limit) {
|
|
|
31732
32637
|
return Math.max(1, Math.min(MAX_LIMIT, bounded));
|
|
31733
32638
|
}
|
|
31734
32639
|
async function searchWithinParentDirectory(input) {
|
|
31735
|
-
const parentPath =
|
|
32640
|
+
const parentPath = path14.resolve(input.homeRoot, input.parentPart || ".");
|
|
31736
32641
|
const parentRoot = await resolveDirectory(parentPath);
|
|
31737
32642
|
if (!parentRoot || !isPathInsideRoot(input.homeRoot, parentRoot)) {
|
|
31738
32643
|
return [];
|
|
@@ -31797,7 +32702,7 @@ async function searchAcrossHomeTree(input) {
|
|
|
31797
32702
|
return dedupeAndSort(ranked).slice(0, input.limit);
|
|
31798
32703
|
}
|
|
31799
32704
|
async function searchWorkspaceWithinParentDirectory(input) {
|
|
31800
|
-
const parentPath =
|
|
32705
|
+
const parentPath = path14.resolve(input.workspaceRoot, input.parentPart || ".");
|
|
31801
32706
|
const parentRoot = await resolveDirectory(parentPath);
|
|
31802
32707
|
if (!parentRoot || !isPathInsideRoot(input.workspaceRoot, parentRoot)) {
|
|
31803
32708
|
return [];
|
|
@@ -32043,15 +32948,15 @@ function findSegmentMatchIndex(segments, predicate) {
|
|
|
32043
32948
|
return -1;
|
|
32044
32949
|
}
|
|
32045
32950
|
function normalizeRelativePath2(homeRoot, absolutePath) {
|
|
32046
|
-
const relative =
|
|
32951
|
+
const relative = path14.relative(homeRoot, absolutePath);
|
|
32047
32952
|
if (!relative) {
|
|
32048
32953
|
return ".";
|
|
32049
32954
|
}
|
|
32050
|
-
return relative.split(
|
|
32955
|
+
return relative.split(path14.sep).join("/");
|
|
32051
32956
|
}
|
|
32052
32957
|
function isPathInsideRoot(root, target) {
|
|
32053
|
-
const relative =
|
|
32054
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
32958
|
+
const relative = path14.relative(root, target);
|
|
32959
|
+
return relative === "" || !relative.startsWith("..") && !path14.isAbsolute(relative);
|
|
32055
32960
|
}
|
|
32056
32961
|
function normalizeQueryParts(query2, homeRoot) {
|
|
32057
32962
|
const typedQuery = query2.trim().replace(/\\/g, "/");
|
|
@@ -32067,9 +32972,9 @@ function normalizeQueryParts(query2, homeRoot) {
|
|
|
32067
32972
|
normalized = normalized.slice(1);
|
|
32068
32973
|
}
|
|
32069
32974
|
}
|
|
32070
|
-
if (
|
|
32975
|
+
if (path14.isAbsolute(normalized)) {
|
|
32071
32976
|
isRooted = true;
|
|
32072
|
-
const absolute =
|
|
32977
|
+
const absolute = path14.resolve(normalized);
|
|
32073
32978
|
if (!isPathInsideRoot(homeRoot, absolute)) {
|
|
32074
32979
|
return null;
|
|
32075
32980
|
}
|
|
@@ -32108,8 +33013,8 @@ function normalizeQueryParts(query2, homeRoot) {
|
|
|
32108
33013
|
}
|
|
32109
33014
|
function normalizeWorkspaceQueryParts(query2, workspaceRoot) {
|
|
32110
33015
|
let normalized = query2.trim().replace(/\\/g, "/");
|
|
32111
|
-
if (
|
|
32112
|
-
const absolute =
|
|
33016
|
+
if (path14.isAbsolute(normalized)) {
|
|
33017
|
+
const absolute = path14.resolve(normalized);
|
|
32113
33018
|
if (!isPathInsideRoot(workspaceRoot, absolute)) {
|
|
32114
33019
|
return null;
|
|
32115
33020
|
}
|
|
@@ -32135,7 +33040,7 @@ function normalizeWorkspaceQueryParts(query2, workspaceRoot) {
|
|
|
32135
33040
|
}
|
|
32136
33041
|
async function resolveDirectory(inputPath) {
|
|
32137
33042
|
try {
|
|
32138
|
-
const resolved = await realpath(
|
|
33043
|
+
const resolved = await realpath(path14.resolve(inputPath));
|
|
32139
33044
|
const stats = await stat3(resolved);
|
|
32140
33045
|
if (!stats.isDirectory()) {
|
|
32141
33046
|
return null;
|
|
@@ -32162,7 +33067,7 @@ async function listChildDirectories(input) {
|
|
|
32162
33067
|
if (!dirent.isDirectory() && !dirent.isSymbolicLink()) {
|
|
32163
33068
|
continue;
|
|
32164
33069
|
}
|
|
32165
|
-
const candidatePath =
|
|
33070
|
+
const candidatePath = path14.join(input.directory, dirent.name);
|
|
32166
33071
|
const absolutePath = await resolveDirectoryCandidate({
|
|
32167
33072
|
candidatePath,
|
|
32168
33073
|
dirent,
|
|
@@ -32199,7 +33104,7 @@ async function listWorkspaceChildEntries(input) {
|
|
|
32199
33104
|
if (isIgnoredWorkspaceDirectoryName(dirent.name)) {
|
|
32200
33105
|
continue;
|
|
32201
33106
|
}
|
|
32202
|
-
const candidatePath =
|
|
33107
|
+
const candidatePath = path14.join(input.directory, dirent.name);
|
|
32203
33108
|
const entry = await resolveWorkspaceCandidate({
|
|
32204
33109
|
candidatePath,
|
|
32205
33110
|
dirent,
|
|
@@ -32222,7 +33127,7 @@ async function listWorkspaceChildEntries(input) {
|
|
|
32222
33127
|
}
|
|
32223
33128
|
async function resolveDirectoryCandidate(input) {
|
|
32224
33129
|
if (input.dirent.isDirectory()) {
|
|
32225
|
-
const resolved2 =
|
|
33130
|
+
const resolved2 = path14.resolve(input.candidatePath);
|
|
32226
33131
|
return isPathInsideRoot(input.homeRoot, resolved2) ? resolved2 : null;
|
|
32227
33132
|
}
|
|
32228
33133
|
const resolved = await resolveDirectory(input.candidatePath);
|
|
@@ -32233,14 +33138,14 @@ async function resolveDirectoryCandidate(input) {
|
|
|
32233
33138
|
}
|
|
32234
33139
|
async function resolveWorkspaceCandidate(input) {
|
|
32235
33140
|
if (input.dirent.isDirectory()) {
|
|
32236
|
-
const resolved =
|
|
33141
|
+
const resolved = path14.resolve(input.candidatePath);
|
|
32237
33142
|
if (!isPathInsideRoot(input.workspaceRoot, resolved)) {
|
|
32238
33143
|
return null;
|
|
32239
33144
|
}
|
|
32240
33145
|
return { absolutePath: resolved, kind: "directory" };
|
|
32241
33146
|
}
|
|
32242
33147
|
if (input.dirent.isFile()) {
|
|
32243
|
-
const resolved =
|
|
33148
|
+
const resolved = path14.resolve(input.candidatePath);
|
|
32244
33149
|
if (!isPathInsideRoot(input.workspaceRoot, resolved)) {
|
|
32245
33150
|
return null;
|
|
32246
33151
|
}
|
|
@@ -32320,7 +33225,7 @@ function pruneWorkspaceEntryListCache() {
|
|
|
32320
33225
|
// ../server/src/utils/directory-listing.ts
|
|
32321
33226
|
import { readdir as readdir3, stat as stat4, realpath as realpath2 } from "node:fs/promises";
|
|
32322
33227
|
import { homedir as homedir3 } from "node:os";
|
|
32323
|
-
import
|
|
33228
|
+
import path15 from "node:path";
|
|
32324
33229
|
var DEFAULT_LIMIT2 = 500;
|
|
32325
33230
|
async function listDirectoryContents(options) {
|
|
32326
33231
|
const includeFiles = options.includeFiles ?? false;
|
|
@@ -32331,7 +33236,7 @@ async function listDirectoryContents(options) {
|
|
|
32331
33236
|
const collected = [];
|
|
32332
33237
|
for (const dirent of dirents) {
|
|
32333
33238
|
if (!includeHidden && dirent.name.startsWith(".")) continue;
|
|
32334
|
-
const childPath =
|
|
33239
|
+
const childPath = path15.join(resolvedPath, dirent.name);
|
|
32335
33240
|
const kind = await classifyEntry(dirent, childPath);
|
|
32336
33241
|
if (!kind) continue;
|
|
32337
33242
|
if (kind === "file" && !includeFiles) continue;
|
|
@@ -32339,7 +33244,7 @@ async function listDirectoryContents(options) {
|
|
|
32339
33244
|
if (collected.length >= limit) break;
|
|
32340
33245
|
}
|
|
32341
33246
|
collected.sort(compareEntries);
|
|
32342
|
-
const parent =
|
|
33247
|
+
const parent = path15.dirname(resolvedPath);
|
|
32343
33248
|
return {
|
|
32344
33249
|
path: resolvedPath,
|
|
32345
33250
|
parent: parent === resolvedPath ? null : parent,
|
|
@@ -32350,12 +33255,12 @@ async function resolveAbsolutePath(rawPath) {
|
|
|
32350
33255
|
const home = process.env.HOME ?? homedir3();
|
|
32351
33256
|
const trimmed = rawPath.trim();
|
|
32352
33257
|
if (trimmed === "" || trimmed === "~") {
|
|
32353
|
-
return
|
|
33258
|
+
return path15.resolve(home);
|
|
32354
33259
|
}
|
|
32355
33260
|
if (trimmed.startsWith("~/")) {
|
|
32356
|
-
return
|
|
33261
|
+
return path15.resolve(home, trimmed.slice(2));
|
|
32357
33262
|
}
|
|
32358
|
-
if (!
|
|
33263
|
+
if (!path15.isAbsolute(trimmed)) {
|
|
32359
33264
|
throw new Error(
|
|
32360
33265
|
`list_directory requires an absolute path, an empty string, or a "~"-prefixed path; got ${JSON.stringify(rawPath)}`
|
|
32361
33266
|
);
|
|
@@ -32363,7 +33268,7 @@ async function resolveAbsolutePath(rawPath) {
|
|
|
32363
33268
|
try {
|
|
32364
33269
|
return await realpath2(trimmed);
|
|
32365
33270
|
} catch {
|
|
32366
|
-
return
|
|
33271
|
+
return path15.resolve(trimmed);
|
|
32367
33272
|
}
|
|
32368
33273
|
}
|
|
32369
33274
|
async function classifyEntry(dirent, fullPath) {
|
|
@@ -32496,9 +33401,9 @@ function buildChatMentionNotification(input) {
|
|
|
32496
33401
|
}
|
|
32497
33402
|
|
|
32498
33403
|
// ../server/src/server/roles/scanner.ts
|
|
32499
|
-
import
|
|
33404
|
+
import fs9 from "node:fs/promises";
|
|
32500
33405
|
import os7 from "node:os";
|
|
32501
|
-
import
|
|
33406
|
+
import path16 from "node:path";
|
|
32502
33407
|
var NAME_REGEX2 = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
32503
33408
|
function homeDir2() {
|
|
32504
33409
|
return process.env.HOME || os7.homedir();
|
|
@@ -32508,22 +33413,22 @@ function resolveScopeDir2(scope, workspaceRoot) {
|
|
|
32508
33413
|
if (!workspaceRoot) {
|
|
32509
33414
|
throw new Error('workspaceRoot is required for scope "project"');
|
|
32510
33415
|
}
|
|
32511
|
-
return
|
|
33416
|
+
return path16.join(workspaceRoot, ".roles");
|
|
32512
33417
|
}
|
|
32513
|
-
return
|
|
33418
|
+
return path16.join(homeDir2(), ".appostle", ".roles");
|
|
32514
33419
|
}
|
|
32515
33420
|
function allowedRoots2(workspaceRoot) {
|
|
32516
|
-
const roots = [
|
|
33421
|
+
const roots = [path16.join(homeDir2(), ".appostle", ".roles")];
|
|
32517
33422
|
if (workspaceRoot) {
|
|
32518
|
-
roots.push(
|
|
33423
|
+
roots.push(path16.join(workspaceRoot, ".roles"));
|
|
32519
33424
|
}
|
|
32520
|
-
return roots.map((r) =>
|
|
33425
|
+
return roots.map((r) => path16.resolve(r));
|
|
32521
33426
|
}
|
|
32522
33427
|
function isInsideAllowedRoot2(absPath, workspaceRoot) {
|
|
32523
|
-
const resolved =
|
|
33428
|
+
const resolved = path16.resolve(absPath);
|
|
32524
33429
|
for (const root of allowedRoots2(workspaceRoot)) {
|
|
32525
|
-
const rel =
|
|
32526
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
33430
|
+
const rel = path16.relative(root, resolved);
|
|
33431
|
+
if (rel === "" || !rel.startsWith("..") && !path16.isAbsolute(rel)) {
|
|
32527
33432
|
return true;
|
|
32528
33433
|
}
|
|
32529
33434
|
}
|
|
@@ -32711,7 +33616,7 @@ function rewriteFrontmatter2(originalLines, next) {
|
|
|
32711
33616
|
async function readRolesFromDir(scope, dir, category) {
|
|
32712
33617
|
let entries;
|
|
32713
33618
|
try {
|
|
32714
|
-
entries = await
|
|
33619
|
+
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
32715
33620
|
} catch {
|
|
32716
33621
|
return [];
|
|
32717
33622
|
}
|
|
@@ -32721,17 +33626,17 @@ async function readRolesFromDir(scope, dir, category) {
|
|
|
32721
33626
|
if (!entry.name.endsWith(".md")) continue;
|
|
32722
33627
|
const name = entry.name.slice(0, -".md".length);
|
|
32723
33628
|
if (!name || !NAME_REGEX2.test(name)) continue;
|
|
32724
|
-
const fullPath =
|
|
33629
|
+
const fullPath = path16.join(dir, entry.name);
|
|
32725
33630
|
let stat5;
|
|
32726
33631
|
try {
|
|
32727
|
-
const s = await
|
|
33632
|
+
const s = await fs9.stat(fullPath);
|
|
32728
33633
|
stat5 = { mtime: s.mtime, size: s.size };
|
|
32729
33634
|
} catch {
|
|
32730
33635
|
continue;
|
|
32731
33636
|
}
|
|
32732
33637
|
let parsed;
|
|
32733
33638
|
try {
|
|
32734
|
-
const text = await
|
|
33639
|
+
const text = await fs9.readFile(fullPath, "utf8");
|
|
32735
33640
|
parsed = parseFrontmatter(text);
|
|
32736
33641
|
} catch {
|
|
32737
33642
|
parsed = {
|
|
@@ -32765,13 +33670,13 @@ async function readRolesFromDir(scope, dir, category) {
|
|
|
32765
33670
|
async function readRolesFromScopeDir(scope, scopeDir) {
|
|
32766
33671
|
let topEntries;
|
|
32767
33672
|
try {
|
|
32768
|
-
topEntries = await
|
|
33673
|
+
topEntries = await fs9.readdir(scopeDir, { withFileTypes: true });
|
|
32769
33674
|
} catch {
|
|
32770
33675
|
return [];
|
|
32771
33676
|
}
|
|
32772
33677
|
const flat = await readRolesFromDir(scope, scopeDir, null);
|
|
32773
33678
|
const categoryResults = await Promise.all(
|
|
32774
|
-
topEntries.filter((e) => e.isDirectory() && NAME_REGEX2.test(e.name)).map((e) => readRolesFromDir(scope,
|
|
33679
|
+
topEntries.filter((e) => e.isDirectory() && NAME_REGEX2.test(e.name)).map((e) => readRolesFromDir(scope, path16.join(scopeDir, e.name), e.name))
|
|
32775
33680
|
);
|
|
32776
33681
|
const all = [...flat, ...categoryResults.flat()];
|
|
32777
33682
|
all.sort((a, b) => {
|
|
@@ -32811,11 +33716,11 @@ async function createRole(args) {
|
|
|
32811
33716
|
throw new Error(`Role name must not contain path separators or "..".`);
|
|
32812
33717
|
}
|
|
32813
33718
|
const scopeDir = resolveScopeDir2(args.scope, args.workspaceRoot);
|
|
32814
|
-
const dir = args.category ?
|
|
32815
|
-
await
|
|
32816
|
-
const filePath =
|
|
33719
|
+
const dir = args.category ? path16.join(scopeDir, args.category) : scopeDir;
|
|
33720
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
33721
|
+
const filePath = path16.join(dir, `${args.name}.md`);
|
|
32817
33722
|
try {
|
|
32818
|
-
await
|
|
33723
|
+
await fs9.access(filePath);
|
|
32819
33724
|
throw new Error(`A role named "${args.name}" already exists at ${filePath}`);
|
|
32820
33725
|
} catch (err) {
|
|
32821
33726
|
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
@@ -32826,7 +33731,7 @@ async function createRole(args) {
|
|
|
32826
33731
|
}
|
|
32827
33732
|
}
|
|
32828
33733
|
const initial = buildStarterRole(args.name);
|
|
32829
|
-
await
|
|
33734
|
+
await fs9.writeFile(filePath, initial, "utf8");
|
|
32830
33735
|
return { path: filePath };
|
|
32831
33736
|
}
|
|
32832
33737
|
function buildStarterRole(name) {
|
|
@@ -32847,7 +33752,7 @@ the role is invoked.
|
|
|
32847
33752
|
`;
|
|
32848
33753
|
}
|
|
32849
33754
|
async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
32850
|
-
if (!
|
|
33755
|
+
if (!path16.isAbsolute(args.path)) {
|
|
32851
33756
|
throw new Error(`writeRoleFrontmatter expects an absolute path; got "${args.path}"`);
|
|
32852
33757
|
}
|
|
32853
33758
|
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
@@ -32855,7 +33760,7 @@ async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
|
32855
33760
|
}
|
|
32856
33761
|
let original;
|
|
32857
33762
|
try {
|
|
32858
|
-
original = await
|
|
33763
|
+
original = await fs9.readFile(args.path, "utf8");
|
|
32859
33764
|
} catch (err) {
|
|
32860
33765
|
throw new Error(
|
|
32861
33766
|
`Failed to read role file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -32868,23 +33773,23 @@ async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
|
32868
33773
|
${parsed.body}` : `${nextFrontmatter}
|
|
32869
33774
|
|
|
32870
33775
|
${original}`;
|
|
32871
|
-
await
|
|
33776
|
+
await fs9.writeFile(args.path, nextContent, "utf8");
|
|
32872
33777
|
}
|
|
32873
33778
|
async function moveRole(args, workspaceRoot) {
|
|
32874
|
-
if (!
|
|
33779
|
+
if (!path16.isAbsolute(args.path)) {
|
|
32875
33780
|
throw new Error(`moveRole expects an absolute path; got "${args.path}"`);
|
|
32876
33781
|
}
|
|
32877
33782
|
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
32878
33783
|
throw new Error(`Path "${args.path}" is not inside an allowlisted role root`);
|
|
32879
33784
|
}
|
|
32880
|
-
const oldDir =
|
|
32881
|
-
const oldFilename =
|
|
33785
|
+
const oldDir = path16.dirname(args.path);
|
|
33786
|
+
const oldFilename = path16.basename(args.path, ".md");
|
|
32882
33787
|
const roots = allowedRoots2(workspaceRoot);
|
|
32883
|
-
const rolesRoot = roots.find((r) =>
|
|
33788
|
+
const rolesRoot = roots.find((r) => path16.resolve(args.path).startsWith(r));
|
|
32884
33789
|
if (!rolesRoot) {
|
|
32885
33790
|
throw new Error(`Cannot determine roles root for "${args.path}"`);
|
|
32886
33791
|
}
|
|
32887
|
-
const relFromRoot =
|
|
33792
|
+
const relFromRoot = path16.relative(rolesRoot, path16.dirname(args.path));
|
|
32888
33793
|
const currentCategory = relFromRoot && relFromRoot !== "." ? relFromRoot : "";
|
|
32889
33794
|
const newName = args.newName ?? oldFilename;
|
|
32890
33795
|
const newCategory = args.newCategory !== void 0 ? args.newCategory : currentCategory;
|
|
@@ -32894,19 +33799,19 @@ async function moveRole(args, workspaceRoot) {
|
|
|
32894
33799
|
if (newCategory && !NAME_REGEX2.test(newCategory)) {
|
|
32895
33800
|
throw new Error(`Invalid category name: "${newCategory}"`);
|
|
32896
33801
|
}
|
|
32897
|
-
const newDir = newCategory ?
|
|
32898
|
-
const newPath =
|
|
32899
|
-
if (
|
|
33802
|
+
const newDir = newCategory ? path16.join(rolesRoot, newCategory) : rolesRoot;
|
|
33803
|
+
const newPath = path16.join(newDir, `${newName}.md`);
|
|
33804
|
+
if (path16.resolve(newPath) === path16.resolve(args.path)) {
|
|
32900
33805
|
return { path: args.path };
|
|
32901
33806
|
}
|
|
32902
|
-
await
|
|
33807
|
+
await fs9.mkdir(newDir, { recursive: true });
|
|
32903
33808
|
try {
|
|
32904
|
-
await
|
|
33809
|
+
await fs9.access(newPath);
|
|
32905
33810
|
throw new Error(`A role already exists at "${newPath}"`);
|
|
32906
33811
|
} catch (err) {
|
|
32907
33812
|
if (err.code !== "ENOENT") throw err;
|
|
32908
33813
|
}
|
|
32909
|
-
await
|
|
33814
|
+
await fs9.rename(args.path, newPath);
|
|
32910
33815
|
const frontmatterPatch = {};
|
|
32911
33816
|
if (newName !== oldFilename) {
|
|
32912
33817
|
frontmatterPatch.description = void 0;
|
|
@@ -32922,9 +33827,9 @@ async function moveRole(args, workspaceRoot) {
|
|
|
32922
33827
|
);
|
|
32923
33828
|
if (oldDir !== rolesRoot) {
|
|
32924
33829
|
try {
|
|
32925
|
-
const remaining = await
|
|
33830
|
+
const remaining = await fs9.readdir(oldDir);
|
|
32926
33831
|
if (remaining.length === 0) {
|
|
32927
|
-
await
|
|
33832
|
+
await fs9.rmdir(oldDir);
|
|
32928
33833
|
}
|
|
32929
33834
|
} catch {
|
|
32930
33835
|
}
|
|
@@ -32933,30 +33838,30 @@ async function moveRole(args, workspaceRoot) {
|
|
|
32933
33838
|
}
|
|
32934
33839
|
|
|
32935
33840
|
// ../server/src/server/brands/scanner.ts
|
|
32936
|
-
import
|
|
32937
|
-
import
|
|
33841
|
+
import fs10 from "node:fs/promises";
|
|
33842
|
+
import path17 from "node:path";
|
|
32938
33843
|
var NAME_REGEX3 = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
32939
33844
|
function resolveScopeDir3(scope, workspaceRoot) {
|
|
32940
33845
|
if (scope === "project") {
|
|
32941
33846
|
if (!workspaceRoot) {
|
|
32942
33847
|
throw new Error('workspaceRoot is required for scope "project"');
|
|
32943
33848
|
}
|
|
32944
|
-
return
|
|
33849
|
+
return path17.join(workspaceRoot, ".brand");
|
|
32945
33850
|
}
|
|
32946
33851
|
throw new Error(`Unknown scope: ${scope}`);
|
|
32947
33852
|
}
|
|
32948
33853
|
function allowedRoots3(workspaceRoot) {
|
|
32949
33854
|
const roots = [];
|
|
32950
33855
|
if (workspaceRoot) {
|
|
32951
|
-
roots.push(
|
|
33856
|
+
roots.push(path17.join(workspaceRoot, ".brand"));
|
|
32952
33857
|
}
|
|
32953
|
-
return roots.map((r) =>
|
|
33858
|
+
return roots.map((r) => path17.resolve(r));
|
|
32954
33859
|
}
|
|
32955
33860
|
function isInsideAllowedRoot3(absPath, workspaceRoot) {
|
|
32956
|
-
const resolved =
|
|
33861
|
+
const resolved = path17.resolve(absPath);
|
|
32957
33862
|
for (const root of allowedRoots3(workspaceRoot)) {
|
|
32958
|
-
const rel =
|
|
32959
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
33863
|
+
const rel = path17.relative(root, resolved);
|
|
33864
|
+
if (rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel)) {
|
|
32960
33865
|
return true;
|
|
32961
33866
|
}
|
|
32962
33867
|
}
|
|
@@ -33173,7 +34078,7 @@ function rewriteFrontmatter3(originalLines, next) {
|
|
|
33173
34078
|
async function readBrandsFromDir(scope, dir) {
|
|
33174
34079
|
let entries;
|
|
33175
34080
|
try {
|
|
33176
|
-
entries = await
|
|
34081
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
33177
34082
|
} catch {
|
|
33178
34083
|
return [];
|
|
33179
34084
|
}
|
|
@@ -33183,17 +34088,17 @@ async function readBrandsFromDir(scope, dir) {
|
|
|
33183
34088
|
if (!entry.name.endsWith(".md")) continue;
|
|
33184
34089
|
const name = entry.name.slice(0, -".md".length);
|
|
33185
34090
|
if (!name || !NAME_REGEX3.test(name)) continue;
|
|
33186
|
-
const fullPath =
|
|
34091
|
+
const fullPath = path17.join(dir, entry.name);
|
|
33187
34092
|
let stat5;
|
|
33188
34093
|
try {
|
|
33189
|
-
const s = await
|
|
34094
|
+
const s = await fs10.stat(fullPath);
|
|
33190
34095
|
stat5 = { mtime: s.mtime, size: s.size };
|
|
33191
34096
|
} catch {
|
|
33192
34097
|
continue;
|
|
33193
34098
|
}
|
|
33194
34099
|
let parsed;
|
|
33195
34100
|
try {
|
|
33196
|
-
const text = await
|
|
34101
|
+
const text = await fs10.readFile(fullPath, "utf8");
|
|
33197
34102
|
const { rawFrontmatterLines, hadFrontmatter } = parseBrandFile(text);
|
|
33198
34103
|
parsed = hadFrontmatter ? parseFrontmatter2(rawFrontmatterLines) : { description: "", tags: [], variables: [] };
|
|
33199
34104
|
} catch {
|
|
@@ -33227,10 +34132,10 @@ async function createBrand(args) {
|
|
|
33227
34132
|
throw new Error(`Brand name must not contain path separators or "..".`);
|
|
33228
34133
|
}
|
|
33229
34134
|
const dir = resolveScopeDir3("project", args.workspaceRoot);
|
|
33230
|
-
await
|
|
33231
|
-
const filePath =
|
|
34135
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
34136
|
+
const filePath = path17.join(dir, `${args.name}.md`);
|
|
33232
34137
|
try {
|
|
33233
|
-
await
|
|
34138
|
+
await fs10.access(filePath);
|
|
33234
34139
|
throw new Error(`A brand named "${args.name}" already exists at ${filePath}`);
|
|
33235
34140
|
} catch (err) {
|
|
33236
34141
|
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
@@ -33240,7 +34145,7 @@ async function createBrand(args) {
|
|
|
33240
34145
|
throw err;
|
|
33241
34146
|
}
|
|
33242
34147
|
}
|
|
33243
|
-
await
|
|
34148
|
+
await fs10.writeFile(filePath, buildStarterBrand(args.name), "utf8");
|
|
33244
34149
|
return { path: filePath };
|
|
33245
34150
|
}
|
|
33246
34151
|
function buildStarterBrand(name) {
|
|
@@ -33285,7 +34190,7 @@ async function copyBrandAsset(args) {
|
|
|
33285
34190
|
if (!args.workspaceRoot) {
|
|
33286
34191
|
throw new Error("workspaceRoot is required to copy a brand asset");
|
|
33287
34192
|
}
|
|
33288
|
-
if (!args.sourcePath || !
|
|
34193
|
+
if (!args.sourcePath || !path17.isAbsolute(args.sourcePath)) {
|
|
33289
34194
|
throw new Error(`copyBrandAsset expects an absolute sourcePath; got "${args.sourcePath}"`);
|
|
33290
34195
|
}
|
|
33291
34196
|
if (!TARGET_NAME_REGEX.test(args.targetName) || args.targetName.includes("..")) {
|
|
@@ -33293,20 +34198,20 @@ async function copyBrandAsset(args) {
|
|
|
33293
34198
|
`Invalid targetName "${args.targetName}". Use letters, digits, dot, underscore, dash.`
|
|
33294
34199
|
);
|
|
33295
34200
|
}
|
|
33296
|
-
const stats = await
|
|
34201
|
+
const stats = await fs10.stat(args.sourcePath);
|
|
33297
34202
|
if (!stats.isFile()) {
|
|
33298
34203
|
throw new Error(`Source path is not a regular file: ${args.sourcePath}`);
|
|
33299
34204
|
}
|
|
33300
|
-
const ext =
|
|
34205
|
+
const ext = path17.extname(args.sourcePath).toLowerCase();
|
|
33301
34206
|
const fileName = `${args.targetName}${ext}`;
|
|
33302
|
-
const assetsDir =
|
|
33303
|
-
const destAbs =
|
|
33304
|
-
const rel =
|
|
33305
|
-
if (rel.startsWith("..") ||
|
|
34207
|
+
const assetsDir = path17.join(args.workspaceRoot, ".brand", "assets");
|
|
34208
|
+
const destAbs = path17.resolve(assetsDir, fileName);
|
|
34209
|
+
const rel = path17.relative(path17.resolve(assetsDir), destAbs);
|
|
34210
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel)) {
|
|
33306
34211
|
throw new Error(`Refusing to write outside of .brand/assets: ${destAbs}`);
|
|
33307
34212
|
}
|
|
33308
|
-
await
|
|
33309
|
-
await
|
|
34213
|
+
await fs10.mkdir(assetsDir, { recursive: true });
|
|
34214
|
+
await fs10.copyFile(args.sourcePath, destAbs);
|
|
33310
34215
|
return {
|
|
33311
34216
|
relativePath: `assets/${fileName}`,
|
|
33312
34217
|
absolutePath: destAbs
|
|
@@ -33324,13 +34229,13 @@ async function uploadBrandAsset(args) {
|
|
|
33324
34229
|
if (!args.dataBase64 || args.dataBase64.trim().length === 0) {
|
|
33325
34230
|
throw new Error("No file data provided for brand asset upload");
|
|
33326
34231
|
}
|
|
33327
|
-
const extFromSource = args.sourceName ?
|
|
34232
|
+
const extFromSource = args.sourceName ? path17.extname(args.sourceName).toLowerCase() : "";
|
|
33328
34233
|
const ext = extFromSource || ".png";
|
|
33329
34234
|
const fileName = `${args.targetName}${ext}`;
|
|
33330
|
-
const assetsDir =
|
|
33331
|
-
const destAbs =
|
|
33332
|
-
const rel =
|
|
33333
|
-
if (rel.startsWith("..") ||
|
|
34235
|
+
const assetsDir = path17.join(args.workspaceRoot, ".brand", "assets");
|
|
34236
|
+
const destAbs = path17.resolve(assetsDir, fileName);
|
|
34237
|
+
const rel = path17.relative(path17.resolve(assetsDir), destAbs);
|
|
34238
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel)) {
|
|
33334
34239
|
throw new Error(`Refusing to write outside of .brand/assets: ${destAbs}`);
|
|
33335
34240
|
}
|
|
33336
34241
|
let data;
|
|
@@ -33342,15 +34247,15 @@ async function uploadBrandAsset(args) {
|
|
|
33342
34247
|
if (data.length === 0) {
|
|
33343
34248
|
throw new Error("Uploaded brand asset is empty");
|
|
33344
34249
|
}
|
|
33345
|
-
await
|
|
33346
|
-
await
|
|
34250
|
+
await fs10.mkdir(assetsDir, { recursive: true });
|
|
34251
|
+
await fs10.writeFile(destAbs, data);
|
|
33347
34252
|
return {
|
|
33348
34253
|
relativePath: `assets/${fileName}`,
|
|
33349
34254
|
absolutePath: destAbs
|
|
33350
34255
|
};
|
|
33351
34256
|
}
|
|
33352
34257
|
async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
33353
|
-
if (!
|
|
34258
|
+
if (!path17.isAbsolute(args.path)) {
|
|
33354
34259
|
throw new Error(`writeBrandFrontmatter expects an absolute path; got "${args.path}"`);
|
|
33355
34260
|
}
|
|
33356
34261
|
if (!isInsideAllowedRoot3(args.path, workspaceRoot)) {
|
|
@@ -33358,7 +34263,7 @@ async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
|
33358
34263
|
}
|
|
33359
34264
|
let original;
|
|
33360
34265
|
try {
|
|
33361
|
-
original = await
|
|
34266
|
+
original = await fs10.readFile(args.path, "utf8");
|
|
33362
34267
|
} catch (err) {
|
|
33363
34268
|
throw new Error(
|
|
33364
34269
|
`Failed to read brand file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -33371,14 +34276,14 @@ async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
|
33371
34276
|
${parsed.body}` : `${nextFrontmatter}
|
|
33372
34277
|
|
|
33373
34278
|
${original}`;
|
|
33374
|
-
await
|
|
34279
|
+
await fs10.writeFile(args.path, nextContent, "utf8");
|
|
33375
34280
|
}
|
|
33376
34281
|
|
|
33377
34282
|
// ../server/src/services/oauth-service.ts
|
|
33378
|
-
import { createHash as
|
|
34283
|
+
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
33379
34284
|
import { mkdir as mkdir4, readFile as readFile3, rename, unlink, writeFile as writeFile4 } from "node:fs/promises";
|
|
33380
34285
|
import { homedir as homedir4 } from "node:os";
|
|
33381
|
-
import { dirname as dirname4, join as
|
|
34286
|
+
import { dirname as dirname4, join as join11 } from "node:path";
|
|
33382
34287
|
import * as YAML from "yaml";
|
|
33383
34288
|
var GITLAB_CLIENT_ID = "7783dfb277ccf3dbce35a9d54c3c0aec24ed2006cb2d171e0749fd8444ed4f7c";
|
|
33384
34289
|
var GITLAB_AUTHORIZE_URL = "https://gitlab.com/oauth/authorize";
|
|
@@ -33400,7 +34305,7 @@ var OAuthError = class extends Error {
|
|
|
33400
34305
|
function createOAuthService(options) {
|
|
33401
34306
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
33402
34307
|
const now = options.now ?? Date.now;
|
|
33403
|
-
const credentialsPath =
|
|
34308
|
+
const credentialsPath = join11(options.appostleHome, "credentials.json");
|
|
33404
34309
|
const glabConfigPath = options.glabConfigPath ?? defaultGlabConfigPath();
|
|
33405
34310
|
const oauthLogger = options.logger.child({ module: "oauth-service" });
|
|
33406
34311
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -33522,7 +34427,7 @@ function generateCodeVerifier() {
|
|
|
33522
34427
|
return base64url(randomBytes2(32));
|
|
33523
34428
|
}
|
|
33524
34429
|
function computeCodeChallenge(verifier) {
|
|
33525
|
-
return base64url(
|
|
34430
|
+
return base64url(createHash4("sha256").update(verifier).digest());
|
|
33526
34431
|
}
|
|
33527
34432
|
function generateState() {
|
|
33528
34433
|
return base64url(randomBytes2(24));
|
|
@@ -33619,80 +34524,80 @@ async function fetchGitLabUsername(fetchImpl, accessToken) {
|
|
|
33619
34524
|
return null;
|
|
33620
34525
|
}
|
|
33621
34526
|
}
|
|
33622
|
-
async function readCredential(
|
|
34527
|
+
async function readCredential(path26, log2) {
|
|
33623
34528
|
try {
|
|
33624
|
-
const raw = await readFile3(
|
|
34529
|
+
const raw = await readFile3(path26, "utf8");
|
|
33625
34530
|
const parsed = JSON.parse(raw);
|
|
33626
34531
|
return parsed.gitlab ?? null;
|
|
33627
34532
|
} catch (error) {
|
|
33628
34533
|
if (isNotFound(error)) return null;
|
|
33629
|
-
log2.warn({ err: error, path:
|
|
34534
|
+
log2.warn({ err: error, path: path26 }, "oauth.credentials.read_failed");
|
|
33630
34535
|
return null;
|
|
33631
34536
|
}
|
|
33632
34537
|
}
|
|
33633
|
-
async function persistCredential(
|
|
33634
|
-
await mkdir4(dirname4(
|
|
34538
|
+
async function persistCredential(path26, credential, log2) {
|
|
34539
|
+
await mkdir4(dirname4(path26), { recursive: true });
|
|
33635
34540
|
let current = {};
|
|
33636
34541
|
try {
|
|
33637
|
-
const raw = await readFile3(
|
|
34542
|
+
const raw = await readFile3(path26, "utf8");
|
|
33638
34543
|
current = JSON.parse(raw);
|
|
33639
34544
|
} catch (error) {
|
|
33640
34545
|
if (!isNotFound(error)) {
|
|
33641
|
-
log2.warn({ err: error, path:
|
|
34546
|
+
log2.warn({ err: error, path: path26 }, "oauth.credentials.read_failed_overwriting");
|
|
33642
34547
|
}
|
|
33643
34548
|
}
|
|
33644
34549
|
const next = { ...current, gitlab: credential };
|
|
33645
|
-
const tmpPath = `${
|
|
34550
|
+
const tmpPath = `${path26}.tmp-${process.pid}-${Date.now()}`;
|
|
33646
34551
|
await writeFile4(tmpPath, JSON.stringify(next, null, 2), { mode: 384 });
|
|
33647
|
-
await rename(tmpPath,
|
|
34552
|
+
await rename(tmpPath, path26);
|
|
33648
34553
|
}
|
|
33649
|
-
async function deleteCredential(
|
|
34554
|
+
async function deleteCredential(path26, log2) {
|
|
33650
34555
|
let current = {};
|
|
33651
34556
|
try {
|
|
33652
|
-
const raw = await readFile3(
|
|
34557
|
+
const raw = await readFile3(path26, "utf8");
|
|
33653
34558
|
current = JSON.parse(raw);
|
|
33654
34559
|
} catch (error) {
|
|
33655
34560
|
if (isNotFound(error)) return;
|
|
33656
|
-
log2.warn({ err: error, path:
|
|
34561
|
+
log2.warn({ err: error, path: path26 }, "oauth.credentials.delete_read_failed");
|
|
33657
34562
|
return;
|
|
33658
34563
|
}
|
|
33659
34564
|
delete current.gitlab;
|
|
33660
34565
|
if (Object.keys(current).length === 0) {
|
|
33661
34566
|
try {
|
|
33662
|
-
await unlink(
|
|
34567
|
+
await unlink(path26);
|
|
33663
34568
|
} catch (error) {
|
|
33664
34569
|
if (!isNotFound(error)) {
|
|
33665
|
-
log2.warn({ err: error, path:
|
|
34570
|
+
log2.warn({ err: error, path: path26 }, "oauth.credentials.unlink_failed");
|
|
33666
34571
|
}
|
|
33667
34572
|
}
|
|
33668
34573
|
return;
|
|
33669
34574
|
}
|
|
33670
|
-
const tmpPath = `${
|
|
34575
|
+
const tmpPath = `${path26}.tmp-${process.pid}-${Date.now()}`;
|
|
33671
34576
|
await writeFile4(tmpPath, JSON.stringify(current, null, 2), { mode: 384 });
|
|
33672
|
-
await rename(tmpPath,
|
|
34577
|
+
await rename(tmpPath, path26);
|
|
33673
34578
|
}
|
|
33674
34579
|
function defaultGlabConfigPath() {
|
|
33675
34580
|
const home = homedir4();
|
|
33676
34581
|
if (process.platform === "darwin") {
|
|
33677
|
-
return
|
|
34582
|
+
return join11(home, "Library", "Application Support", "glab-cli", "config.yml");
|
|
33678
34583
|
}
|
|
33679
34584
|
if (process.platform === "win32") {
|
|
33680
34585
|
const appData = process.env.APPDATA;
|
|
33681
|
-
if (appData) return
|
|
33682
|
-
return
|
|
34586
|
+
if (appData) return join11(appData, "glab-cli", "config.yml");
|
|
34587
|
+
return join11(home, "AppData", "Roaming", "glab-cli", "config.yml");
|
|
33683
34588
|
}
|
|
33684
34589
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
33685
|
-
return
|
|
34590
|
+
return join11(xdg && xdg.length > 0 ? xdg : join11(home, ".config"), "glab-cli", "config.yml");
|
|
33686
34591
|
}
|
|
33687
|
-
async function writeGlabConfig(
|
|
33688
|
-
await mkdir4(dirname4(
|
|
34592
|
+
async function writeGlabConfig(path26, credential, log2) {
|
|
34593
|
+
await mkdir4(dirname4(path26), { recursive: true });
|
|
33689
34594
|
let doc;
|
|
33690
34595
|
try {
|
|
33691
|
-
const raw = await readFile3(
|
|
34596
|
+
const raw = await readFile3(path26, "utf8");
|
|
33692
34597
|
doc = YAML.parseDocument(raw);
|
|
33693
34598
|
if (doc.errors.length > 0) {
|
|
33694
34599
|
log2.warn(
|
|
33695
|
-
{ errors: doc.errors.map((e) => e.message), path:
|
|
34600
|
+
{ errors: doc.errors.map((e) => e.message), path: path26 },
|
|
33696
34601
|
"oauth.glab.parse_errors_replacing"
|
|
33697
34602
|
);
|
|
33698
34603
|
doc = YAML.parseDocument("{}");
|
|
@@ -33701,7 +34606,7 @@ async function writeGlabConfig(path24, credential, log2) {
|
|
|
33701
34606
|
if (isNotFound(error)) {
|
|
33702
34607
|
doc = YAML.parseDocument("{}");
|
|
33703
34608
|
} else {
|
|
33704
|
-
log2.warn({ err: error, path:
|
|
34609
|
+
log2.warn({ err: error, path: path26 }, "oauth.glab.read_failed_replacing");
|
|
33705
34610
|
doc = YAML.parseDocument("{}");
|
|
33706
34611
|
}
|
|
33707
34612
|
}
|
|
@@ -33714,18 +34619,18 @@ async function writeGlabConfig(path24, credential, log2) {
|
|
|
33714
34619
|
if (credential.username) {
|
|
33715
34620
|
hostEntry.set("user", credential.username);
|
|
33716
34621
|
}
|
|
33717
|
-
const tmpPath = `${
|
|
34622
|
+
const tmpPath = `${path26}.tmp-${process.pid}-${Date.now()}`;
|
|
33718
34623
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
33719
|
-
await rename(tmpPath,
|
|
34624
|
+
await rename(tmpPath, path26);
|
|
33720
34625
|
}
|
|
33721
|
-
async function removeGlabHost(
|
|
34626
|
+
async function removeGlabHost(path26, log2) {
|
|
33722
34627
|
let doc;
|
|
33723
34628
|
try {
|
|
33724
|
-
const raw = await readFile3(
|
|
34629
|
+
const raw = await readFile3(path26, "utf8");
|
|
33725
34630
|
doc = YAML.parseDocument(raw);
|
|
33726
34631
|
} catch (error) {
|
|
33727
34632
|
if (isNotFound(error)) return;
|
|
33728
|
-
log2.warn({ err: error, path:
|
|
34633
|
+
log2.warn({ err: error, path: path26 }, "oauth.glab.remove_read_failed");
|
|
33729
34634
|
return;
|
|
33730
34635
|
}
|
|
33731
34636
|
const hosts = doc.get("hosts");
|
|
@@ -33734,9 +34639,9 @@ async function removeGlabHost(path24, log2) {
|
|
|
33734
34639
|
if (hosts.items.length === 0) {
|
|
33735
34640
|
doc.delete("hosts");
|
|
33736
34641
|
}
|
|
33737
|
-
const tmpPath = `${
|
|
34642
|
+
const tmpPath = `${path26}.tmp-${process.pid}-${Date.now()}`;
|
|
33738
34643
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
33739
|
-
await rename(tmpPath,
|
|
34644
|
+
await rename(tmpPath, path26);
|
|
33740
34645
|
}
|
|
33741
34646
|
function ensureMap(doc, key) {
|
|
33742
34647
|
const existing = doc.get(key);
|
|
@@ -34702,6 +35607,50 @@ function summarizeFetchWorkspacesEntries(entries) {
|
|
|
34702
35607
|
workspaces
|
|
34703
35608
|
};
|
|
34704
35609
|
}
|
|
35610
|
+
function synthesizeRecordedUserText(rawText, files, imageCount = 0) {
|
|
35611
|
+
if (rawText.trim().length > 0) {
|
|
35612
|
+
return rawText;
|
|
35613
|
+
}
|
|
35614
|
+
if (files.length === 0 && imageCount === 0) {
|
|
35615
|
+
return rawText;
|
|
35616
|
+
}
|
|
35617
|
+
const parts = [];
|
|
35618
|
+
if (imageCount > 0) {
|
|
35619
|
+
parts.push(imageCount === 1 ? "Sent 1 image" : `Sent ${imageCount} images`);
|
|
35620
|
+
}
|
|
35621
|
+
if (files.length > 0) {
|
|
35622
|
+
const names = files.map((f) => f.fileName).join(", ");
|
|
35623
|
+
parts.push(
|
|
35624
|
+
files.length === 1 ? `uploaded ${names}` : `uploaded ${files.length} files: ${names}`
|
|
35625
|
+
);
|
|
35626
|
+
}
|
|
35627
|
+
return parts.join(" and ");
|
|
35628
|
+
}
|
|
35629
|
+
function buildImageEcho(incoming, persisted) {
|
|
35630
|
+
const out = [];
|
|
35631
|
+
for (let i = 0; i < incoming.length; i++) {
|
|
35632
|
+
const img = incoming[i];
|
|
35633
|
+
const stored = persisted[i];
|
|
35634
|
+
const id = stored?.id ?? img?.id;
|
|
35635
|
+
if (!img || !id) continue;
|
|
35636
|
+
const fileName = img.fileName ?? stored?.fileName ?? void 0;
|
|
35637
|
+
const byteSize = typeof img.byteSize === "number" ? img.byteSize : typeof stored?.size === "number" ? stored.size : void 0;
|
|
35638
|
+
out.push({
|
|
35639
|
+
id,
|
|
35640
|
+
mimeType: img.mimeType,
|
|
35641
|
+
...fileName ? { fileName } : {},
|
|
35642
|
+
...typeof byteSize === "number" ? { byteSize } : {},
|
|
35643
|
+
...stored ? {
|
|
35644
|
+
daemonRef: {
|
|
35645
|
+
kind: "image",
|
|
35646
|
+
attachmentId: stored.id,
|
|
35647
|
+
relativePath: stored.relativePath
|
|
35648
|
+
}
|
|
35649
|
+
} : {}
|
|
35650
|
+
});
|
|
35651
|
+
}
|
|
35652
|
+
return out;
|
|
35653
|
+
}
|
|
34705
35654
|
var SessionRequestError = class extends Error {
|
|
34706
35655
|
constructor(code, message) {
|
|
34707
35656
|
super(message);
|
|
@@ -34879,6 +35828,8 @@ var Session = class _Session {
|
|
|
34879
35828
|
});
|
|
34880
35829
|
this.agentManager = agentManager;
|
|
34881
35830
|
this.agentStorage = agentStorage;
|
|
35831
|
+
this.uploadStore = new SessionUploadStore({ logger: this.sessionLogger });
|
|
35832
|
+
this.imageStore = new SessionImageStore({ logger: this.sessionLogger });
|
|
34882
35833
|
this.projectRegistry = projectRegistry;
|
|
34883
35834
|
this.workspaceRegistry = workspaceRegistry;
|
|
34884
35835
|
this.chatService = chatService;
|
|
@@ -34990,11 +35941,12 @@ var Session = class _Session {
|
|
|
34990
35941
|
/**
|
|
34991
35942
|
* Normalize a user prompt (with optional image metadata) for AgentManager
|
|
34992
35943
|
*/
|
|
34993
|
-
buildAgentPrompt(text, images, attachments) {
|
|
35944
|
+
buildAgentPrompt(text, images, attachments, files) {
|
|
34994
35945
|
const normalized = text?.trim() ?? "";
|
|
34995
35946
|
const hasImages = Boolean(images && images.length > 0);
|
|
34996
35947
|
const hasAttachments = Boolean(attachments && attachments.length > 0);
|
|
34997
|
-
|
|
35948
|
+
const hasFiles = Boolean(files && files.length > 0);
|
|
35949
|
+
if (!hasImages && !hasAttachments && !hasFiles) {
|
|
34998
35950
|
return normalized;
|
|
34999
35951
|
}
|
|
35000
35952
|
const blocks = [];
|
|
@@ -35007,8 +35959,46 @@ var Session = class _Session {
|
|
|
35007
35959
|
for (const attachment of attachments ?? []) {
|
|
35008
35960
|
blocks.push(attachment);
|
|
35009
35961
|
}
|
|
35962
|
+
if (hasFiles && files) {
|
|
35963
|
+
blocks.push({ type: "text", text: renderFileAttachmentFooter(files) });
|
|
35964
|
+
}
|
|
35010
35965
|
return blocks;
|
|
35011
35966
|
}
|
|
35967
|
+
/**
|
|
35968
|
+
* Read persisted image bytes off disk for the LLM prompt. The daemon is
|
|
35969
|
+
* the canonical source of truth for image bytes (Clay pattern); the wire
|
|
35970
|
+
* payload is just an upload conduit. Reads through the agent's cwd via
|
|
35971
|
+
* `imageStore.readImageBytes`, which traversal-guards the relative path.
|
|
35972
|
+
*
|
|
35973
|
+
* Falls back to skipping any image whose disk read fails (logged) — better
|
|
35974
|
+
* to send a partial prompt than to crash the whole turn.
|
|
35975
|
+
*/
|
|
35976
|
+
async loadPromptImagesFromDisk(persistedImages, agentId) {
|
|
35977
|
+
const agent = this.agentManager.getAgent(agentId);
|
|
35978
|
+
if (!agent) {
|
|
35979
|
+
this.sessionLogger.warn(
|
|
35980
|
+
{ agentId },
|
|
35981
|
+
"loadPromptImagesFromDisk: agent vanished between persist and read"
|
|
35982
|
+
);
|
|
35983
|
+
return [];
|
|
35984
|
+
}
|
|
35985
|
+
const out = [];
|
|
35986
|
+
for (const persisted of persistedImages) {
|
|
35987
|
+
try {
|
|
35988
|
+
const { data, mimeType } = await this.imageStore.readImageBytes({
|
|
35989
|
+
cwd: agent.cwd,
|
|
35990
|
+
relativePath: persisted.relativePath
|
|
35991
|
+
});
|
|
35992
|
+
out.push({ data: data.toString("base64"), mimeType });
|
|
35993
|
+
} catch (error) {
|
|
35994
|
+
this.sessionLogger.warn(
|
|
35995
|
+
{ agentId, attachmentId: persisted.id, err: error },
|
|
35996
|
+
"loadPromptImagesFromDisk: failed to read image; skipping for this turn"
|
|
35997
|
+
);
|
|
35998
|
+
}
|
|
35999
|
+
}
|
|
36000
|
+
return out;
|
|
36001
|
+
}
|
|
35012
36002
|
/**
|
|
35013
36003
|
* Interrupt the agent's active run so the next prompt starts a fresh turn.
|
|
35014
36004
|
* Returns once the manager confirms the stream has been cancelled.
|
|
@@ -35477,6 +36467,21 @@ var Session = class _Session {
|
|
|
35477
36467
|
case "send_agent_message_request":
|
|
35478
36468
|
await this.handleSendAgentMessageRequest(msg);
|
|
35479
36469
|
break;
|
|
36470
|
+
case "list_session_uploads_request":
|
|
36471
|
+
await this.handleListSessionUploadsRequest(msg);
|
|
36472
|
+
break;
|
|
36473
|
+
case "delete_session_upload_request":
|
|
36474
|
+
await this.handleDeleteSessionUploadRequest(msg);
|
|
36475
|
+
break;
|
|
36476
|
+
case "list_session_images_request":
|
|
36477
|
+
await this.handleListSessionImagesRequest(msg);
|
|
36478
|
+
break;
|
|
36479
|
+
case "delete_session_image_request":
|
|
36480
|
+
await this.handleDeleteSessionImageRequest(msg);
|
|
36481
|
+
break;
|
|
36482
|
+
case "fetch_attachment_bytes_request":
|
|
36483
|
+
await this.handleFetchAttachmentBytesRequest(msg);
|
|
36484
|
+
break;
|
|
35480
36485
|
case "wait_for_finish_request":
|
|
35481
36486
|
await this.handleWaitForFinish(msg.agentId, msg.requestId, msg.timeoutMs);
|
|
35482
36487
|
break;
|
|
@@ -36747,26 +37752,86 @@ var Session = class _Session {
|
|
|
36747
37752
|
* Handle text message to agent (with optional image attachments)
|
|
36748
37753
|
*/
|
|
36749
37754
|
async handleSendAgentMessage(agentId, text, messageId, images, attachments, runOptions, options) {
|
|
37755
|
+
const files = options?.files ?? [];
|
|
36750
37756
|
this.sessionLogger.info(
|
|
36751
37757
|
{
|
|
36752
37758
|
agentId,
|
|
36753
37759
|
textPreview: text.substring(0, 50),
|
|
36754
37760
|
imageCount: images?.length ?? 0,
|
|
36755
|
-
attachmentCount: attachments?.length ?? 0
|
|
37761
|
+
attachmentCount: attachments?.length ?? 0,
|
|
37762
|
+
fileCount: files.length
|
|
36756
37763
|
},
|
|
36757
|
-
`Sending text to agent ${agentId}${images && images.length > 0 ? ` with ${images.length} image attachment(s)` : ""}${attachments && attachments.length > 0 ? ` and ${attachments.length} structured attachment(s)` : ""}`
|
|
37764
|
+
`Sending text to agent ${agentId}${images && images.length > 0 ? ` with ${images.length} image attachment(s)` : ""}${attachments && attachments.length > 0 ? ` and ${attachments.length} structured attachment(s)` : ""}${files.length > 0 ? ` and ${files.length} file attachment(s)` : ""}`
|
|
36758
37765
|
);
|
|
37766
|
+
let persistedFiles = [];
|
|
37767
|
+
let persistedImages = [];
|
|
37768
|
+
const hasFiles = files.length > 0;
|
|
37769
|
+
const hasImages = (images?.length ?? 0) > 0;
|
|
37770
|
+
if (hasFiles || hasImages) {
|
|
37771
|
+
const agent = this.agentManager.getAgent(agentId);
|
|
37772
|
+
if (!agent) {
|
|
37773
|
+
return {
|
|
37774
|
+
ok: false,
|
|
37775
|
+
error: `Agent ${agentId} not found; cannot persist attachments`
|
|
37776
|
+
};
|
|
37777
|
+
}
|
|
37778
|
+
try {
|
|
37779
|
+
if (hasFiles) {
|
|
37780
|
+
persistedFiles = await this.uploadStore.persistFiles({
|
|
37781
|
+
cwd: agent.cwd,
|
|
37782
|
+
agentId,
|
|
37783
|
+
messageId: messageId ?? null,
|
|
37784
|
+
files
|
|
37785
|
+
});
|
|
37786
|
+
}
|
|
37787
|
+
if (hasImages && images) {
|
|
37788
|
+
persistedImages = await this.imageStore.persistImages({
|
|
37789
|
+
cwd: agent.cwd,
|
|
37790
|
+
agentId,
|
|
37791
|
+
messageId: messageId ?? null,
|
|
37792
|
+
images
|
|
37793
|
+
});
|
|
37794
|
+
}
|
|
37795
|
+
} catch (error) {
|
|
37796
|
+
if (error instanceof UploadSizeError) {
|
|
37797
|
+
return {
|
|
37798
|
+
ok: false,
|
|
37799
|
+
error: `Attachments exceed the ${MAX_UPLOAD_BYTES_PER_MESSAGE} byte limit per message`
|
|
37800
|
+
};
|
|
37801
|
+
}
|
|
37802
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37803
|
+
return { ok: false, error: `Failed to persist attachments: ${message}` };
|
|
37804
|
+
}
|
|
37805
|
+
}
|
|
36759
37806
|
const promptText = options?.spokenInput ? wrapSpokenInput(text) : text;
|
|
36760
|
-
const
|
|
37807
|
+
const promptImages = persistedImages.length > 0 ? await this.loadPromptImagesFromDisk(persistedImages, agentId) : images;
|
|
37808
|
+
const prompt = this.buildAgentPrompt(promptText, promptImages, attachments, persistedFiles);
|
|
37809
|
+
const recordedText = synthesizeRecordedUserText(text, persistedFiles, images?.length ?? 0);
|
|
37810
|
+
const userMessageImages = buildImageEcho(images ?? [], persistedImages);
|
|
37811
|
+
const userMessageFiles = persistedFiles.map((f) => ({
|
|
37812
|
+
id: f.id,
|
|
37813
|
+
fileName: f.fileName,
|
|
37814
|
+
mimeType: f.mimeType,
|
|
37815
|
+
size: f.size,
|
|
37816
|
+
daemonRef: {
|
|
37817
|
+
kind: "file",
|
|
37818
|
+
attachmentId: f.id,
|
|
37819
|
+
relativePath: f.relativePath
|
|
37820
|
+
}
|
|
37821
|
+
}));
|
|
37822
|
+
const recordedTextIsSynthesized = text.trim().length === 0 && (userMessageImages.length > 0 || userMessageFiles.length > 0);
|
|
36761
37823
|
try {
|
|
36762
37824
|
await sendPromptToAgent({
|
|
36763
37825
|
agentManager: this.agentManager,
|
|
36764
37826
|
agentStorage: this.agentStorage,
|
|
36765
37827
|
agentId,
|
|
36766
|
-
userMessageText:
|
|
37828
|
+
userMessageText: recordedText,
|
|
36767
37829
|
prompt,
|
|
36768
37830
|
messageId,
|
|
36769
37831
|
runOptions,
|
|
37832
|
+
...userMessageImages.length > 0 ? { userMessageImages } : {},
|
|
37833
|
+
...userMessageFiles.length > 0 ? { userMessageFiles } : {},
|
|
37834
|
+
...recordedTextIsSynthesized ? { userMessageTextIsSynthesized: true } : {},
|
|
36770
37835
|
logger: this.sessionLogger
|
|
36771
37836
|
});
|
|
36772
37837
|
return { ok: true };
|
|
@@ -36791,6 +37856,7 @@ var Session = class _Session {
|
|
|
36791
37856
|
outputSchema,
|
|
36792
37857
|
git,
|
|
36793
37858
|
images,
|
|
37859
|
+
files,
|
|
36794
37860
|
attachments,
|
|
36795
37861
|
labels
|
|
36796
37862
|
} = msg;
|
|
@@ -36831,7 +37897,7 @@ var Session = class _Session {
|
|
|
36831
37897
|
}
|
|
36832
37898
|
);
|
|
36833
37899
|
await this.forwardAgentUpdate(snapshot);
|
|
36834
|
-
if (trimmedPrompt || (images?.length ?? 0) > 0 || (attachments?.length ?? 0) > 0) {
|
|
37900
|
+
if (trimmedPrompt || (images?.length ?? 0) > 0 || (attachments?.length ?? 0) > 0 || (files?.length ?? 0) > 0) {
|
|
36835
37901
|
scheduleAgentMetadataGeneration({
|
|
36836
37902
|
agentManager: this.agentManager,
|
|
36837
37903
|
agentId: snapshot.id,
|
|
@@ -36847,7 +37913,8 @@ var Session = class _Session {
|
|
|
36847
37913
|
resolveClientMessageId(clientMessageId),
|
|
36848
37914
|
images,
|
|
36849
37915
|
attachments,
|
|
36850
|
-
outputSchema ? { outputSchema } : void 0
|
|
37916
|
+
outputSchema ? { outputSchema } : void 0,
|
|
37917
|
+
files && files.length > 0 ? { files } : void 0
|
|
36851
37918
|
);
|
|
36852
37919
|
if (!started.ok) {
|
|
36853
37920
|
throw new Error(started.error);
|
|
@@ -38264,7 +39331,7 @@ var Session = class _Session {
|
|
|
38264
39331
|
homeDir: process.env.HOME ?? homedir5(),
|
|
38265
39332
|
query: query2,
|
|
38266
39333
|
limit
|
|
38267
|
-
})).map((
|
|
39334
|
+
})).map((path26) => ({ path: path26, kind: "directory" }));
|
|
38268
39335
|
const directories = entries.filter((entry) => entry.kind === "directory").map((entry) => entry.path);
|
|
38269
39336
|
this.emit({
|
|
38270
39337
|
type: "directory_suggestions_response",
|
|
@@ -39318,10 +40385,11 @@ ${details}`.trim());
|
|
|
39318
40385
|
root: cwd,
|
|
39319
40386
|
relativePath: requestedPath
|
|
39320
40387
|
});
|
|
40388
|
+
const recoveredFileName = originalUploadFileName(info.path) ?? info.fileName;
|
|
39321
40389
|
const entry = this.downloadTokenStore.issueToken({
|
|
39322
40390
|
path: info.path,
|
|
39323
40391
|
absolutePath: info.absolutePath,
|
|
39324
|
-
fileName:
|
|
40392
|
+
fileName: recoveredFileName,
|
|
39325
40393
|
mimeType: info.mimeType,
|
|
39326
40394
|
size: info.size
|
|
39327
40395
|
});
|
|
@@ -39368,7 +40436,7 @@ ${details}`.trim());
|
|
|
39368
40436
|
);
|
|
39369
40437
|
const registryRecords = await this.agentStorage.list();
|
|
39370
40438
|
const liveIds = new Set(agentSnapshots.map((a) => a.id));
|
|
39371
|
-
const persistedAgents = registryRecords.filter((record) => !liveIds.has(record.id)
|
|
40439
|
+
const persistedAgents = registryRecords.filter((record) => !liveIds.has(record.id)).map((record) => this.buildStoredAgentPayload(record));
|
|
39372
40440
|
let agents = [...liveAgents, ...persistedAgents];
|
|
39373
40441
|
agents = agents.filter((agent) => this.isProviderVisibleToClient(agent.provider));
|
|
39374
40442
|
if (filter?.labels) {
|
|
@@ -40755,19 +41823,96 @@ ${details}`.trim());
|
|
|
40755
41823
|
}
|
|
40756
41824
|
try {
|
|
40757
41825
|
const agentId = resolved.agentId;
|
|
40758
|
-
|
|
41826
|
+
let persistedFiles = [];
|
|
41827
|
+
let persistedImages = [];
|
|
41828
|
+
const hasFiles = (msg.files?.length ?? 0) > 0;
|
|
41829
|
+
const hasImages = (msg.images?.length ?? 0) > 0;
|
|
41830
|
+
if (hasFiles || hasImages) {
|
|
41831
|
+
const agent = this.agentManager.getAgent(agentId);
|
|
41832
|
+
if (!agent) {
|
|
41833
|
+
this.emit({
|
|
41834
|
+
type: "send_agent_message_response",
|
|
41835
|
+
payload: {
|
|
41836
|
+
requestId: msg.requestId,
|
|
41837
|
+
agentId,
|
|
41838
|
+
accepted: false,
|
|
41839
|
+
error: `Agent ${agentId} not found; cannot persist attachments`
|
|
41840
|
+
}
|
|
41841
|
+
});
|
|
41842
|
+
return;
|
|
41843
|
+
}
|
|
41844
|
+
try {
|
|
41845
|
+
if (hasFiles && msg.files) {
|
|
41846
|
+
persistedFiles = await this.uploadStore.persistFiles({
|
|
41847
|
+
cwd: agent.cwd,
|
|
41848
|
+
agentId,
|
|
41849
|
+
messageId: msg.messageId ?? null,
|
|
41850
|
+
files: msg.files
|
|
41851
|
+
});
|
|
41852
|
+
}
|
|
41853
|
+
if (hasImages && msg.images) {
|
|
41854
|
+
persistedImages = await this.imageStore.persistImages({
|
|
41855
|
+
cwd: agent.cwd,
|
|
41856
|
+
agentId,
|
|
41857
|
+
messageId: msg.messageId ?? null,
|
|
41858
|
+
images: msg.images
|
|
41859
|
+
});
|
|
41860
|
+
}
|
|
41861
|
+
} catch (error) {
|
|
41862
|
+
const message = error instanceof UploadSizeError ? `Attachments exceed the ${MAX_UPLOAD_BYTES_PER_MESSAGE} byte limit per message` : error instanceof Error ? `Failed to persist attachments: ${error.message}` : "Failed to persist attachments";
|
|
41863
|
+
this.emit({
|
|
41864
|
+
type: "send_agent_message_response",
|
|
41865
|
+
payload: {
|
|
41866
|
+
requestId: msg.requestId,
|
|
41867
|
+
agentId,
|
|
41868
|
+
accepted: false,
|
|
41869
|
+
error: message
|
|
41870
|
+
}
|
|
41871
|
+
});
|
|
41872
|
+
return;
|
|
41873
|
+
}
|
|
41874
|
+
}
|
|
41875
|
+
const promptImages = persistedImages.length > 0 ? await this.loadPromptImagesFromDisk(persistedImages, agentId) : msg.images;
|
|
41876
|
+
const prompt = this.buildAgentPrompt(msg.text, promptImages, msg.attachments, persistedFiles);
|
|
41877
|
+
const recordedText = synthesizeRecordedUserText(
|
|
41878
|
+
msg.text,
|
|
41879
|
+
persistedFiles,
|
|
41880
|
+
msg.images?.length ?? 0
|
|
41881
|
+
);
|
|
41882
|
+
const userMessageImages = buildImageEcho(msg.images ?? [], persistedImages);
|
|
41883
|
+
const userMessageFiles = persistedFiles.map((f) => ({
|
|
41884
|
+
id: f.id,
|
|
41885
|
+
fileName: f.fileName,
|
|
41886
|
+
mimeType: f.mimeType,
|
|
41887
|
+
size: f.size,
|
|
41888
|
+
daemonRef: {
|
|
41889
|
+
kind: "file",
|
|
41890
|
+
attachmentId: f.id,
|
|
41891
|
+
relativePath: f.relativePath
|
|
41892
|
+
}
|
|
41893
|
+
}));
|
|
40759
41894
|
this.sessionLogger.trace(
|
|
40760
|
-
{
|
|
41895
|
+
{
|
|
41896
|
+
agentId,
|
|
41897
|
+
messageId: msg.messageId,
|
|
41898
|
+
textPrefix: msg.text.slice(0, 80),
|
|
41899
|
+
fileCount: persistedFiles.length,
|
|
41900
|
+
echoImageCount: userMessageImages.length
|
|
41901
|
+
},
|
|
40761
41902
|
"send_agent_message_request: dispatching shared sendPromptToAgent"
|
|
40762
41903
|
);
|
|
41904
|
+
const recordedTextIsSynthesized = msg.text.trim().length === 0 && (userMessageImages.length > 0 || userMessageFiles.length > 0);
|
|
40763
41905
|
try {
|
|
40764
41906
|
await sendPromptToAgent({
|
|
40765
41907
|
agentManager: this.agentManager,
|
|
40766
41908
|
agentStorage: this.agentStorage,
|
|
40767
41909
|
agentId,
|
|
40768
|
-
userMessageText:
|
|
41910
|
+
userMessageText: recordedText,
|
|
40769
41911
|
prompt,
|
|
40770
41912
|
messageId: msg.messageId,
|
|
41913
|
+
...userMessageImages.length > 0 ? { userMessageImages } : {},
|
|
41914
|
+
...userMessageFiles.length > 0 ? { userMessageFiles } : {},
|
|
41915
|
+
...recordedTextIsSynthesized ? { userMessageTextIsSynthesized: true } : {},
|
|
40771
41916
|
logger: this.sessionLogger
|
|
40772
41917
|
});
|
|
40773
41918
|
} catch (error) {
|
|
@@ -40826,6 +41971,242 @@ ${details}`.trim());
|
|
|
40826
41971
|
});
|
|
40827
41972
|
}
|
|
40828
41973
|
}
|
|
41974
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
41975
|
+
// Session uploads — backs the in-app "Files" tab inside an active session.
|
|
41976
|
+
// Both handlers resolve `agentId` against the agent registry first so we
|
|
41977
|
+
// get the same accept-prefix-or-title behaviour as send_agent_message, and
|
|
41978
|
+
// both fail closed on a missing agent (no implicit "list everything" that
|
|
41979
|
+
// could leak across sessions).
|
|
41980
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
41981
|
+
async handleListSessionUploadsRequest(msg) {
|
|
41982
|
+
const resolved = await this.resolveAgentIdentifier(msg.agentId);
|
|
41983
|
+
if (!resolved.ok) {
|
|
41984
|
+
this.emit({
|
|
41985
|
+
type: "list_session_uploads_response",
|
|
41986
|
+
payload: { requestId: msg.requestId, ok: false, error: resolved.error }
|
|
41987
|
+
});
|
|
41988
|
+
return;
|
|
41989
|
+
}
|
|
41990
|
+
const agent = this.agentManager.getAgent(resolved.agentId);
|
|
41991
|
+
if (!agent) {
|
|
41992
|
+
this.emit({
|
|
41993
|
+
type: "list_session_uploads_response",
|
|
41994
|
+
payload: { requestId: msg.requestId, ok: false, error: "Agent not found" }
|
|
41995
|
+
});
|
|
41996
|
+
return;
|
|
41997
|
+
}
|
|
41998
|
+
try {
|
|
41999
|
+
const uploads = await this.uploadStore.listUploads({
|
|
42000
|
+
cwd: agent.cwd,
|
|
42001
|
+
agentId: resolved.agentId
|
|
42002
|
+
});
|
|
42003
|
+
this.emit({
|
|
42004
|
+
type: "list_session_uploads_response",
|
|
42005
|
+
payload: { requestId: msg.requestId, ok: true, uploads }
|
|
42006
|
+
});
|
|
42007
|
+
} catch (error) {
|
|
42008
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42009
|
+
this.emit({
|
|
42010
|
+
type: "list_session_uploads_response",
|
|
42011
|
+
payload: { requestId: msg.requestId, ok: false, error: message }
|
|
42012
|
+
});
|
|
42013
|
+
}
|
|
42014
|
+
}
|
|
42015
|
+
async handleDeleteSessionUploadRequest(msg) {
|
|
42016
|
+
const resolved = await this.resolveAgentIdentifier(msg.agentId);
|
|
42017
|
+
if (!resolved.ok) {
|
|
42018
|
+
this.emit({
|
|
42019
|
+
type: "delete_session_upload_response",
|
|
42020
|
+
payload: { requestId: msg.requestId, ok: false, error: resolved.error }
|
|
42021
|
+
});
|
|
42022
|
+
return;
|
|
42023
|
+
}
|
|
42024
|
+
const agent = this.agentManager.getAgent(resolved.agentId);
|
|
42025
|
+
if (!agent) {
|
|
42026
|
+
this.emit({
|
|
42027
|
+
type: "delete_session_upload_response",
|
|
42028
|
+
payload: { requestId: msg.requestId, ok: false, error: "Agent not found" }
|
|
42029
|
+
});
|
|
42030
|
+
return;
|
|
42031
|
+
}
|
|
42032
|
+
try {
|
|
42033
|
+
const result = await this.uploadStore.deleteUpload({
|
|
42034
|
+
cwd: agent.cwd,
|
|
42035
|
+
agentId: resolved.agentId,
|
|
42036
|
+
uploadId: msg.uploadId
|
|
42037
|
+
});
|
|
42038
|
+
if (!result.deleted) {
|
|
42039
|
+
this.emit({
|
|
42040
|
+
type: "delete_session_upload_response",
|
|
42041
|
+
payload: { requestId: msg.requestId, ok: false, error: "Upload not found" }
|
|
42042
|
+
});
|
|
42043
|
+
return;
|
|
42044
|
+
}
|
|
42045
|
+
this.emit({
|
|
42046
|
+
type: "delete_session_upload_response",
|
|
42047
|
+
payload: { requestId: msg.requestId, ok: true }
|
|
42048
|
+
});
|
|
42049
|
+
} catch (error) {
|
|
42050
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42051
|
+
this.emit({
|
|
42052
|
+
type: "delete_session_upload_response",
|
|
42053
|
+
payload: { requestId: msg.requestId, ok: false, error: message }
|
|
42054
|
+
});
|
|
42055
|
+
}
|
|
42056
|
+
}
|
|
42057
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
42058
|
+
// Session images — backs the "Images" tab inside the uploads modal. Same
|
|
42059
|
+
// shape as the file handlers above; the store has no manifest, so listing
|
|
42060
|
+
// is a directory scan and delete is `unlink` (traversal-guarded).
|
|
42061
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
42062
|
+
async handleListSessionImagesRequest(msg) {
|
|
42063
|
+
const resolved = await this.resolveAgentIdentifier(msg.agentId);
|
|
42064
|
+
if (!resolved.ok) {
|
|
42065
|
+
this.emit({
|
|
42066
|
+
type: "list_session_images_response",
|
|
42067
|
+
payload: { requestId: msg.requestId, ok: false, error: resolved.error }
|
|
42068
|
+
});
|
|
42069
|
+
return;
|
|
42070
|
+
}
|
|
42071
|
+
const agent = this.agentManager.getAgent(resolved.agentId);
|
|
42072
|
+
if (!agent) {
|
|
42073
|
+
this.emit({
|
|
42074
|
+
type: "list_session_images_response",
|
|
42075
|
+
payload: { requestId: msg.requestId, ok: false, error: "Agent not found" }
|
|
42076
|
+
});
|
|
42077
|
+
return;
|
|
42078
|
+
}
|
|
42079
|
+
try {
|
|
42080
|
+
const images = await this.imageStore.listImages({ cwd: agent.cwd });
|
|
42081
|
+
this.emit({
|
|
42082
|
+
type: "list_session_images_response",
|
|
42083
|
+
payload: { requestId: msg.requestId, ok: true, images }
|
|
42084
|
+
});
|
|
42085
|
+
} catch (error) {
|
|
42086
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42087
|
+
this.emit({
|
|
42088
|
+
type: "list_session_images_response",
|
|
42089
|
+
payload: { requestId: msg.requestId, ok: false, error: message }
|
|
42090
|
+
});
|
|
42091
|
+
}
|
|
42092
|
+
}
|
|
42093
|
+
async handleDeleteSessionImageRequest(msg) {
|
|
42094
|
+
const resolved = await this.resolveAgentIdentifier(msg.agentId);
|
|
42095
|
+
if (!resolved.ok) {
|
|
42096
|
+
this.emit({
|
|
42097
|
+
type: "delete_session_image_response",
|
|
42098
|
+
payload: { requestId: msg.requestId, ok: false, error: resolved.error }
|
|
42099
|
+
});
|
|
42100
|
+
return;
|
|
42101
|
+
}
|
|
42102
|
+
const agent = this.agentManager.getAgent(resolved.agentId);
|
|
42103
|
+
if (!agent) {
|
|
42104
|
+
this.emit({
|
|
42105
|
+
type: "delete_session_image_response",
|
|
42106
|
+
payload: { requestId: msg.requestId, ok: false, error: "Agent not found" }
|
|
42107
|
+
});
|
|
42108
|
+
return;
|
|
42109
|
+
}
|
|
42110
|
+
try {
|
|
42111
|
+
const result = await this.imageStore.deleteImage({
|
|
42112
|
+
cwd: agent.cwd,
|
|
42113
|
+
relativePath: msg.relativePath
|
|
42114
|
+
});
|
|
42115
|
+
if (!result.deleted) {
|
|
42116
|
+
this.emit({
|
|
42117
|
+
type: "delete_session_image_response",
|
|
42118
|
+
payload: { requestId: msg.requestId, ok: false, error: "Image not found" }
|
|
42119
|
+
});
|
|
42120
|
+
return;
|
|
42121
|
+
}
|
|
42122
|
+
this.emit({
|
|
42123
|
+
type: "delete_session_image_response",
|
|
42124
|
+
payload: { requestId: msg.requestId, ok: true }
|
|
42125
|
+
});
|
|
42126
|
+
} catch (error) {
|
|
42127
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42128
|
+
this.emit({
|
|
42129
|
+
type: "delete_session_image_response",
|
|
42130
|
+
payload: { requestId: msg.requestId, ok: false, error: message }
|
|
42131
|
+
});
|
|
42132
|
+
}
|
|
42133
|
+
}
|
|
42134
|
+
/**
|
|
42135
|
+
* Serve attachment bytes by id. The daemon is the canonical source — every
|
|
42136
|
+
* paired client (Electron after Cmd-R, phone, web) hits this RPC to render
|
|
42137
|
+
* thumbnails or download files. Bytes never round-trip through the chat
|
|
42138
|
+
* timeline; they're fetched on demand.
|
|
42139
|
+
*
|
|
42140
|
+
* `relativePath` comes from the user_message timeline echo's `daemonRef`;
|
|
42141
|
+
* the store's read methods traversal-guard it so it can't escape the
|
|
42142
|
+
* dedicated subdirectories.
|
|
42143
|
+
*/
|
|
42144
|
+
async handleFetchAttachmentBytesRequest(msg) {
|
|
42145
|
+
const resolved = await this.resolveAgentIdentifier(msg.agentId);
|
|
42146
|
+
if (!resolved.ok) {
|
|
42147
|
+
this.emit({
|
|
42148
|
+
type: "fetch_attachment_bytes_response",
|
|
42149
|
+
payload: { requestId: msg.requestId, ok: false, error: resolved.error }
|
|
42150
|
+
});
|
|
42151
|
+
return;
|
|
42152
|
+
}
|
|
42153
|
+
const agent = this.agentManager.getAgent(resolved.agentId);
|
|
42154
|
+
if (!agent) {
|
|
42155
|
+
this.emit({
|
|
42156
|
+
type: "fetch_attachment_bytes_response",
|
|
42157
|
+
payload: { requestId: msg.requestId, ok: false, error: "Agent not found" }
|
|
42158
|
+
});
|
|
42159
|
+
return;
|
|
42160
|
+
}
|
|
42161
|
+
try {
|
|
42162
|
+
if (msg.kind === "image") {
|
|
42163
|
+
const { data, mimeType, byteSize } = await this.imageStore.readImageBytes({
|
|
42164
|
+
cwd: agent.cwd,
|
|
42165
|
+
relativePath: msg.relativePath
|
|
42166
|
+
});
|
|
42167
|
+
this.emit({
|
|
42168
|
+
type: "fetch_attachment_bytes_response",
|
|
42169
|
+
payload: {
|
|
42170
|
+
requestId: msg.requestId,
|
|
42171
|
+
ok: true,
|
|
42172
|
+
attachmentId: msg.attachmentId,
|
|
42173
|
+
kind: "image",
|
|
42174
|
+
data: data.toString("base64"),
|
|
42175
|
+
mimeType,
|
|
42176
|
+
byteSize
|
|
42177
|
+
}
|
|
42178
|
+
});
|
|
42179
|
+
} else {
|
|
42180
|
+
const { data, mimeType, byteSize, fileName } = await this.uploadStore.readUploadBytes({
|
|
42181
|
+
cwd: agent.cwd,
|
|
42182
|
+
relativePath: msg.relativePath
|
|
42183
|
+
});
|
|
42184
|
+
this.emit({
|
|
42185
|
+
type: "fetch_attachment_bytes_response",
|
|
42186
|
+
payload: {
|
|
42187
|
+
requestId: msg.requestId,
|
|
42188
|
+
ok: true,
|
|
42189
|
+
attachmentId: msg.attachmentId,
|
|
42190
|
+
kind: "file",
|
|
42191
|
+
data: data.toString("base64"),
|
|
42192
|
+
mimeType,
|
|
42193
|
+
byteSize,
|
|
42194
|
+
fileName
|
|
42195
|
+
}
|
|
42196
|
+
});
|
|
42197
|
+
}
|
|
42198
|
+
} catch (error) {
|
|
42199
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42200
|
+
this.sessionLogger.warn(
|
|
42201
|
+
{ agentId: resolved.agentId, attachmentId: msg.attachmentId, kind: msg.kind, err: error },
|
|
42202
|
+
"fetch_attachment_bytes_request: failed to read"
|
|
42203
|
+
);
|
|
42204
|
+
this.emit({
|
|
42205
|
+
type: "fetch_attachment_bytes_response",
|
|
42206
|
+
payload: { requestId: msg.requestId, ok: false, error: message }
|
|
42207
|
+
});
|
|
42208
|
+
}
|
|
42209
|
+
}
|
|
40829
42210
|
async handleWaitForFinish(agentIdOrIdentifier, requestId, timeoutMs) {
|
|
40830
42211
|
const resolved = await this.resolveAgentIdentifier(agentIdOrIdentifier);
|
|
40831
42212
|
if (!resolved.ok) {
|
|
@@ -41970,6 +43351,7 @@ ${details}`.trim());
|
|
|
41970
43351
|
status: w.status,
|
|
41971
43352
|
cwd: w.cwd,
|
|
41972
43353
|
prompt: w.prompt,
|
|
43354
|
+
name: w.name,
|
|
41973
43355
|
createdAt: w.createdAt,
|
|
41974
43356
|
updatedAt: w.updatedAt
|
|
41975
43357
|
}));
|
|
@@ -43044,6 +44426,64 @@ var StoredLoopsSchema = z39.array(LoopRecordSchema2);
|
|
|
43044
44426
|
import { z as z40 } from "zod";
|
|
43045
44427
|
var StoredQuestsSchema = z40.array(QuestRecordSchema);
|
|
43046
44428
|
|
|
44429
|
+
// ../server/src/server/quest/quest-metadata-generator.ts
|
|
44430
|
+
import { z as z41 } from "zod";
|
|
44431
|
+
|
|
44432
|
+
// ../server/src/server/quest/orchestrator-mcp.ts
|
|
44433
|
+
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
44434
|
+
import { z as z42 } from "zod";
|
|
44435
|
+
var HANDOFF_INPUT_SHAPE = {
|
|
44436
|
+
rolePath: z42.string().min(1).describe(
|
|
44437
|
+
"Absolute filesystem path to the role .md file the worker should adopt as its system prompt. Must be one of the role file paths listed in your team roster \u2014 do not invent paths."
|
|
44438
|
+
),
|
|
44439
|
+
task: z42.string().min(1).describe(
|
|
44440
|
+
"Concrete, self-contained instruction for the worker. The worker has its own toolbelt and reads the role at rolePath as its system prompt \u2014 write the task as a normal user message describing what to do, what inputs to read, and where to write outputs. Reference the hivemind doc by absolute path if relevant."
|
|
44441
|
+
)
|
|
44442
|
+
};
|
|
44443
|
+
|
|
44444
|
+
// ../server/src/server/quest/runner-orchestrator.ts
|
|
44445
|
+
var FALLBACK_QUEEN_PROMPT_TEMPLATE = [
|
|
44446
|
+
"you are going to send out agents, but first you get aquanted with 'the team'",
|
|
44447
|
+
"You don't go do the work yourself!",
|
|
44448
|
+
"",
|
|
44449
|
+
"this team has very deep knowledge of the task at hand. they have very detailed skills and output prefs so they are smarter than you. don't assume otherwise.",
|
|
44450
|
+
"",
|
|
44451
|
+
"the team is split up in categories. you will try to consolidate all of their knowledge into an approach for the task below.",
|
|
44452
|
+
"",
|
|
44453
|
+
"",
|
|
44454
|
+
"The team:",
|
|
44455
|
+
"",
|
|
44456
|
+
"{{team}}",
|
|
44457
|
+
"",
|
|
44458
|
+
"===",
|
|
44459
|
+
"",
|
|
44460
|
+
"The task at hand:",
|
|
44461
|
+
"",
|
|
44462
|
+
"",
|
|
44463
|
+
">>>>>",
|
|
44464
|
+
"{{task}}",
|
|
44465
|
+
"<<<<<<<<<<",
|
|
44466
|
+
"",
|
|
44467
|
+
"Again, whatever you are thinking of now I repeat: You don't go do the work yourself!",
|
|
44468
|
+
"What your job IS tho:",
|
|
44469
|
+
"",
|
|
44470
|
+
"Now you have full knowledge of what the user wants and how he composed his 'team'",
|
|
44471
|
+
"you do not delegate this team as subagents but you spawn new handoff sessions, you decide on what can be done in paralel and what needs to happen before the next can start. stage. the amount of handof sessions you spawn is equal to the amount of team entries in your prompt.",
|
|
44472
|
+
"",
|
|
44473
|
+
"",
|
|
44474
|
+
"",
|
|
44475
|
+
"We are going to use a hivemind dock in the repo.",
|
|
44476
|
+
"What you can do, is now startup that hivemind doc in /.hivemind/orchestrator-{{questId}}.md",
|
|
44477
|
+
"",
|
|
44478
|
+
"",
|
|
44479
|
+
"You instruct every handoff with a clear instruction on it's task.",
|
|
44480
|
+
"But you also make sure that each handoff knows the greater context of the end goal and knows of the existence of the hivemind document.",
|
|
44481
|
+
"it should know that it can write to it but first needs to check if there are no conflicts.",
|
|
44482
|
+
"",
|
|
44483
|
+
"",
|
|
44484
|
+
"do not use subagents because these are emphetic. we need full sessions."
|
|
44485
|
+
].join("\n");
|
|
44486
|
+
|
|
43047
44487
|
// ../server/src/server/quest/runner-ralph.ts
|
|
43048
44488
|
import { promisify as promisify4 } from "node:util";
|
|
43049
44489
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -43051,13 +44491,13 @@ var execFileAsync3 = promisify4(execFile3);
|
|
|
43051
44491
|
var MAX_VERIFY_OUTPUT_BYTES2 = 64 * 1024;
|
|
43052
44492
|
|
|
43053
44493
|
// ../server/src/shared/connection-offer.ts
|
|
43054
|
-
import { z as
|
|
43055
|
-
var ConnectionOfferV2Schema =
|
|
43056
|
-
v:
|
|
43057
|
-
serverId:
|
|
43058
|
-
daemonPublicKeyB64:
|
|
43059
|
-
relay:
|
|
43060
|
-
endpoint:
|
|
44494
|
+
import { z as z43 } from "zod";
|
|
44495
|
+
var ConnectionOfferV2Schema = z43.object({
|
|
44496
|
+
v: z43.literal(2),
|
|
44497
|
+
serverId: z43.string().min(1),
|
|
44498
|
+
daemonPublicKeyB64: z43.string().min(1),
|
|
44499
|
+
relay: z43.object({
|
|
44500
|
+
endpoint: z43.string().min(1)
|
|
43061
44501
|
})
|
|
43062
44502
|
});
|
|
43063
44503
|
|
|
@@ -43090,22 +44530,22 @@ function isRelayClientWebSocketUrl(url) {
|
|
|
43090
44530
|
}
|
|
43091
44531
|
|
|
43092
44532
|
// ../server/src/server/config.ts
|
|
43093
|
-
import
|
|
43094
|
-
import { z as
|
|
44533
|
+
import path19 from "node:path";
|
|
44534
|
+
import { z as z47 } from "zod";
|
|
43095
44535
|
|
|
43096
44536
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
43097
|
-
import { z as
|
|
44537
|
+
import { z as z46 } from "zod";
|
|
43098
44538
|
|
|
43099
44539
|
// ../server/src/server/speech/providers/local/config.ts
|
|
43100
|
-
import
|
|
43101
|
-
import { z as
|
|
43102
|
-
var DEFAULT_LOCAL_MODELS_SUBDIR =
|
|
43103
|
-
var NumberLikeSchema2 =
|
|
43104
|
-
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(
|
|
43105
|
-
var OptionalIntegerSchema = NumberLikeSchema2.pipe(
|
|
43106
|
-
var LocalSpeechResolutionSchema =
|
|
43107
|
-
includeProviderConfig:
|
|
43108
|
-
modelsDir:
|
|
44540
|
+
import path18 from "node:path";
|
|
44541
|
+
import { z as z44 } from "zod";
|
|
44542
|
+
var DEFAULT_LOCAL_MODELS_SUBDIR = path18.join("models", "local-speech");
|
|
44543
|
+
var NumberLikeSchema2 = z44.union([z44.number(), z44.string().trim().min(1)]);
|
|
44544
|
+
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z44.coerce.number().finite()).optional();
|
|
44545
|
+
var OptionalIntegerSchema = NumberLikeSchema2.pipe(z44.coerce.number().int()).optional();
|
|
44546
|
+
var LocalSpeechResolutionSchema = z44.object({
|
|
44547
|
+
includeProviderConfig: z44.boolean(),
|
|
44548
|
+
modelsDir: z44.string().trim().min(1),
|
|
43109
44549
|
dictationLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
43110
44550
|
voiceLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
43111
44551
|
voiceLocalTtsModel: LocalTtsModelIdSchema.default(DEFAULT_LOCAL_TTS_MODEL),
|
|
@@ -43126,7 +44566,7 @@ function resolveLocalSpeechConfig(params) {
|
|
|
43126
44566
|
const includeProviderConfig = shouldIncludeLocalProviderConfig(params);
|
|
43127
44567
|
const parsed = LocalSpeechResolutionSchema.parse({
|
|
43128
44568
|
includeProviderConfig,
|
|
43129
|
-
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ??
|
|
44569
|
+
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ?? path18.join(params.appostleHome, DEFAULT_LOCAL_MODELS_SUBDIR),
|
|
43130
44570
|
dictationLocalSttModel: params.env.APPOSTLE_DICTATION_LOCAL_STT_MODEL ?? persistedLocalFeatureModel(
|
|
43131
44571
|
params.providers.dictationStt.provider,
|
|
43132
44572
|
params.providers.dictationStt.enabled,
|
|
@@ -43161,17 +44601,17 @@ function resolveLocalSpeechConfig(params) {
|
|
|
43161
44601
|
}
|
|
43162
44602
|
|
|
43163
44603
|
// ../server/src/server/speech/speech-types.ts
|
|
43164
|
-
import { z as
|
|
43165
|
-
var SpeechProviderIdSchema2 =
|
|
43166
|
-
var RequestedSpeechProviderSchema =
|
|
44604
|
+
import { z as z45 } from "zod";
|
|
44605
|
+
var SpeechProviderIdSchema2 = z45.enum(["openai", "local"]);
|
|
44606
|
+
var RequestedSpeechProviderSchema = z45.object({
|
|
43167
44607
|
provider: SpeechProviderIdSchema2,
|
|
43168
|
-
explicit:
|
|
43169
|
-
enabled:
|
|
44608
|
+
explicit: z45.boolean(),
|
|
44609
|
+
enabled: z45.boolean().optional()
|
|
43170
44610
|
});
|
|
43171
44611
|
|
|
43172
44612
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
43173
|
-
var OptionalSpeechProviderSchema =
|
|
43174
|
-
var OptionalBooleanFlagSchema =
|
|
44613
|
+
var OptionalSpeechProviderSchema = z46.string().trim().toLowerCase().pipe(SpeechProviderIdSchema2).optional();
|
|
44614
|
+
var OptionalBooleanFlagSchema = z46.union([z46.boolean(), z46.string().trim().toLowerCase()]).optional().transform((value) => {
|
|
43175
44615
|
if (typeof value === "boolean") {
|
|
43176
44616
|
return value;
|
|
43177
44617
|
}
|
|
@@ -43186,7 +44626,7 @@ var OptionalBooleanFlagSchema = z44.union([z44.boolean(), z44.string().trim().to
|
|
|
43186
44626
|
}
|
|
43187
44627
|
return void 0;
|
|
43188
44628
|
});
|
|
43189
|
-
var RequestedSpeechProvidersSchema =
|
|
44629
|
+
var RequestedSpeechProvidersSchema = z46.object({
|
|
43190
44630
|
dictationStt: OptionalSpeechProviderSchema.default("local"),
|
|
43191
44631
|
voiceTurnDetection: OptionalSpeechProviderSchema.default("local"),
|
|
43192
44632
|
voiceStt: OptionalSpeechProviderSchema.default("local"),
|
|
@@ -43295,9 +44735,9 @@ function parseBooleanEnv(value) {
|
|
|
43295
44735
|
}
|
|
43296
44736
|
return void 0;
|
|
43297
44737
|
}
|
|
43298
|
-
var OptionalVoiceLlmProviderSchema =
|
|
44738
|
+
var OptionalVoiceLlmProviderSchema = z47.union([z47.string(), z47.null(), z47.undefined()]).transform(
|
|
43299
44739
|
(value) => typeof value === "string" ? value.trim().toLowerCase() : null
|
|
43300
|
-
).pipe(
|
|
44740
|
+
).pipe(z47.union([AgentProviderSchema, z47.null()]));
|
|
43301
44741
|
function parseOptionalVoiceLlmProvider(value) {
|
|
43302
44742
|
const parsed = OptionalVoiceLlmProviderSchema.safeParse(value);
|
|
43303
44743
|
return parsed.success ? parsed.data : null;
|
|
@@ -43382,7 +44822,7 @@ function loadConfig(appostleHome, options) {
|
|
|
43382
44822
|
chromeEnabled,
|
|
43383
44823
|
mcpDebug: env.MCP_DEBUG === "1",
|
|
43384
44824
|
daemonIcon,
|
|
43385
|
-
agentStoragePath:
|
|
44825
|
+
agentStoragePath: path19.join(appostleHome, "agents"),
|
|
43386
44826
|
staticDir: "public",
|
|
43387
44827
|
agentClients: {},
|
|
43388
44828
|
relayEnabled,
|
|
@@ -44600,12 +46040,12 @@ var DaemonClient = class {
|
|
|
44600
46040
|
timeout: 1e4
|
|
44601
46041
|
});
|
|
44602
46042
|
}
|
|
44603
|
-
async openInEditor(
|
|
46043
|
+
async openInEditor(path26, editorId, requestId) {
|
|
44604
46044
|
return this.sendCorrelatedSessionRequest({
|
|
44605
46045
|
requestId,
|
|
44606
46046
|
message: {
|
|
44607
46047
|
type: "open_in_editor_request",
|
|
44608
|
-
path:
|
|
46048
|
+
path: path26,
|
|
44609
46049
|
editorId
|
|
44610
46050
|
},
|
|
44611
46051
|
responseType: "open_in_editor_response",
|
|
@@ -44705,6 +46145,7 @@ var DaemonClient = class {
|
|
|
44705
46145
|
...options.clientMessageId ? { clientMessageId: options.clientMessageId } : {},
|
|
44706
46146
|
...options.outputSchema ? { outputSchema: options.outputSchema } : {},
|
|
44707
46147
|
...options.images && options.images.length > 0 ? { images: options.images } : {},
|
|
46148
|
+
...options.files && options.files.length > 0 ? { files: options.files } : {},
|
|
44708
46149
|
...options.attachments && options.attachments.length > 0 ? { attachments: options.attachments } : {},
|
|
44709
46150
|
...options.git ? { git: options.git } : {},
|
|
44710
46151
|
...options.worktreeName ? { worktreeName: options.worktreeName } : {},
|
|
@@ -44930,6 +46371,7 @@ var DaemonClient = class {
|
|
|
44930
46371
|
text,
|
|
44931
46372
|
...messageId ? { messageId } : {},
|
|
44932
46373
|
...options?.images ? { images: options.images } : {},
|
|
46374
|
+
...options?.files ? { files: options.files } : {},
|
|
44933
46375
|
...options?.attachments ? { attachments: options.attachments } : {}
|
|
44934
46376
|
});
|
|
44935
46377
|
const payload = await this.sendRequest({
|
|
@@ -44954,6 +46396,157 @@ var DaemonClient = class {
|
|
|
44954
46396
|
async sendMessage(agentId, text, options) {
|
|
44955
46397
|
await this.sendAgentMessage(agentId, text, options);
|
|
44956
46398
|
}
|
|
46399
|
+
/**
|
|
46400
|
+
* List the files the user has attached to this agent's chat (the "Files"
|
|
46401
|
+
* tab in an active session). Returns newest first; the daemon serves the
|
|
46402
|
+
* manifest stored under `<workspace>/.appostle/uploaded_files/`.
|
|
46403
|
+
*/
|
|
46404
|
+
async listSessionUploads(agentId) {
|
|
46405
|
+
const requestId = this.createRequestId();
|
|
46406
|
+
const message = SessionInboundMessageSchema.parse({
|
|
46407
|
+
type: "list_session_uploads_request",
|
|
46408
|
+
requestId,
|
|
46409
|
+
agentId
|
|
46410
|
+
});
|
|
46411
|
+
const payload = await this.sendRequest({
|
|
46412
|
+
requestId,
|
|
46413
|
+
message,
|
|
46414
|
+
timeout: 15e3,
|
|
46415
|
+
options: { skipQueue: true },
|
|
46416
|
+
select: (msg) => {
|
|
46417
|
+
if (msg.type !== "list_session_uploads_response") return null;
|
|
46418
|
+
if (msg.payload.requestId !== requestId) return null;
|
|
46419
|
+
return msg.payload;
|
|
46420
|
+
}
|
|
46421
|
+
});
|
|
46422
|
+
if (!payload.ok) {
|
|
46423
|
+
throw new Error(payload.error || "Failed to list session uploads");
|
|
46424
|
+
}
|
|
46425
|
+
return payload.uploads;
|
|
46426
|
+
}
|
|
46427
|
+
/**
|
|
46428
|
+
* Delete a single uploaded file. Removes both the manifest entry and the
|
|
46429
|
+
* file on disk; the operation is idempotent — deleting a no-longer-existent
|
|
46430
|
+
* upload throws so the caller can refresh the listing.
|
|
46431
|
+
*/
|
|
46432
|
+
async deleteSessionUpload(agentId, uploadId) {
|
|
46433
|
+
const requestId = this.createRequestId();
|
|
46434
|
+
const message = SessionInboundMessageSchema.parse({
|
|
46435
|
+
type: "delete_session_upload_request",
|
|
46436
|
+
requestId,
|
|
46437
|
+
agentId,
|
|
46438
|
+
uploadId
|
|
46439
|
+
});
|
|
46440
|
+
const payload = await this.sendRequest({
|
|
46441
|
+
requestId,
|
|
46442
|
+
message,
|
|
46443
|
+
timeout: 15e3,
|
|
46444
|
+
options: { skipQueue: true },
|
|
46445
|
+
select: (msg) => {
|
|
46446
|
+
if (msg.type !== "delete_session_upload_response") return null;
|
|
46447
|
+
if (msg.payload.requestId !== requestId) return null;
|
|
46448
|
+
return msg.payload;
|
|
46449
|
+
}
|
|
46450
|
+
});
|
|
46451
|
+
if (!payload.ok) {
|
|
46452
|
+
throw new Error(payload.error || "Failed to delete session upload");
|
|
46453
|
+
}
|
|
46454
|
+
}
|
|
46455
|
+
/**
|
|
46456
|
+
* List the images the user has attached to this agent's chat (the "Images"
|
|
46457
|
+
* tab in the uploads modal). Returns newest first; the daemon scans
|
|
46458
|
+
* `<workspace>/.appostle/uploaded_images/` directly — there's no manifest.
|
|
46459
|
+
*/
|
|
46460
|
+
async listSessionImages(agentId) {
|
|
46461
|
+
const requestId = this.createRequestId();
|
|
46462
|
+
const message = SessionInboundMessageSchema.parse({
|
|
46463
|
+
type: "list_session_images_request",
|
|
46464
|
+
requestId,
|
|
46465
|
+
agentId
|
|
46466
|
+
});
|
|
46467
|
+
const payload = await this.sendRequest({
|
|
46468
|
+
requestId,
|
|
46469
|
+
message,
|
|
46470
|
+
timeout: 15e3,
|
|
46471
|
+
options: { skipQueue: true },
|
|
46472
|
+
select: (msg) => {
|
|
46473
|
+
if (msg.type !== "list_session_images_response") return null;
|
|
46474
|
+
if (msg.payload.requestId !== requestId) return null;
|
|
46475
|
+
return msg.payload;
|
|
46476
|
+
}
|
|
46477
|
+
});
|
|
46478
|
+
if (!payload.ok) {
|
|
46479
|
+
throw new Error(payload.error || "Failed to list session images");
|
|
46480
|
+
}
|
|
46481
|
+
return payload.images;
|
|
46482
|
+
}
|
|
46483
|
+
/**
|
|
46484
|
+
* Delete a single uploaded image by workspace-relative path. The daemon
|
|
46485
|
+
* traversal-guards the path so it cannot escape `.appostle/uploaded_images/`.
|
|
46486
|
+
*/
|
|
46487
|
+
async deleteSessionImage(agentId, relativePath) {
|
|
46488
|
+
const requestId = this.createRequestId();
|
|
46489
|
+
const message = SessionInboundMessageSchema.parse({
|
|
46490
|
+
type: "delete_session_image_request",
|
|
46491
|
+
requestId,
|
|
46492
|
+
agentId,
|
|
46493
|
+
relativePath
|
|
46494
|
+
});
|
|
46495
|
+
const payload = await this.sendRequest({
|
|
46496
|
+
requestId,
|
|
46497
|
+
message,
|
|
46498
|
+
timeout: 15e3,
|
|
46499
|
+
options: { skipQueue: true },
|
|
46500
|
+
select: (msg) => {
|
|
46501
|
+
if (msg.type !== "delete_session_image_response") return null;
|
|
46502
|
+
if (msg.payload.requestId !== requestId) return null;
|
|
46503
|
+
return msg.payload;
|
|
46504
|
+
}
|
|
46505
|
+
});
|
|
46506
|
+
if (!payload.ok) {
|
|
46507
|
+
throw new Error(payload.error || "Failed to delete session image");
|
|
46508
|
+
}
|
|
46509
|
+
}
|
|
46510
|
+
/**
|
|
46511
|
+
* Fetch attachment bytes by id from the daemon. The daemon is the canonical
|
|
46512
|
+
* source for image and file bytes — every paired client (Electron after
|
|
46513
|
+
* Cmd-R, phone, web) hits this RPC to render thumbnails and previews.
|
|
46514
|
+
*
|
|
46515
|
+
* `relativePath` comes from the user_message timeline echo's `daemonRef`.
|
|
46516
|
+
* Returns base64-encoded bytes plus mime/size.
|
|
46517
|
+
*/
|
|
46518
|
+
async fetchAttachmentBytes(input) {
|
|
46519
|
+
const requestId = this.createRequestId();
|
|
46520
|
+
const message = SessionInboundMessageSchema.parse({
|
|
46521
|
+
type: "fetch_attachment_bytes_request",
|
|
46522
|
+
requestId,
|
|
46523
|
+
agentId: input.agentId,
|
|
46524
|
+
kind: input.kind,
|
|
46525
|
+
attachmentId: input.attachmentId,
|
|
46526
|
+
relativePath: input.relativePath
|
|
46527
|
+
});
|
|
46528
|
+
const payload = await this.sendRequest({
|
|
46529
|
+
requestId,
|
|
46530
|
+
message,
|
|
46531
|
+
// Image bytes can be a couple MB; allow more time than a typical RPC.
|
|
46532
|
+
timeout: 3e4,
|
|
46533
|
+
options: { skipQueue: true },
|
|
46534
|
+
select: (msg) => {
|
|
46535
|
+
if (msg.type !== "fetch_attachment_bytes_response") return null;
|
|
46536
|
+
if (msg.payload.requestId !== requestId) return null;
|
|
46537
|
+
return msg.payload;
|
|
46538
|
+
}
|
|
46539
|
+
});
|
|
46540
|
+
if (!payload.ok) {
|
|
46541
|
+
throw new Error(payload.error || "Failed to fetch attachment bytes");
|
|
46542
|
+
}
|
|
46543
|
+
return {
|
|
46544
|
+
data: payload.data,
|
|
46545
|
+
mimeType: payload.mimeType,
|
|
46546
|
+
byteSize: payload.byteSize,
|
|
46547
|
+
...payload.fileName !== void 0 ? { fileName: payload.fileName } : {}
|
|
46548
|
+
};
|
|
46549
|
+
}
|
|
44957
46550
|
async cancelAgent(agentId) {
|
|
44958
46551
|
const requestId = this.createRequestId();
|
|
44959
46552
|
const message = SessionInboundMessageSchema.parse({
|
|
@@ -45867,13 +47460,13 @@ var DaemonClient = class {
|
|
|
45867
47460
|
// ============================================================================
|
|
45868
47461
|
// File Explorer
|
|
45869
47462
|
// ============================================================================
|
|
45870
|
-
async exploreFileSystem(cwd,
|
|
47463
|
+
async exploreFileSystem(cwd, path26, mode = "list", requestId) {
|
|
45871
47464
|
return this.sendCorrelatedSessionRequest({
|
|
45872
47465
|
requestId,
|
|
45873
47466
|
message: {
|
|
45874
47467
|
type: "file_explorer_request",
|
|
45875
47468
|
cwd,
|
|
45876
|
-
path:
|
|
47469
|
+
path: path26,
|
|
45877
47470
|
mode
|
|
45878
47471
|
},
|
|
45879
47472
|
responseType: "file_explorer_response",
|
|
@@ -45885,13 +47478,13 @@ var DaemonClient = class {
|
|
|
45885
47478
|
* allowlists extensions (currently `.md` only) — callers don't need to
|
|
45886
47479
|
* re-check. Used by the plan-todos UI to rewrite plan-file frontmatter.
|
|
45887
47480
|
*/
|
|
45888
|
-
async writeFile(cwd,
|
|
47481
|
+
async writeFile(cwd, path26, content, requestId) {
|
|
45889
47482
|
return this.sendCorrelatedSessionRequest({
|
|
45890
47483
|
requestId,
|
|
45891
47484
|
message: {
|
|
45892
47485
|
type: "file_write_request",
|
|
45893
47486
|
cwd,
|
|
45894
|
-
path:
|
|
47487
|
+
path: path26,
|
|
45895
47488
|
content
|
|
45896
47489
|
},
|
|
45897
47490
|
responseType: "file_write_response",
|
|
@@ -45904,42 +47497,42 @@ var DaemonClient = class {
|
|
|
45904
47497
|
* action is the only consumer). Returns the daemon's structured response
|
|
45905
47498
|
* so callers can surface the error in the UI.
|
|
45906
47499
|
*/
|
|
45907
|
-
async deleteFile(cwd,
|
|
47500
|
+
async deleteFile(cwd, path26, requestId) {
|
|
45908
47501
|
return this.sendCorrelatedSessionRequest({
|
|
45909
47502
|
requestId,
|
|
45910
47503
|
message: {
|
|
45911
47504
|
type: "file_delete_request",
|
|
45912
47505
|
cwd,
|
|
45913
|
-
path:
|
|
47506
|
+
path: path26
|
|
45914
47507
|
},
|
|
45915
47508
|
responseType: "file_delete_response",
|
|
45916
47509
|
timeout: 1e4
|
|
45917
47510
|
});
|
|
45918
47511
|
}
|
|
45919
|
-
async requestDownloadToken(cwd,
|
|
47512
|
+
async requestDownloadToken(cwd, path26, requestId) {
|
|
45920
47513
|
return this.sendCorrelatedSessionRequest({
|
|
45921
47514
|
requestId,
|
|
45922
47515
|
message: {
|
|
45923
47516
|
type: "file_download_token_request",
|
|
45924
47517
|
cwd,
|
|
45925
|
-
path:
|
|
47518
|
+
path: path26
|
|
45926
47519
|
},
|
|
45927
47520
|
responseType: "file_download_token_response",
|
|
45928
47521
|
timeout: 1e4
|
|
45929
47522
|
});
|
|
45930
47523
|
}
|
|
45931
|
-
async explorerDeleteEntry(cwd,
|
|
47524
|
+
async explorerDeleteEntry(cwd, path26, requestId) {
|
|
45932
47525
|
return this.sendCorrelatedSessionRequest({
|
|
45933
47526
|
requestId,
|
|
45934
|
-
message: { type: "file_explorer_delete_request", cwd, path:
|
|
47527
|
+
message: { type: "file_explorer_delete_request", cwd, path: path26 },
|
|
45935
47528
|
responseType: "file_explorer_delete_response",
|
|
45936
47529
|
timeout: 1e4
|
|
45937
47530
|
});
|
|
45938
47531
|
}
|
|
45939
|
-
async explorerMkdir(cwd,
|
|
47532
|
+
async explorerMkdir(cwd, path26, requestId) {
|
|
45940
47533
|
return this.sendCorrelatedSessionRequest({
|
|
45941
47534
|
requestId,
|
|
45942
|
-
message: { type: "file_mkdir_request", cwd, path:
|
|
47535
|
+
message: { type: "file_mkdir_request", cwd, path: path26 },
|
|
45943
47536
|
responseType: "file_mkdir_response",
|
|
45944
47537
|
timeout: 1e4
|
|
45945
47538
|
});
|
|
@@ -47329,16 +48922,16 @@ function resolveAgentConfig(options) {
|
|
|
47329
48922
|
}
|
|
47330
48923
|
|
|
47331
48924
|
// ../cli/src/utils/client.ts
|
|
47332
|
-
import
|
|
48925
|
+
import path20 from "node:path";
|
|
47333
48926
|
import WebSocket3 from "ws";
|
|
47334
48927
|
|
|
47335
48928
|
// ../cli/src/utils/client-id.ts
|
|
47336
48929
|
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "node:fs/promises";
|
|
47337
|
-
import { randomUUID as
|
|
47338
|
-
import { dirname as dirname5, join as
|
|
48930
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
48931
|
+
import { dirname as dirname5, join as join12 } from "node:path";
|
|
47339
48932
|
import { homedir as homedir6 } from "node:os";
|
|
47340
|
-
var CLIENT_SESSION_KEY_FILE =
|
|
47341
|
-
process.env.APPOSTLE_HOME ??
|
|
48933
|
+
var CLIENT_SESSION_KEY_FILE = join12(
|
|
48934
|
+
process.env.APPOSTLE_HOME ?? join12(homedir6(), ".appostle"),
|
|
47342
48935
|
"cli-client-id"
|
|
47343
48936
|
);
|
|
47344
48937
|
var cachedClientId = null;
|
|
@@ -47347,7 +48940,7 @@ function normalizeClientId2(value) {
|
|
|
47347
48940
|
return trimmed.length > 0 ? trimmed : null;
|
|
47348
48941
|
}
|
|
47349
48942
|
function generateClientId() {
|
|
47350
|
-
return `cid_${
|
|
48943
|
+
return `cid_${randomUUID6().replace(/-/g, "")}`;
|
|
47351
48944
|
}
|
|
47352
48945
|
async function getOrCreateCliClientId() {
|
|
47353
48946
|
if (cachedClientId) {
|
|
@@ -47405,7 +48998,7 @@ function isTcpDaemonHost(host) {
|
|
|
47405
48998
|
return host !== null && !isIpcDaemonHost(host);
|
|
47406
48999
|
}
|
|
47407
49000
|
function readPidSocketTarget(appostleHome) {
|
|
47408
|
-
const pidPath =
|
|
49001
|
+
const pidPath = path20.join(appostleHome, PID_FILENAME);
|
|
47409
49002
|
if (!existsSync10(pidPath)) {
|
|
47410
49003
|
return null;
|
|
47411
49004
|
}
|
|
@@ -47874,12 +49467,12 @@ function relativeTime(date) {
|
|
|
47874
49467
|
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
|
|
47875
49468
|
return `${Math.floor(seconds / 86400)} days ago`;
|
|
47876
49469
|
}
|
|
47877
|
-
function shortenPath(
|
|
49470
|
+
function shortenPath(path26) {
|
|
47878
49471
|
const home = process.env.HOME;
|
|
47879
|
-
if (home &&
|
|
47880
|
-
return "~" +
|
|
49472
|
+
if (home && path26.startsWith(home)) {
|
|
49473
|
+
return "~" + path26.slice(home.length);
|
|
47881
49474
|
}
|
|
47882
|
-
return
|
|
49475
|
+
return path26;
|
|
47883
49476
|
}
|
|
47884
49477
|
function normalizeModelId(modelId) {
|
|
47885
49478
|
if (typeof modelId !== "string") return null;
|
|
@@ -48722,7 +50315,7 @@ async function runStopCommand(id, options, _command) {
|
|
|
48722
50315
|
|
|
48723
50316
|
// ../cli/src/commands/agent/send.ts
|
|
48724
50317
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
48725
|
-
import { extname as
|
|
50318
|
+
import { extname as extname4, resolve as resolve11 } from "node:path";
|
|
48726
50319
|
var agentSendSchema = {
|
|
48727
50320
|
idField: "agentId",
|
|
48728
50321
|
columns: [
|
|
@@ -48736,10 +50329,10 @@ function addSendOptions(cmd) {
|
|
|
48736
50329
|
}
|
|
48737
50330
|
async function readImageFiles(imagePaths) {
|
|
48738
50331
|
const images = [];
|
|
48739
|
-
for (const
|
|
50332
|
+
for (const path26 of imagePaths) {
|
|
48740
50333
|
try {
|
|
48741
|
-
const buffer = await readFile5(
|
|
48742
|
-
const ext =
|
|
50334
|
+
const buffer = await readFile5(path26);
|
|
50335
|
+
const ext = extname4(path26).toLowerCase();
|
|
48743
50336
|
let mimeType = "image/jpeg";
|
|
48744
50337
|
switch (ext) {
|
|
48745
50338
|
case ".png":
|
|
@@ -48767,7 +50360,7 @@ async function readImageFiles(imagePaths) {
|
|
|
48767
50360
|
const message = err instanceof Error ? err.message : String(err);
|
|
48768
50361
|
const error = {
|
|
48769
50362
|
code: "IMAGE_READ_ERROR",
|
|
48770
|
-
message: `Failed to read image file: ${
|
|
50363
|
+
message: `Failed to read image file: ${path26}`,
|
|
48771
50364
|
details: message
|
|
48772
50365
|
};
|
|
48773
50366
|
throw error;
|
|
@@ -48940,12 +50533,12 @@ function createInspectSchema(agent) {
|
|
|
48940
50533
|
serialize: (_item) => agent
|
|
48941
50534
|
};
|
|
48942
50535
|
}
|
|
48943
|
-
function shortenPath2(
|
|
50536
|
+
function shortenPath2(path26) {
|
|
48944
50537
|
const home = process.env.HOME;
|
|
48945
|
-
if (home &&
|
|
48946
|
-
return "~" +
|
|
50538
|
+
if (home && path26.startsWith(home)) {
|
|
50539
|
+
return "~" + path26.slice(home.length);
|
|
48947
50540
|
}
|
|
48948
|
-
return
|
|
50541
|
+
return path26;
|
|
48949
50542
|
}
|
|
48950
50543
|
function formatCost(costUsd) {
|
|
48951
50544
|
if (costUsd === 0) return "$0.00";
|
|
@@ -49913,7 +51506,7 @@ import chalk3 from "chalk";
|
|
|
49913
51506
|
import { spawn as spawn7, spawnSync } from "node:child_process";
|
|
49914
51507
|
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
|
|
49915
51508
|
import { createRequire as createRequire3 } from "node:module";
|
|
49916
|
-
import
|
|
51509
|
+
import path21 from "node:path";
|
|
49917
51510
|
import { fileURLToPath } from "node:url";
|
|
49918
51511
|
var DETACHED_STARTUP_GRACE_MS = 1200;
|
|
49919
51512
|
var PID_POLL_INTERVAL_MS = 100;
|
|
@@ -49964,7 +51557,7 @@ function buildChildEnv(options) {
|
|
|
49964
51557
|
function resolveDaemonRunnerEntry() {
|
|
49965
51558
|
try {
|
|
49966
51559
|
const here = fileURLToPath(import.meta.url);
|
|
49967
|
-
const sibling =
|
|
51560
|
+
const sibling = path21.join(path21.dirname(here), "supervisor-entrypoint.js");
|
|
49968
51561
|
if (existsSync11(sibling)) {
|
|
49969
51562
|
return sibling;
|
|
49970
51563
|
}
|
|
@@ -49978,23 +51571,23 @@ function resolveDaemonRunnerEntry() {
|
|
|
49978
51571
|
"Unable to resolve @appostle/server package root for daemon runner (and no sibling supervisor-entrypoint.js was bundled)"
|
|
49979
51572
|
);
|
|
49980
51573
|
}
|
|
49981
|
-
let currentDir =
|
|
51574
|
+
let currentDir = path21.dirname(serverExportPath);
|
|
49982
51575
|
while (true) {
|
|
49983
|
-
const packageJsonPath =
|
|
51576
|
+
const packageJsonPath = path21.join(currentDir, "package.json");
|
|
49984
51577
|
if (existsSync11(packageJsonPath)) {
|
|
49985
51578
|
try {
|
|
49986
51579
|
const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
|
|
49987
51580
|
if (packageJson.name === "@appostle/server") {
|
|
49988
|
-
const distRunner =
|
|
51581
|
+
const distRunner = path21.join(currentDir, "dist", "scripts", "supervisor-entrypoint.js");
|
|
49989
51582
|
if (existsSync11(distRunner)) {
|
|
49990
51583
|
return distRunner;
|
|
49991
51584
|
}
|
|
49992
|
-
return
|
|
51585
|
+
return path21.join(currentDir, "scripts", "supervisor-entrypoint.ts");
|
|
49993
51586
|
}
|
|
49994
51587
|
} catch {
|
|
49995
51588
|
}
|
|
49996
51589
|
}
|
|
49997
|
-
const parentDir =
|
|
51590
|
+
const parentDir = path21.dirname(currentDir);
|
|
49998
51591
|
if (parentDir === currentDir) {
|
|
49999
51592
|
break;
|
|
50000
51593
|
}
|
|
@@ -50003,7 +51596,7 @@ function resolveDaemonRunnerEntry() {
|
|
|
50003
51596
|
throw new Error("Unable to resolve @appostle/server package root for daemon runner");
|
|
50004
51597
|
}
|
|
50005
51598
|
function pidFilePath(appostleHome) {
|
|
50006
|
-
return
|
|
51599
|
+
return path21.join(appostleHome, DAEMON_PID_FILENAME);
|
|
50007
51600
|
}
|
|
50008
51601
|
function readPidFile(pidPath) {
|
|
50009
51602
|
try {
|
|
@@ -50145,7 +51738,7 @@ function resolveLocalDaemonState(options = {}) {
|
|
|
50145
51738
|
const home = resolveAppostleHome(env);
|
|
50146
51739
|
const config = loadConfig(home, { env });
|
|
50147
51740
|
const pidPath = pidFilePath(home);
|
|
50148
|
-
const logPath =
|
|
51741
|
+
const logPath = path21.join(home, DAEMON_LOG_FILENAME);
|
|
50149
51742
|
const pidInfo = existsSync11(pidPath) ? readPidFile(pidPath) : null;
|
|
50150
51743
|
const running = pidInfo ? isProcessRunning(pidInfo.pid) : false;
|
|
50151
51744
|
const listen = pidInfo?.listen ?? config.listen;
|
|
@@ -50160,7 +51753,7 @@ function resolveLocalDaemonState(options = {}) {
|
|
|
50160
51753
|
};
|
|
50161
51754
|
}
|
|
50162
51755
|
function tailDaemonLog(home, lines = 30) {
|
|
50163
|
-
const logPath =
|
|
51756
|
+
const logPath = path21.join(resolveLocalAppostleHome(home), DAEMON_LOG_FILENAME);
|
|
50164
51757
|
return tailFile(logPath, lines);
|
|
50165
51758
|
}
|
|
50166
51759
|
async function startLocalDaemonDetached(options) {
|
|
@@ -50169,7 +51762,7 @@ async function startLocalDaemonDetached(options) {
|
|
|
50169
51762
|
}
|
|
50170
51763
|
const childEnv = buildChildEnv(options);
|
|
50171
51764
|
const appostleHome = resolveAppostleHome(childEnv);
|
|
50172
|
-
const logPath =
|
|
51765
|
+
const logPath = path21.join(appostleHome, DAEMON_LOG_FILENAME);
|
|
50173
51766
|
const daemonRunnerEntry = resolveDaemonRunnerEntry();
|
|
50174
51767
|
const child = spawn7(
|
|
50175
51768
|
process.execPath,
|
|
@@ -52894,22 +54487,22 @@ import { Command as Command13 } from "commander";
|
|
|
52894
54487
|
|
|
52895
54488
|
// ../cli/src/commands/worktree/ls.ts
|
|
52896
54489
|
import { homedir as homedir7 } from "node:os";
|
|
52897
|
-
import { basename as basename7, join as
|
|
52898
|
-
function shortenPath3(
|
|
54490
|
+
import { basename as basename7, join as join13, sep as sep3 } from "node:path";
|
|
54491
|
+
function shortenPath3(path26) {
|
|
52899
54492
|
const home = process.env.HOME;
|
|
52900
|
-
if (home &&
|
|
52901
|
-
return "~" +
|
|
54493
|
+
if (home && path26.startsWith(home)) {
|
|
54494
|
+
return "~" + path26.slice(home.length);
|
|
52902
54495
|
}
|
|
52903
|
-
return
|
|
54496
|
+
return path26;
|
|
52904
54497
|
}
|
|
52905
|
-
function extractWorktreeName(
|
|
52906
|
-
return basename7(
|
|
54498
|
+
function extractWorktreeName(path26) {
|
|
54499
|
+
return basename7(path26);
|
|
52907
54500
|
}
|
|
52908
54501
|
function resolveAppostleHomePath() {
|
|
52909
|
-
return process.env.APPOSTLE_HOME ??
|
|
54502
|
+
return process.env.APPOSTLE_HOME ?? join13(homedir7(), ".appostle");
|
|
52910
54503
|
}
|
|
52911
54504
|
function resolveAppostleWorktreesDir() {
|
|
52912
|
-
return
|
|
54505
|
+
return join13(resolveAppostleHomePath(), "worktrees");
|
|
52913
54506
|
}
|
|
52914
54507
|
function isAgentInManagedWorktree(agentCwd) {
|
|
52915
54508
|
const worktreesDir = resolveAppostleWorktreesDir();
|
|
@@ -52983,7 +54576,7 @@ async function runLsCommand7(options, _command) {
|
|
|
52983
54576
|
}
|
|
52984
54577
|
|
|
52985
54578
|
// ../cli/src/commands/worktree/archive.ts
|
|
52986
|
-
import
|
|
54579
|
+
import path22 from "path";
|
|
52987
54580
|
var archiveSchema2 = {
|
|
52988
54581
|
idField: "name",
|
|
52989
54582
|
columns: [
|
|
@@ -53027,7 +54620,7 @@ async function runArchiveCommand2(nameArg, options, _command) {
|
|
|
53027
54620
|
throw error;
|
|
53028
54621
|
}
|
|
53029
54622
|
const worktree = listResponse.worktrees.find((wt) => {
|
|
53030
|
-
const name =
|
|
54623
|
+
const name = path22.basename(wt.worktreePath);
|
|
53031
54624
|
return name === nameArg || wt.branchName === nameArg;
|
|
53032
54625
|
});
|
|
53033
54626
|
if (!worktree) {
|
|
@@ -53049,7 +54642,7 @@ async function runArchiveCommand2(nameArg, options, _command) {
|
|
|
53049
54642
|
};
|
|
53050
54643
|
throw error;
|
|
53051
54644
|
}
|
|
53052
|
-
const worktreeName =
|
|
54645
|
+
const worktreeName = path22.basename(worktree.worktreePath) || nameArg;
|
|
53053
54646
|
return {
|
|
53054
54647
|
type: "single",
|
|
53055
54648
|
data: {
|
|
@@ -53090,7 +54683,7 @@ function createWorktreeCommand() {
|
|
|
53090
54683
|
import { cancel, confirm, intro, isCancel, log, note, outro, spinner } from "@clack/prompts";
|
|
53091
54684
|
import { Command as Command14, Option as Option4 } from "commander";
|
|
53092
54685
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
53093
|
-
import
|
|
54686
|
+
import path23 from "node:path";
|
|
53094
54687
|
var DEFAULT_READY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
53095
54688
|
var OnboardCancelledError = class extends Error {
|
|
53096
54689
|
};
|
|
@@ -53133,7 +54726,7 @@ function toCliOverrides(options) {
|
|
|
53133
54726
|
return cliOverrides;
|
|
53134
54727
|
}
|
|
53135
54728
|
function savePersistedConfig2(appostleHome, config) {
|
|
53136
|
-
const configPath =
|
|
54729
|
+
const configPath = path23.join(appostleHome, "config.json");
|
|
53137
54730
|
writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
|
|
53138
54731
|
`);
|
|
53139
54732
|
}
|
|
@@ -53254,7 +54847,7 @@ ${recentLogs}` : null
|
|
|
53254
54847
|
);
|
|
53255
54848
|
}
|
|
53256
54849
|
function printNextSteps(pairingUrl, appostleHome, richUi) {
|
|
53257
|
-
const daemonLogPath =
|
|
54850
|
+
const daemonLogPath = path23.join(appostleHome, "daemon.log");
|
|
53258
54851
|
const nextStepsLines = [
|
|
53259
54852
|
pairingUrl ? "1. Open Appostle and scan the QR code above, or paste the pairing link." : "1. Open Appostle and connect to your daemon.",
|
|
53260
54853
|
"2. Web app: https://appostle.app",
|
|
@@ -53508,18 +55101,18 @@ function createCli() {
|
|
|
53508
55101
|
// ../cli/src/classify.ts
|
|
53509
55102
|
import { existsSync as existsSync12, statSync as statSync3 } from "node:fs";
|
|
53510
55103
|
import { homedir as homedir8 } from "node:os";
|
|
53511
|
-
import
|
|
55104
|
+
import path24 from "node:path";
|
|
53512
55105
|
function expandUserPath2(inputPath) {
|
|
53513
55106
|
if (inputPath === "~") {
|
|
53514
55107
|
return homedir8();
|
|
53515
55108
|
}
|
|
53516
55109
|
if (inputPath.startsWith("~/")) {
|
|
53517
|
-
return
|
|
55110
|
+
return path24.join(homedir8(), inputPath.slice(2));
|
|
53518
55111
|
}
|
|
53519
55112
|
return inputPath;
|
|
53520
55113
|
}
|
|
53521
55114
|
function isExistingDirectory(input) {
|
|
53522
|
-
const resolvedPath =
|
|
55115
|
+
const resolvedPath = path24.resolve(input.cwd, expandUserPath2(input.pathArg));
|
|
53523
55116
|
if (!existsSync12(resolvedPath)) {
|
|
53524
55117
|
return false;
|
|
53525
55118
|
}
|
|
@@ -53539,7 +55132,7 @@ function classifyInvocation(input) {
|
|
|
53539
55132
|
if (isExistingDirectory({ pathArg: firstArg, cwd: input.cwd })) {
|
|
53540
55133
|
return {
|
|
53541
55134
|
kind: "open-project",
|
|
53542
|
-
resolvedPath:
|
|
55135
|
+
resolvedPath: path24.resolve(input.cwd, expandUserPath2(firstArg))
|
|
53543
55136
|
};
|
|
53544
55137
|
}
|
|
53545
55138
|
return { kind: "cli", argv: input.argv };
|
|
@@ -53549,12 +55142,12 @@ function classifyInvocation(input) {
|
|
|
53549
55142
|
import { existsSync as existsSync13 } from "node:fs";
|
|
53550
55143
|
import { spawn as spawn8 } from "node:child_process";
|
|
53551
55144
|
import { homedir as homedir9 } from "node:os";
|
|
53552
|
-
import
|
|
55145
|
+
import path25 from "node:path";
|
|
53553
55146
|
function findDesktopApp() {
|
|
53554
55147
|
if (process.platform === "darwin") {
|
|
53555
55148
|
const candidates = [
|
|
53556
55149
|
"/Applications/Appostle.app",
|
|
53557
|
-
|
|
55150
|
+
path25.join(homedir9(), "Applications", "Appostle.app")
|
|
53558
55151
|
];
|
|
53559
55152
|
for (const candidate of candidates) {
|
|
53560
55153
|
if (existsSync13(candidate)) {
|
|
@@ -53567,7 +55160,7 @@ function findDesktopApp() {
|
|
|
53567
55160
|
const candidates = [
|
|
53568
55161
|
"/usr/bin/Appostle",
|
|
53569
55162
|
"/opt/Appostle/Appostle",
|
|
53570
|
-
|
|
55163
|
+
path25.join(homedir9(), "Applications", "Appostle.AppImage")
|
|
53571
55164
|
];
|
|
53572
55165
|
for (const candidate of candidates) {
|
|
53573
55166
|
if (existsSync13(candidate)) {
|
|
@@ -53581,7 +55174,7 @@ function findDesktopApp() {
|
|
|
53581
55174
|
if (!localAppData) {
|
|
53582
55175
|
return null;
|
|
53583
55176
|
}
|
|
53584
|
-
const candidate =
|
|
55177
|
+
const candidate = path25.join(localAppData, "Programs", "Appostle", "Appostle.exe");
|
|
53585
55178
|
return existsSync13(candidate) ? candidate : null;
|
|
53586
55179
|
}
|
|
53587
55180
|
return null;
|