appostle-installer 0.0.21 → 0.0.23
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.js +866 -574
- package/dist/appostle.js.map +4 -4
- package/dist/schema-templates/spacing.md +45 -1
- package/dist/worker.js +1420 -1053
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -259,7 +259,7 @@ import { createReadStream, unlinkSync, existsSync as existsSync20 } from "fs";
|
|
|
259
259
|
import { stat as stat9 } from "fs/promises";
|
|
260
260
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
261
261
|
import { hostname as getHostname2 } from "node:os";
|
|
262
|
-
import
|
|
262
|
+
import path36 from "node:path";
|
|
263
263
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
264
264
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
265
265
|
|
|
@@ -712,18 +712,18 @@ function resolveNodePtyPackageRoot() {
|
|
|
712
712
|
return null;
|
|
713
713
|
}
|
|
714
714
|
}
|
|
715
|
-
function ensureExecutableBit(
|
|
716
|
-
if (!existsSync2(
|
|
715
|
+
function ensureExecutableBit(path40) {
|
|
716
|
+
if (!existsSync2(path40)) {
|
|
717
717
|
return;
|
|
718
718
|
}
|
|
719
|
-
const stat10 = statSync(
|
|
719
|
+
const stat10 = statSync(path40);
|
|
720
720
|
if (!stat10.isFile()) {
|
|
721
721
|
return;
|
|
722
722
|
}
|
|
723
723
|
if ((stat10.mode & 73) === 73) {
|
|
724
724
|
return;
|
|
725
725
|
}
|
|
726
|
-
chmodSync(
|
|
726
|
+
chmodSync(path40, stat10.mode | 73);
|
|
727
727
|
}
|
|
728
728
|
function ensureNodePtySpawnHelperExecutableForCurrentPlatform(options = {}) {
|
|
729
729
|
const platform = options.platform ?? process.platform;
|
|
@@ -1341,11 +1341,11 @@ function parseGitRevParsePath(stdout) {
|
|
|
1341
1341
|
if (lines.length !== 1) {
|
|
1342
1342
|
return null;
|
|
1343
1343
|
}
|
|
1344
|
-
const
|
|
1345
|
-
if (!
|
|
1344
|
+
const path40 = lines[0]?.trim() ?? "";
|
|
1345
|
+
if (!path40 || path40.startsWith("--")) {
|
|
1346
1346
|
return null;
|
|
1347
1347
|
}
|
|
1348
|
-
return
|
|
1348
|
+
return path40;
|
|
1349
1349
|
}
|
|
1350
1350
|
function resolveGitRevParsePath(cwd, stdout) {
|
|
1351
1351
|
const parsed = parseGitRevParsePath(stdout);
|
|
@@ -2118,9 +2118,9 @@ async function deleteAppostleWorktree({
|
|
|
2118
2118
|
}
|
|
2119
2119
|
}
|
|
2120
2120
|
}
|
|
2121
|
-
async function pathExists(
|
|
2121
|
+
async function pathExists(path40) {
|
|
2122
2122
|
try {
|
|
2123
|
-
await stat(
|
|
2123
|
+
await stat(path40);
|
|
2124
2124
|
return true;
|
|
2125
2125
|
} catch (error) {
|
|
2126
2126
|
if (error.code === "ENOENT") {
|
|
@@ -2129,8 +2129,8 @@ async function pathExists(path39) {
|
|
|
2129
2129
|
throw error;
|
|
2130
2130
|
}
|
|
2131
2131
|
}
|
|
2132
|
-
async function removeDirectoryWithRetries(
|
|
2133
|
-
if (!await pathExists(
|
|
2132
|
+
async function removeDirectoryWithRetries(path40) {
|
|
2133
|
+
if (!await pathExists(path40)) {
|
|
2134
2134
|
return;
|
|
2135
2135
|
}
|
|
2136
2136
|
const delaysMs = [0, 100, 300, 700, 1500];
|
|
@@ -2140,17 +2140,17 @@ async function removeDirectoryWithRetries(path39) {
|
|
|
2140
2140
|
await new Promise((resolve15) => setTimeout(resolve15, delay));
|
|
2141
2141
|
}
|
|
2142
2142
|
try {
|
|
2143
|
-
await rm(
|
|
2144
|
-
if (!await pathExists(
|
|
2143
|
+
await rm(path40, { recursive: true, force: true });
|
|
2144
|
+
if (!await pathExists(path40)) {
|
|
2145
2145
|
return;
|
|
2146
2146
|
}
|
|
2147
|
-
lastError = new Error(`Directory still present after rm: ${
|
|
2147
|
+
lastError = new Error(`Directory still present after rm: ${path40}`);
|
|
2148
2148
|
} catch (error) {
|
|
2149
2149
|
lastError = error;
|
|
2150
2150
|
}
|
|
2151
2151
|
}
|
|
2152
|
-
if (await pathExists(
|
|
2153
|
-
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${
|
|
2152
|
+
if (await pathExists(path40)) {
|
|
2153
|
+
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${path40}`);
|
|
2154
2154
|
}
|
|
2155
2155
|
}
|
|
2156
2156
|
var createWorktree = async ({
|
|
@@ -3975,7 +3975,9 @@ var BrandEntrySchema = z10.object({
|
|
|
3975
3975
|
variables: z10.array(BrandVariableSchema).optional().default([]),
|
|
3976
3976
|
path: z10.string(),
|
|
3977
3977
|
modifiedAt: z10.string(),
|
|
3978
|
-
size: z10.number()
|
|
3978
|
+
size: z10.number(),
|
|
3979
|
+
/** Raw markdown body (frontmatter stripped). Optional, fed by the scanner. */
|
|
3980
|
+
body: z10.string().optional()
|
|
3979
3981
|
});
|
|
3980
3982
|
var BrandFrontmatterSchema = z10.object({
|
|
3981
3983
|
description: z10.string().optional(),
|
|
@@ -4152,6 +4154,22 @@ var BrandGenerateWireframeResponseSchema = z10.object({
|
|
|
4152
4154
|
error: z10.string().nullable()
|
|
4153
4155
|
})
|
|
4154
4156
|
});
|
|
4157
|
+
var BrandAnalyzePhotographyRequestSchema = z10.object({
|
|
4158
|
+
type: z10.literal("brands/analyze-photography"),
|
|
4159
|
+
requestId: z10.string(),
|
|
4160
|
+
workspaceRoot: z10.string(),
|
|
4161
|
+
brandPath: z10.string(),
|
|
4162
|
+
brief: z10.string(),
|
|
4163
|
+
referenceDescriptions: z10.array(z10.string()).optional()
|
|
4164
|
+
});
|
|
4165
|
+
var BrandAnalyzePhotographyResponseSchema = z10.object({
|
|
4166
|
+
type: z10.literal("brands/analyze-photography/response"),
|
|
4167
|
+
payload: z10.object({
|
|
4168
|
+
requestId: z10.string(),
|
|
4169
|
+
updatedCount: z10.number(),
|
|
4170
|
+
error: z10.string().nullable()
|
|
4171
|
+
})
|
|
4172
|
+
});
|
|
4155
4173
|
var RightFontEntrySchema = z10.object({
|
|
4156
4174
|
id: z10.string(),
|
|
4157
4175
|
name: z10.string(),
|
|
@@ -4251,10 +4269,14 @@ var MutableDaemonConfigSchema = z11.object({
|
|
|
4251
4269
|
icon: z11.string().optional()
|
|
4252
4270
|
}).passthrough().optional(),
|
|
4253
4271
|
chrome: z11.object({
|
|
4254
|
-
// When false, agents on this host won't
|
|
4255
|
-
//
|
|
4256
|
-
//
|
|
4257
|
-
|
|
4272
|
+
// When false, agents on this host won't have the claude-in-chrome
|
|
4273
|
+
// tools injected. Set to false on hosts where the client's browser
|
|
4274
|
+
// should not be controlled (e.g. headless/server-only hosts).
|
|
4275
|
+
enabled: z11.boolean()
|
|
4276
|
+
}).passthrough().optional(),
|
|
4277
|
+
playwright: z11.object({
|
|
4278
|
+
// When false, agents on this host won't have the Playwright MCP
|
|
4279
|
+
// server injected.
|
|
4258
4280
|
enabled: z11.boolean()
|
|
4259
4281
|
}).passthrough().optional()
|
|
4260
4282
|
}).passthrough();
|
|
@@ -4263,7 +4285,8 @@ var MutableDaemonConfigPatchSchema = z11.object({
|
|
|
4263
4285
|
identity: z11.object({
|
|
4264
4286
|
icon: z11.string().optional()
|
|
4265
4287
|
}).partial().passthrough().optional(),
|
|
4266
|
-
chrome: MutableDaemonConfigSchema.shape.chrome
|
|
4288
|
+
chrome: MutableDaemonConfigSchema.shape.chrome,
|
|
4289
|
+
playwright: MutableDaemonConfigSchema.shape.playwright
|
|
4267
4290
|
}).partial().passthrough();
|
|
4268
4291
|
var AgentStatusSchema = z11.enum(AGENT_LIFECYCLE_STATUSES);
|
|
4269
4292
|
var AgentModeSchema = z11.object({
|
|
@@ -6273,6 +6296,7 @@ var SessionInboundMessageSchema = z11.discriminatedUnion("type", [
|
|
|
6273
6296
|
BrandGenerateLayoutRequestSchema,
|
|
6274
6297
|
BrandLayoutQaNextRequestSchema,
|
|
6275
6298
|
BrandGenerateWireframeRequestSchema,
|
|
6299
|
+
BrandAnalyzePhotographyRequestSchema,
|
|
6276
6300
|
RightFontLibraryRequestSchema,
|
|
6277
6301
|
GoogleFontsCatalogRequestSchema,
|
|
6278
6302
|
GoogleFontsDownloadRequestSchema,
|
|
@@ -7909,6 +7933,7 @@ var SessionOutboundMessageSchema = z11.discriminatedUnion("type", [
|
|
|
7909
7933
|
BrandGenerateLayoutResponseSchema,
|
|
7910
7934
|
BrandLayoutQaNextResponseSchema,
|
|
7911
7935
|
BrandGenerateWireframeResponseSchema,
|
|
7936
|
+
BrandAnalyzePhotographyResponseSchema,
|
|
7912
7937
|
RightFontLibraryResponseSchema,
|
|
7913
7938
|
GoogleFontsCatalogResponseSchema,
|
|
7914
7939
|
GoogleFontsDownloadResponseSchema,
|
|
@@ -8157,7 +8182,7 @@ import { exec } from "node:child_process";
|
|
|
8157
8182
|
import { promisify as promisify3 } from "util";
|
|
8158
8183
|
import { join as join16, resolve as resolve10, sep as sep2 } from "path";
|
|
8159
8184
|
import { homedir as homedir6, hostname as osHostname } from "node:os";
|
|
8160
|
-
import { z as
|
|
8185
|
+
import { z as z40 } from "zod";
|
|
8161
8186
|
|
|
8162
8187
|
// ../server/src/server/persisted-config.ts
|
|
8163
8188
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -8454,6 +8479,9 @@ var PersistedConfigSchema = z14.object({
|
|
|
8454
8479
|
}).strict().optional(),
|
|
8455
8480
|
chrome: z14.object({
|
|
8456
8481
|
enabled: z14.boolean().optional()
|
|
8482
|
+
}).strict().optional(),
|
|
8483
|
+
playwright: z14.object({
|
|
8484
|
+
enabled: z14.boolean().optional()
|
|
8457
8485
|
}).strict().optional()
|
|
8458
8486
|
}).strict().transform(({ allowedHosts, ...daemon }) => {
|
|
8459
8487
|
const hostnames = daemon.hostnames ?? allowedHosts;
|
|
@@ -10480,14 +10508,14 @@ var DictationStreamManager = class {
|
|
|
10480
10508
|
PCM_CHANNELS,
|
|
10481
10509
|
PCM_BITS_PER_SAMPLE
|
|
10482
10510
|
);
|
|
10483
|
-
const
|
|
10511
|
+
const path40 = await maybePersistDictationDebugAudio(
|
|
10484
10512
|
wavBuffer,
|
|
10485
10513
|
{ sessionId: state.sessionId, dictationId: state.dictationId, format: "audio/wav" },
|
|
10486
10514
|
this.logger,
|
|
10487
10515
|
state.debugChunkWriter?.folder
|
|
10488
10516
|
);
|
|
10489
|
-
state.debugRecordingPath =
|
|
10490
|
-
return
|
|
10517
|
+
state.debugRecordingPath = path40;
|
|
10518
|
+
return path40;
|
|
10491
10519
|
}
|
|
10492
10520
|
failDictationStream(dictationId, error, retryable) {
|
|
10493
10521
|
this.emit({
|
|
@@ -12505,14 +12533,14 @@ function parseDiff(diffText) {
|
|
|
12505
12533
|
const firstLine = lines[0];
|
|
12506
12534
|
const isNew = section.includes("new file mode") || section.includes("--- /dev/null");
|
|
12507
12535
|
const isDeleted = section.includes("deleted file mode") || section.includes("+++ /dev/null");
|
|
12508
|
-
let
|
|
12536
|
+
let path40 = "unknown";
|
|
12509
12537
|
const pathMatch = firstLine.match(/a\/(.*?) b\//);
|
|
12510
12538
|
if (pathMatch) {
|
|
12511
|
-
|
|
12539
|
+
path40 = pathMatch[1];
|
|
12512
12540
|
} else {
|
|
12513
12541
|
const newFileMatch = firstLine.match(/b\/(.+)$/);
|
|
12514
12542
|
if (newFileMatch) {
|
|
12515
|
-
|
|
12543
|
+
path40 = newFileMatch[1];
|
|
12516
12544
|
}
|
|
12517
12545
|
}
|
|
12518
12546
|
const hunks = [];
|
|
@@ -12556,7 +12584,7 @@ function parseDiff(diffText) {
|
|
|
12556
12584
|
if (currentHunk) {
|
|
12557
12585
|
hunks.push(currentHunk);
|
|
12558
12586
|
}
|
|
12559
|
-
files.push({ path:
|
|
12587
|
+
files.push({ path: path40, isNew, isDeleted, additions, deletions, hunks });
|
|
12560
12588
|
}
|
|
12561
12589
|
return files;
|
|
12562
12590
|
}
|
|
@@ -12612,9 +12640,9 @@ function buildTokenLookup(lineMap, highlighted) {
|
|
|
12612
12640
|
}
|
|
12613
12641
|
return lookup;
|
|
12614
12642
|
}
|
|
12615
|
-
function buildFullFileTokenLookup(fileContent,
|
|
12643
|
+
function buildFullFileTokenLookup(fileContent, path40) {
|
|
12616
12644
|
const lookup = /* @__PURE__ */ new Map();
|
|
12617
|
-
const highlighted = highlightCode(fileContent,
|
|
12645
|
+
const highlighted = highlightCode(fileContent, path40);
|
|
12618
12646
|
for (let i = 0; i < highlighted.length; i++) {
|
|
12619
12647
|
lookup.set(i + 1, highlighted[i]);
|
|
12620
12648
|
}
|
|
@@ -14319,11 +14347,11 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
14319
14347
|
}
|
|
14320
14348
|
continue;
|
|
14321
14349
|
}
|
|
14322
|
-
const
|
|
14323
|
-
if (!
|
|
14350
|
+
const path40 = tabParts[1];
|
|
14351
|
+
if (!path40) continue;
|
|
14324
14352
|
const code = rawStatus[0];
|
|
14325
14353
|
changes.push({
|
|
14326
|
-
path:
|
|
14354
|
+
path: path40,
|
|
14327
14355
|
status: rawStatus,
|
|
14328
14356
|
isNew: code === "A",
|
|
14329
14357
|
isDeleted: code === "D"
|
|
@@ -14358,9 +14386,9 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
14358
14386
|
}
|
|
14359
14387
|
return Array.from(byPath.values());
|
|
14360
14388
|
}
|
|
14361
|
-
async function readGitFileContentAtRef(cwd, ref,
|
|
14389
|
+
async function readGitFileContentAtRef(cwd, ref, path40) {
|
|
14362
14390
|
try {
|
|
14363
|
-
const { stdout } = await runGitCommand(["show", `${ref}:${
|
|
14391
|
+
const { stdout } = await runGitCommand(["show", `${ref}:${path40}`], {
|
|
14364
14392
|
cwd,
|
|
14365
14393
|
env: READ_ONLY_GIT_ENV2
|
|
14366
14394
|
});
|
|
@@ -14421,21 +14449,21 @@ async function getTrackedNumstatByPath(cwd, ref, ignoreWhitespace = false) {
|
|
|
14421
14449
|
const additionsField = parts[0] ?? "";
|
|
14422
14450
|
const deletionsField = parts[1] ?? "";
|
|
14423
14451
|
const rawPath = parts.slice(2).join(" ");
|
|
14424
|
-
const
|
|
14425
|
-
if (!
|
|
14452
|
+
const path40 = normalizeNumstatPath(rawPath);
|
|
14453
|
+
if (!path40) {
|
|
14426
14454
|
continue;
|
|
14427
14455
|
}
|
|
14428
14456
|
if (additionsField === "-" || deletionsField === "-") {
|
|
14429
|
-
stats.set(
|
|
14457
|
+
stats.set(path40, { additions: 0, deletions: 0, isBinary: true });
|
|
14430
14458
|
continue;
|
|
14431
14459
|
}
|
|
14432
14460
|
const additions = Number.parseInt(additionsField, 10);
|
|
14433
14461
|
const deletions = Number.parseInt(deletionsField, 10);
|
|
14434
14462
|
if (Number.isNaN(additions) || Number.isNaN(deletions)) {
|
|
14435
|
-
stats.set(
|
|
14463
|
+
stats.set(path40, null);
|
|
14436
14464
|
continue;
|
|
14437
14465
|
}
|
|
14438
|
-
stats.set(
|
|
14466
|
+
stats.set(path40, { additions, deletions, isBinary: false });
|
|
14439
14467
|
}
|
|
14440
14468
|
return stats;
|
|
14441
14469
|
}
|
|
@@ -15127,10 +15155,10 @@ async function listUncommittedFiles(cwd) {
|
|
|
15127
15155
|
if (dest) results.push({ path: dest, changeType: code });
|
|
15128
15156
|
continue;
|
|
15129
15157
|
}
|
|
15130
|
-
const
|
|
15131
|
-
if (!
|
|
15158
|
+
const path40 = parts[1];
|
|
15159
|
+
if (!path40) continue;
|
|
15132
15160
|
if (code === "A" || code === "M" || code === "D") {
|
|
15133
|
-
results.push({ path:
|
|
15161
|
+
results.push({ path: path40, changeType: code });
|
|
15134
15162
|
}
|
|
15135
15163
|
}
|
|
15136
15164
|
} catch {
|
|
@@ -19959,6 +19987,7 @@ var ClaudeAgentClient = class {
|
|
|
19959
19987
|
runtimeSettings: this.runtimeSettings,
|
|
19960
19988
|
launchEnv: launchContext?.env,
|
|
19961
19989
|
chromeEnabled: launchContext?.chromeEnabled,
|
|
19990
|
+
playwrightEnabled: launchContext?.playwrightEnabled,
|
|
19962
19991
|
logger: this.logger,
|
|
19963
19992
|
queryFactory: this.queryFactory
|
|
19964
19993
|
});
|
|
@@ -19977,6 +20006,7 @@ var ClaudeAgentClient = class {
|
|
|
19977
20006
|
handle,
|
|
19978
20007
|
launchEnv: launchContext?.env,
|
|
19979
20008
|
chromeEnabled: launchContext?.chromeEnabled,
|
|
20009
|
+
playwrightEnabled: launchContext?.playwrightEnabled,
|
|
19980
20010
|
logger: this.logger,
|
|
19981
20011
|
queryFactory: this.queryFactory
|
|
19982
20012
|
});
|
|
@@ -20291,6 +20321,7 @@ var ClaudeAgentSession = class {
|
|
|
20291
20321
|
this.config = config;
|
|
20292
20322
|
this.launchEnv = options.launchEnv;
|
|
20293
20323
|
this.chromeEnabled = options.chromeEnabled ?? true;
|
|
20324
|
+
this.playwrightEnabled = options.playwrightEnabled ?? true;
|
|
20294
20325
|
this.defaults = options.defaults;
|
|
20295
20326
|
this.runtimeSettings = options.runtimeSettings;
|
|
20296
20327
|
this.logger = options.logger;
|
|
@@ -21054,7 +21085,8 @@ var ClaudeAgentSession = class {
|
|
|
21054
21085
|
} else {
|
|
21055
21086
|
const homeMcps = loadHomeMcpServers(this.logger);
|
|
21056
21087
|
if (homeMcps) {
|
|
21057
|
-
|
|
21088
|
+
const filtered = this.playwrightEnabled ? homeMcps : Object.fromEntries(Object.entries(homeMcps).filter(([k]) => k !== "playwright"));
|
|
21089
|
+
base.mcpServers = this.normalizeMcpServers(filtered);
|
|
21058
21090
|
}
|
|
21059
21091
|
}
|
|
21060
21092
|
if (this.config.model) {
|
|
@@ -22192,6 +22224,12 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
22192
22224
|
buildStructuredToolResult(server, tool4, output, input) {
|
|
22193
22225
|
const normalizedServer = server.toLowerCase();
|
|
22194
22226
|
const normalizedTool = tool4.toLowerCase();
|
|
22227
|
+
if (normalizedTool === "mcp__playwright__browser_take_screenshot" || normalizedTool === "browser_take_screenshot") {
|
|
22228
|
+
const imageOutput = this.tryInlinePlaywrightScreenshot(output, input);
|
|
22229
|
+
if (imageOutput) {
|
|
22230
|
+
return { output: imageOutput };
|
|
22231
|
+
}
|
|
22232
|
+
}
|
|
22195
22233
|
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))) {
|
|
22196
22234
|
const command = this.extractCommandText(input ?? {}) ?? "command";
|
|
22197
22235
|
return {
|
|
@@ -22233,8 +22271,46 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
22233
22271
|
};
|
|
22234
22272
|
}
|
|
22235
22273
|
}
|
|
22274
|
+
if (normalizedServer === "playwright" && normalizedTool === "browser_take_screenshot") {
|
|
22275
|
+
const imageDetail = this.tryReadPlaywrightScreenshot(output, input);
|
|
22276
|
+
if (imageDetail) return imageDetail;
|
|
22277
|
+
}
|
|
22236
22278
|
return void 0;
|
|
22237
22279
|
}
|
|
22280
|
+
/**
|
|
22281
|
+
* Playwright MCP's `browser_take_screenshot` saves the image to a local file
|
|
22282
|
+
* and returns a markdown link (`[Screenshot of viewport](./file.png)`).
|
|
22283
|
+
* Extract the path, resolve it against the agent CWD, read the bytes, and
|
|
22284
|
+
* return an image ToolCallDetail so the client renders it inline.
|
|
22285
|
+
*/
|
|
22286
|
+
tryReadPlaywrightScreenshot(output, input) {
|
|
22287
|
+
let filePath;
|
|
22288
|
+
if (input && typeof input.filename === "string") {
|
|
22289
|
+
filePath = input.filename;
|
|
22290
|
+
}
|
|
22291
|
+
if (!filePath) {
|
|
22292
|
+
const match = output.match(/\[Screenshot[^\]]*\]\(([^)]+)\)/i);
|
|
22293
|
+
if (match?.[1]) {
|
|
22294
|
+
filePath = match[1];
|
|
22295
|
+
}
|
|
22296
|
+
}
|
|
22297
|
+
if (!filePath) return void 0;
|
|
22298
|
+
const cwd = this.config.cwd;
|
|
22299
|
+
if (!cwd) return void 0;
|
|
22300
|
+
const abs = path9.isAbsolute(filePath) ? filePath : path9.resolve(cwd, filePath);
|
|
22301
|
+
try {
|
|
22302
|
+
const buf = fs4.readFileSync(abs);
|
|
22303
|
+
const ext = path9.extname(abs).toLowerCase();
|
|
22304
|
+
const mime = ext === ".jpeg" || ext === ".jpg" ? "image/jpeg" : "image/png";
|
|
22305
|
+
return {
|
|
22306
|
+
type: "image",
|
|
22307
|
+
toolName: "mcp__playwright__browser_take_screenshot",
|
|
22308
|
+
images: [{ mimeType: mime, data: buf.toString("base64") }]
|
|
22309
|
+
};
|
|
22310
|
+
} catch {
|
|
22311
|
+
return void 0;
|
|
22312
|
+
}
|
|
22313
|
+
}
|
|
22238
22314
|
mapPartialEvent(event, options) {
|
|
22239
22315
|
if (event.type === "content_block_start") {
|
|
22240
22316
|
const block = isClaudeContentChunk2(event.content_block) ? event.content_block : null;
|
|
@@ -22407,6 +22483,44 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
22407
22483
|
}
|
|
22408
22484
|
return input;
|
|
22409
22485
|
}
|
|
22486
|
+
/**
|
|
22487
|
+
* Playwright MCP returns `### Result\n- [Screenshot of viewport](./foo.png)`
|
|
22488
|
+
* and writes the file to disk instead of inlining the image. Resolve that
|
|
22489
|
+
* path against the agent cwd and turn the file into an Anthropic-style
|
|
22490
|
+
* image content block array, so downstream `extractImageToolDetail`
|
|
22491
|
+
* promotes the tool call to a renderable `image` detail.
|
|
22492
|
+
*
|
|
22493
|
+
* Returns `null` when the path cannot be resolved/read safely; the caller
|
|
22494
|
+
* then falls back to the default text output behavior.
|
|
22495
|
+
*/
|
|
22496
|
+
tryInlinePlaywrightScreenshot(output, input) {
|
|
22497
|
+
const cwd = this.config.cwd;
|
|
22498
|
+
if (!cwd) return null;
|
|
22499
|
+
let relPath;
|
|
22500
|
+
if (input && typeof input.filename === "string" && input.filename.length > 0) {
|
|
22501
|
+
relPath = input.filename;
|
|
22502
|
+
} else {
|
|
22503
|
+
const match = output.match(/\[Screenshot[^\]]*\]\(([^)]+)\)/);
|
|
22504
|
+
if (match?.[1]) {
|
|
22505
|
+
relPath = match[1];
|
|
22506
|
+
}
|
|
22507
|
+
}
|
|
22508
|
+
if (!relPath) return null;
|
|
22509
|
+
const resolved = path9.resolve(cwd, relPath);
|
|
22510
|
+
const cwdReal = path9.resolve(cwd);
|
|
22511
|
+
if (!resolved.startsWith(cwdReal + path9.sep) && resolved !== cwdReal) {
|
|
22512
|
+
return null;
|
|
22513
|
+
}
|
|
22514
|
+
let buf;
|
|
22515
|
+
try {
|
|
22516
|
+
buf = fs4.readFileSync(resolved);
|
|
22517
|
+
} catch {
|
|
22518
|
+
return null;
|
|
22519
|
+
}
|
|
22520
|
+
const ext = path9.extname(resolved).toLowerCase();
|
|
22521
|
+
const mimeType = ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : ext === ".webp" ? "image/webp" : ext === ".gif" ? "image/gif" : "image/png";
|
|
22522
|
+
return [{ type: "image", mimeType, data: buf.toString("base64") }];
|
|
22523
|
+
}
|
|
22410
22524
|
areToolInputsEqual(left, right) {
|
|
22411
22525
|
if (!left) {
|
|
22412
22526
|
return false;
|
|
@@ -23349,14 +23463,14 @@ function codexApplyPatchToUnifiedDiff(text) {
|
|
|
23349
23463
|
for (const line of lines) {
|
|
23350
23464
|
const directive = parseCodexApplyPatchDirective(line);
|
|
23351
23465
|
if (directive) {
|
|
23352
|
-
const
|
|
23353
|
-
if (
|
|
23466
|
+
const path40 = normalizeDiffHeaderPath(directive.path);
|
|
23467
|
+
if (path40.length > 0) {
|
|
23354
23468
|
if (output.length > 0 && output[output.length - 1] !== "") {
|
|
23355
23469
|
output.push("");
|
|
23356
23470
|
}
|
|
23357
|
-
const left = directive.kind === "add" ? "/dev/null" : `a/${
|
|
23358
|
-
const right = directive.kind === "delete" ? "/dev/null" : `b/${
|
|
23359
|
-
output.push(`diff --git a/${
|
|
23471
|
+
const left = directive.kind === "add" ? "/dev/null" : `a/${path40}`;
|
|
23472
|
+
const right = directive.kind === "delete" ? "/dev/null" : `b/${path40}`;
|
|
23473
|
+
output.push(`diff --git a/${path40} b/${path40}`);
|
|
23360
23474
|
output.push(`--- ${left}`);
|
|
23361
23475
|
output.push(`+++ ${right}`);
|
|
23362
23476
|
sawDiffContent = true;
|
|
@@ -23426,9 +23540,9 @@ function asEditTextFields(text) {
|
|
|
23426
23540
|
function normalizeRolloutEditInput(input) {
|
|
23427
23541
|
if (typeof input === "string") {
|
|
23428
23542
|
const textFields2 = asEditTextFields(input);
|
|
23429
|
-
const
|
|
23543
|
+
const path40 = extractPatchPrimaryFilePath(input);
|
|
23430
23544
|
return {
|
|
23431
|
-
...
|
|
23545
|
+
...path40 ? { path: path40 } : {},
|
|
23432
23546
|
...textFields2.unifiedDiff ? { patch: textFields2.unifiedDiff } : {},
|
|
23433
23547
|
...textFields2.newString ? { content: textFields2.newString } : {}
|
|
23434
23548
|
};
|
|
@@ -23576,12 +23690,12 @@ function parseFileChangeDiff(entry) {
|
|
|
23576
23690
|
]);
|
|
23577
23691
|
}
|
|
23578
23692
|
function toFileChangeEntry(entry, options, fallbackPath) {
|
|
23579
|
-
const
|
|
23580
|
-
if (!
|
|
23693
|
+
const path40 = parseFileChangePath(entry, options, fallbackPath);
|
|
23694
|
+
if (!path40) {
|
|
23581
23695
|
return null;
|
|
23582
23696
|
}
|
|
23583
23697
|
return {
|
|
23584
|
-
path:
|
|
23698
|
+
path: path40,
|
|
23585
23699
|
kind: parseFileChangeKind(entry),
|
|
23586
23700
|
diff: parseFileChangeDiff(entry)
|
|
23587
23701
|
};
|
|
@@ -23603,12 +23717,12 @@ function parseFileChangeEntries(changes, options) {
|
|
|
23603
23717
|
if (singleEntry) {
|
|
23604
23718
|
return [singleEntry];
|
|
23605
23719
|
}
|
|
23606
|
-
return Object.entries(changes).map(([
|
|
23720
|
+
return Object.entries(changes).map(([path40, value]) => {
|
|
23607
23721
|
if (isRecord2(value)) {
|
|
23608
|
-
return toFileChangeEntry(value, options,
|
|
23722
|
+
return toFileChangeEntry(value, options, path40);
|
|
23609
23723
|
}
|
|
23610
23724
|
if (typeof value === "string") {
|
|
23611
|
-
const normalizedPath = normalizeCodexFilePath(
|
|
23725
|
+
const normalizedPath = normalizeCodexFilePath(path40.trim(), options?.cwd);
|
|
23612
23726
|
if (!normalizedPath) {
|
|
23613
23727
|
return null;
|
|
23614
23728
|
}
|
|
@@ -24353,16 +24467,16 @@ function isObjectSchemaNode(schema) {
|
|
|
24353
24467
|
const type = schema.type;
|
|
24354
24468
|
return isSchemaRecord(schema.properties) || type === "object" || Array.isArray(type) && type.includes("object");
|
|
24355
24469
|
}
|
|
24356
|
-
function normalizeCodexOutputSchemaNode(schema,
|
|
24470
|
+
function normalizeCodexOutputSchemaNode(schema, path40) {
|
|
24357
24471
|
if (Array.isArray(schema)) {
|
|
24358
|
-
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${
|
|
24472
|
+
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${path40}[${index}]`));
|
|
24359
24473
|
}
|
|
24360
24474
|
if (!isSchemaRecord(schema)) {
|
|
24361
24475
|
return schema;
|
|
24362
24476
|
}
|
|
24363
24477
|
const normalized = {};
|
|
24364
24478
|
for (const [key, value] of Object.entries(schema)) {
|
|
24365
|
-
normalized[key] = normalizeCodexOutputSchemaNode(value, `${
|
|
24479
|
+
normalized[key] = normalizeCodexOutputSchemaNode(value, `${path40}.${key}`);
|
|
24366
24480
|
}
|
|
24367
24481
|
if (!isObjectSchemaNode(normalized)) {
|
|
24368
24482
|
return normalized;
|
|
@@ -24371,7 +24485,7 @@ function normalizeCodexOutputSchemaNode(schema, path39) {
|
|
|
24371
24485
|
normalized.additionalProperties = false;
|
|
24372
24486
|
} else if (normalized.additionalProperties !== false) {
|
|
24373
24487
|
throw new Error(
|
|
24374
|
-
`Codex structured outputs require ${
|
|
24488
|
+
`Codex structured outputs require ${path40} to set additionalProperties to false for object schemas.`
|
|
24375
24489
|
);
|
|
24376
24490
|
}
|
|
24377
24491
|
const properties = isSchemaRecord(normalized.properties) ? normalized.properties : null;
|
|
@@ -25135,8 +25249,8 @@ function parseCodexPatchChanges(changes) {
|
|
|
25135
25249
|
}
|
|
25136
25250
|
];
|
|
25137
25251
|
}
|
|
25138
|
-
return Object.entries(recordChanges).map(([
|
|
25139
|
-
const normalizedPath =
|
|
25252
|
+
return Object.entries(recordChanges).map(([path40, value]) => {
|
|
25253
|
+
const normalizedPath = path40.trim();
|
|
25140
25254
|
if (!normalizedPath) {
|
|
25141
25255
|
return null;
|
|
25142
25256
|
}
|
|
@@ -33012,6 +33126,428 @@ function forkSession(input) {
|
|
|
33012
33126
|
};
|
|
33013
33127
|
}
|
|
33014
33128
|
|
|
33129
|
+
// ../server/src/server/agent/providers/claude-draft-commands.ts
|
|
33130
|
+
import fs9 from "node:fs/promises";
|
|
33131
|
+
import os7 from "node:os";
|
|
33132
|
+
import path15 from "node:path";
|
|
33133
|
+
|
|
33134
|
+
// ../server/src/server/skills/scanner.ts
|
|
33135
|
+
import fs8 from "node:fs/promises";
|
|
33136
|
+
import os6 from "node:os";
|
|
33137
|
+
import path14 from "node:path";
|
|
33138
|
+
var NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
33139
|
+
function homeDir() {
|
|
33140
|
+
return process.env.HOME || os6.homedir();
|
|
33141
|
+
}
|
|
33142
|
+
function codexHomeDir() {
|
|
33143
|
+
return process.env.CODEX_HOME || path14.join(homeDir(), ".codex");
|
|
33144
|
+
}
|
|
33145
|
+
function resolveScopeDir(provider, scope, workspaceRoot) {
|
|
33146
|
+
if (scope === "codex-prompts") {
|
|
33147
|
+
if (provider !== "codex") {
|
|
33148
|
+
throw new Error(`Scope "codex-prompts" is only valid for provider "codex"`);
|
|
33149
|
+
}
|
|
33150
|
+
return path14.join(codexHomeDir(), "prompts");
|
|
33151
|
+
}
|
|
33152
|
+
if (scope === "project") {
|
|
33153
|
+
if (!workspaceRoot) {
|
|
33154
|
+
throw new Error(`workspaceRoot is required for scope "project"`);
|
|
33155
|
+
}
|
|
33156
|
+
const dotDir = provider === "claude" ? ".claude" : ".codex";
|
|
33157
|
+
return path14.join(workspaceRoot, dotDir, "skills");
|
|
33158
|
+
}
|
|
33159
|
+
if (provider === "claude") {
|
|
33160
|
+
return path14.join(homeDir(), ".claude", "skills");
|
|
33161
|
+
}
|
|
33162
|
+
return path14.join(codexHomeDir(), "skills");
|
|
33163
|
+
}
|
|
33164
|
+
function allowedRoots(workspaceRoot) {
|
|
33165
|
+
const roots = [
|
|
33166
|
+
path14.join(homeDir(), ".claude", "skills"),
|
|
33167
|
+
path14.join(codexHomeDir(), "skills"),
|
|
33168
|
+
path14.join(codexHomeDir(), "prompts")
|
|
33169
|
+
];
|
|
33170
|
+
if (workspaceRoot) {
|
|
33171
|
+
roots.push(path14.join(workspaceRoot, ".claude", "skills"));
|
|
33172
|
+
roots.push(path14.join(workspaceRoot, ".codex", "skills"));
|
|
33173
|
+
}
|
|
33174
|
+
return roots.map((r) => path14.resolve(r));
|
|
33175
|
+
}
|
|
33176
|
+
function isInsideAllowedRoot(absPath, workspaceRoot) {
|
|
33177
|
+
const resolved = path14.resolve(absPath);
|
|
33178
|
+
for (const root of allowedRoots(workspaceRoot)) {
|
|
33179
|
+
const rel = path14.relative(root, resolved);
|
|
33180
|
+
if (rel === "" || !rel.startsWith("..") && !path14.isAbsolute(rel)) {
|
|
33181
|
+
return true;
|
|
33182
|
+
}
|
|
33183
|
+
}
|
|
33184
|
+
return false;
|
|
33185
|
+
}
|
|
33186
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
33187
|
+
function parseSkillFile(text) {
|
|
33188
|
+
const match = FRONTMATTER_RE.exec(text);
|
|
33189
|
+
if (!match) {
|
|
33190
|
+
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
33191
|
+
}
|
|
33192
|
+
const yamlBlock = match[1] ?? "";
|
|
33193
|
+
const body = match[2] ?? "";
|
|
33194
|
+
return {
|
|
33195
|
+
rawFrontmatterLines: yamlBlock.split(/\r?\n/),
|
|
33196
|
+
body,
|
|
33197
|
+
hadFrontmatter: true
|
|
33198
|
+
};
|
|
33199
|
+
}
|
|
33200
|
+
function readDescription(text) {
|
|
33201
|
+
const parsed = parseSkillFile(text);
|
|
33202
|
+
if (!parsed.hadFrontmatter) return "";
|
|
33203
|
+
for (const line of parsed.rawFrontmatterLines) {
|
|
33204
|
+
const m = /^description\s*:\s*(.*)$/.exec(line);
|
|
33205
|
+
if (m) {
|
|
33206
|
+
const value = (m[1] ?? "").trim();
|
|
33207
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
33208
|
+
return value.slice(1, -1);
|
|
33209
|
+
}
|
|
33210
|
+
return value;
|
|
33211
|
+
}
|
|
33212
|
+
}
|
|
33213
|
+
return "";
|
|
33214
|
+
}
|
|
33215
|
+
function findOwnedSpans(lines) {
|
|
33216
|
+
const spans = [];
|
|
33217
|
+
let i = 0;
|
|
33218
|
+
while (i < lines.length) {
|
|
33219
|
+
const line = lines[i] ?? "";
|
|
33220
|
+
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
33221
|
+
if (!keyMatch) {
|
|
33222
|
+
i++;
|
|
33223
|
+
continue;
|
|
33224
|
+
}
|
|
33225
|
+
const key = keyMatch[1];
|
|
33226
|
+
const value = keyMatch[2];
|
|
33227
|
+
if (key !== "description" && key !== "argument-hint" && key !== "allowed-tools") {
|
|
33228
|
+
i++;
|
|
33229
|
+
continue;
|
|
33230
|
+
}
|
|
33231
|
+
let end = i + 1;
|
|
33232
|
+
if (value === "") {
|
|
33233
|
+
while (end < lines.length) {
|
|
33234
|
+
const next = lines[end] ?? "";
|
|
33235
|
+
if (/^\s+-\s+/.test(next) || /^\s*$/.test(next)) {
|
|
33236
|
+
end++;
|
|
33237
|
+
} else {
|
|
33238
|
+
break;
|
|
33239
|
+
}
|
|
33240
|
+
}
|
|
33241
|
+
}
|
|
33242
|
+
spans.push({ key, startLine: i, endLine: end });
|
|
33243
|
+
i = end;
|
|
33244
|
+
}
|
|
33245
|
+
return spans;
|
|
33246
|
+
}
|
|
33247
|
+
function emitFrontmatterValue(key, value) {
|
|
33248
|
+
if (key === "allowed-tools") {
|
|
33249
|
+
if (!Array.isArray(value) || value.length === 0) return [];
|
|
33250
|
+
return [`allowed-tools:`, ...value.map((v) => ` - ${v}`)];
|
|
33251
|
+
}
|
|
33252
|
+
const text = typeof value === "string" ? value : "";
|
|
33253
|
+
if (text.length === 0) return [];
|
|
33254
|
+
const needsQuote = /[:#\n]/.test(text) || text.startsWith(" ") || text.endsWith(" ");
|
|
33255
|
+
const formatted = needsQuote ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
33256
|
+
return [`${key}: ${formatted}`];
|
|
33257
|
+
}
|
|
33258
|
+
function rewriteFrontmatter(originalLines, next) {
|
|
33259
|
+
const spans = findOwnedSpans(originalLines);
|
|
33260
|
+
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
33261
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
33262
|
+
if (next.description !== void 0) {
|
|
33263
|
+
replacements.set("description", emitFrontmatterValue("description", next.description));
|
|
33264
|
+
}
|
|
33265
|
+
if (next.argumentHint !== void 0) {
|
|
33266
|
+
replacements.set("argument-hint", emitFrontmatterValue("argument-hint", next.argumentHint));
|
|
33267
|
+
}
|
|
33268
|
+
if (next.allowedTools !== void 0) {
|
|
33269
|
+
replacements.set("allowed-tools", emitFrontmatterValue("allowed-tools", next.allowedTools));
|
|
33270
|
+
}
|
|
33271
|
+
const out = [];
|
|
33272
|
+
let i = 0;
|
|
33273
|
+
while (i < originalLines.length) {
|
|
33274
|
+
const span = spans.find((s) => s.startLine === i);
|
|
33275
|
+
if (span) {
|
|
33276
|
+
if (replacements.has(span.key)) {
|
|
33277
|
+
out.push(...replacements.get(span.key) ?? []);
|
|
33278
|
+
} else {
|
|
33279
|
+
for (let j = span.startLine; j < span.endLine; j++) {
|
|
33280
|
+
out.push(originalLines[j] ?? "");
|
|
33281
|
+
}
|
|
33282
|
+
}
|
|
33283
|
+
i = span.endLine;
|
|
33284
|
+
continue;
|
|
33285
|
+
}
|
|
33286
|
+
out.push(originalLines[i] ?? "");
|
|
33287
|
+
i++;
|
|
33288
|
+
}
|
|
33289
|
+
for (const key of ["description", "argument-hint", "allowed-tools"]) {
|
|
33290
|
+
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
33291
|
+
out.push(...replacements.get(key) ?? []);
|
|
33292
|
+
}
|
|
33293
|
+
}
|
|
33294
|
+
return out;
|
|
33295
|
+
}
|
|
33296
|
+
async function listSkills(args) {
|
|
33297
|
+
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
33298
|
+
let entries;
|
|
33299
|
+
try {
|
|
33300
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
33301
|
+
} catch {
|
|
33302
|
+
return [];
|
|
33303
|
+
}
|
|
33304
|
+
const results = [];
|
|
33305
|
+
if (args.scope === "codex-prompts") {
|
|
33306
|
+
for (const entry of entries) {
|
|
33307
|
+
if (!entry.isFile()) continue;
|
|
33308
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
33309
|
+
const name = entry.name.slice(0, -".md".length);
|
|
33310
|
+
if (!name) continue;
|
|
33311
|
+
const fullPath = path14.join(dir, entry.name);
|
|
33312
|
+
const stat10 = await safeStat(fullPath);
|
|
33313
|
+
if (!stat10) continue;
|
|
33314
|
+
const description = await readDescriptionSafely(fullPath);
|
|
33315
|
+
results.push({
|
|
33316
|
+
id: `${args.provider}:${args.scope}:${name}`,
|
|
33317
|
+
provider: args.provider,
|
|
33318
|
+
scope: args.scope,
|
|
33319
|
+
name,
|
|
33320
|
+
path: fullPath,
|
|
33321
|
+
description,
|
|
33322
|
+
modifiedAt: stat10.mtime.toISOString(),
|
|
33323
|
+
size: stat10.size
|
|
33324
|
+
});
|
|
33325
|
+
}
|
|
33326
|
+
} else {
|
|
33327
|
+
for (const entry of entries) {
|
|
33328
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
33329
|
+
const skillDir = path14.join(dir, entry.name);
|
|
33330
|
+
const skillPath = path14.join(skillDir, "SKILL.md");
|
|
33331
|
+
const stat10 = await safeStat(skillPath);
|
|
33332
|
+
if (!stat10) continue;
|
|
33333
|
+
const description = await readDescriptionSafely(skillPath);
|
|
33334
|
+
results.push({
|
|
33335
|
+
id: `${args.provider}:${args.scope}:${entry.name}`,
|
|
33336
|
+
provider: args.provider,
|
|
33337
|
+
scope: args.scope,
|
|
33338
|
+
name: entry.name,
|
|
33339
|
+
path: skillPath,
|
|
33340
|
+
description,
|
|
33341
|
+
modifiedAt: stat10.mtime.toISOString(),
|
|
33342
|
+
size: stat10.size
|
|
33343
|
+
});
|
|
33344
|
+
}
|
|
33345
|
+
}
|
|
33346
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
33347
|
+
return results;
|
|
33348
|
+
}
|
|
33349
|
+
async function safeStat(filePath) {
|
|
33350
|
+
try {
|
|
33351
|
+
const s = await fs8.stat(filePath);
|
|
33352
|
+
return { mtime: s.mtime, size: s.size };
|
|
33353
|
+
} catch {
|
|
33354
|
+
return null;
|
|
33355
|
+
}
|
|
33356
|
+
}
|
|
33357
|
+
async function readDescriptionSafely(filePath) {
|
|
33358
|
+
try {
|
|
33359
|
+
const text = await fs8.readFile(filePath, "utf8");
|
|
33360
|
+
return readDescription(text);
|
|
33361
|
+
} catch {
|
|
33362
|
+
return "";
|
|
33363
|
+
}
|
|
33364
|
+
}
|
|
33365
|
+
async function createSkill(args) {
|
|
33366
|
+
if (!NAME_REGEX.test(args.name)) {
|
|
33367
|
+
throw new Error(
|
|
33368
|
+
`Invalid skill name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
33369
|
+
);
|
|
33370
|
+
}
|
|
33371
|
+
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
33372
|
+
throw new Error(`Skill name must not contain path separators or "..".`);
|
|
33373
|
+
}
|
|
33374
|
+
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
33375
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
33376
|
+
if (args.scope === "codex-prompts") {
|
|
33377
|
+
const filePath2 = path14.join(dir, `${args.name}.md`);
|
|
33378
|
+
try {
|
|
33379
|
+
await fs8.access(filePath2);
|
|
33380
|
+
throw new Error(`A prompt named "${args.name}" already exists at ${filePath2}`);
|
|
33381
|
+
} catch (err) {
|
|
33382
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
33383
|
+
throw err;
|
|
33384
|
+
}
|
|
33385
|
+
if (err instanceof Error && err.message.includes("already exists")) {
|
|
33386
|
+
throw err;
|
|
33387
|
+
}
|
|
33388
|
+
}
|
|
33389
|
+
const initial2 = buildStarterPrompt(args.name);
|
|
33390
|
+
await fs8.writeFile(filePath2, initial2, "utf8");
|
|
33391
|
+
return { path: filePath2 };
|
|
33392
|
+
}
|
|
33393
|
+
const skillDir = path14.join(dir, args.name);
|
|
33394
|
+
let dirExists = false;
|
|
33395
|
+
try {
|
|
33396
|
+
const stat10 = await fs8.stat(skillDir);
|
|
33397
|
+
dirExists = stat10.isDirectory();
|
|
33398
|
+
} catch {
|
|
33399
|
+
dirExists = false;
|
|
33400
|
+
}
|
|
33401
|
+
if (dirExists) {
|
|
33402
|
+
throw new Error(`A skill named "${args.name}" already exists at ${skillDir}`);
|
|
33403
|
+
}
|
|
33404
|
+
await fs8.mkdir(skillDir, { recursive: true });
|
|
33405
|
+
const filePath = path14.join(skillDir, "SKILL.md");
|
|
33406
|
+
const initial = buildStarterSkill(args.name);
|
|
33407
|
+
await fs8.writeFile(filePath, initial, "utf8");
|
|
33408
|
+
return { path: filePath };
|
|
33409
|
+
}
|
|
33410
|
+
function buildStarterSkill(name) {
|
|
33411
|
+
return `---
|
|
33412
|
+
name: ${name}
|
|
33413
|
+
description: ""
|
|
33414
|
+
---
|
|
33415
|
+
|
|
33416
|
+
# ${name}
|
|
33417
|
+
|
|
33418
|
+
Describe when this skill should be used and what it does. The body of this
|
|
33419
|
+
file is loaded into the agent's context when the skill is invoked.
|
|
33420
|
+
`;
|
|
33421
|
+
}
|
|
33422
|
+
function buildStarterPrompt(name) {
|
|
33423
|
+
return `---
|
|
33424
|
+
description: ""
|
|
33425
|
+
argument-hint: ""
|
|
33426
|
+
---
|
|
33427
|
+
|
|
33428
|
+
# ${name}
|
|
33429
|
+
|
|
33430
|
+
Body of the prompt. Use \`$1\`, \`$2\`, ... or \`$ARGUMENTS\` for parameter expansion.
|
|
33431
|
+
`;
|
|
33432
|
+
}
|
|
33433
|
+
async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
33434
|
+
if (!path14.isAbsolute(args.path)) {
|
|
33435
|
+
throw new Error(`writeSkillFrontmatter expects an absolute path; got "${args.path}"`);
|
|
33436
|
+
}
|
|
33437
|
+
if (!isInsideAllowedRoot(args.path, workspaceRoot)) {
|
|
33438
|
+
throw new Error(`Path "${args.path}" is not inside an allowlisted skill root`);
|
|
33439
|
+
}
|
|
33440
|
+
let original;
|
|
33441
|
+
try {
|
|
33442
|
+
original = await fs8.readFile(args.path, "utf8");
|
|
33443
|
+
} catch (err) {
|
|
33444
|
+
throw new Error(
|
|
33445
|
+
`Failed to read skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
33446
|
+
);
|
|
33447
|
+
}
|
|
33448
|
+
const parsed = parseSkillFile(original);
|
|
33449
|
+
const newLines = rewriteFrontmatter(parsed.rawFrontmatterLines, args.frontmatter);
|
|
33450
|
+
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
33451
|
+
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
33452
|
+
${parsed.body}` : `${nextFrontmatter}
|
|
33453
|
+
|
|
33454
|
+
${original}`;
|
|
33455
|
+
await fs8.writeFile(args.path, nextContent, "utf8");
|
|
33456
|
+
}
|
|
33457
|
+
|
|
33458
|
+
// ../server/src/server/agent/providers/claude-draft-commands.ts
|
|
33459
|
+
async function enumerateClaudeDraftCommands(cwd) {
|
|
33460
|
+
const byName = /* @__PURE__ */ new Map();
|
|
33461
|
+
const [global, project, plugin] = await Promise.all([
|
|
33462
|
+
listSkills({ provider: "claude", scope: "global" }).catch(() => []),
|
|
33463
|
+
listSkills({ provider: "claude", scope: "project", workspaceRoot: cwd }).catch(() => []),
|
|
33464
|
+
listClaudePluginSkills().catch(() => [])
|
|
33465
|
+
]);
|
|
33466
|
+
for (const entry of [...project, ...global, ...plugin]) {
|
|
33467
|
+
if (!byName.has(entry.name)) {
|
|
33468
|
+
byName.set(entry.name, {
|
|
33469
|
+
name: entry.name,
|
|
33470
|
+
description: entry.description,
|
|
33471
|
+
argumentHint: ""
|
|
33472
|
+
});
|
|
33473
|
+
}
|
|
33474
|
+
}
|
|
33475
|
+
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
33476
|
+
}
|
|
33477
|
+
async function listClaudePluginSkills() {
|
|
33478
|
+
const pluginsRoot = path15.join(os7.homedir(), ".claude", "plugins");
|
|
33479
|
+
const installedPath = path15.join(pluginsRoot, "installed_plugins.json");
|
|
33480
|
+
let raw;
|
|
33481
|
+
try {
|
|
33482
|
+
raw = await fs9.readFile(installedPath, "utf8");
|
|
33483
|
+
} catch {
|
|
33484
|
+
return [];
|
|
33485
|
+
}
|
|
33486
|
+
let parsed;
|
|
33487
|
+
try {
|
|
33488
|
+
parsed = JSON.parse(raw);
|
|
33489
|
+
} catch {
|
|
33490
|
+
return [];
|
|
33491
|
+
}
|
|
33492
|
+
const out = [];
|
|
33493
|
+
const plugins = parsed.plugins ?? {};
|
|
33494
|
+
for (const [key, installs] of Object.entries(plugins)) {
|
|
33495
|
+
if (!Array.isArray(installs) || installs.length === 0) continue;
|
|
33496
|
+
const prefix = key.split("@")[0];
|
|
33497
|
+
if (!prefix) continue;
|
|
33498
|
+
const install = installs[installs.length - 1];
|
|
33499
|
+
const installPath = install?.installPath;
|
|
33500
|
+
if (!installPath) continue;
|
|
33501
|
+
const skills = await readSkillFolder(path15.join(installPath, "skills"));
|
|
33502
|
+
for (const skill of skills) {
|
|
33503
|
+
out.push({
|
|
33504
|
+
name: `${prefix}:${skill.name}`,
|
|
33505
|
+
description: skill.description
|
|
33506
|
+
});
|
|
33507
|
+
}
|
|
33508
|
+
}
|
|
33509
|
+
return out;
|
|
33510
|
+
}
|
|
33511
|
+
async function readSkillFolder(dir) {
|
|
33512
|
+
let entries;
|
|
33513
|
+
try {
|
|
33514
|
+
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
33515
|
+
} catch {
|
|
33516
|
+
return [];
|
|
33517
|
+
}
|
|
33518
|
+
const out = [];
|
|
33519
|
+
for (const entry of entries) {
|
|
33520
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
33521
|
+
const skillPath = path15.join(dir, entry.name, "SKILL.md");
|
|
33522
|
+
const description = await readDescriptionField(skillPath);
|
|
33523
|
+
if (description === null) continue;
|
|
33524
|
+
out.push({ name: entry.name, description });
|
|
33525
|
+
}
|
|
33526
|
+
return out;
|
|
33527
|
+
}
|
|
33528
|
+
var FRONTMATTER_RE2 = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
33529
|
+
async function readDescriptionField(filePath) {
|
|
33530
|
+
let text;
|
|
33531
|
+
try {
|
|
33532
|
+
text = await fs9.readFile(filePath, "utf8");
|
|
33533
|
+
} catch {
|
|
33534
|
+
return null;
|
|
33535
|
+
}
|
|
33536
|
+
const match = FRONTMATTER_RE2.exec(text);
|
|
33537
|
+
if (!match) return "";
|
|
33538
|
+
const yaml = match[1] ?? "";
|
|
33539
|
+
for (const line of yaml.split(/\r?\n/)) {
|
|
33540
|
+
const m = /^description\s*:\s*(.*)$/.exec(line);
|
|
33541
|
+
if (!m) continue;
|
|
33542
|
+
let value = (m[1] ?? "").trim();
|
|
33543
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
33544
|
+
value = value.slice(1, -1);
|
|
33545
|
+
}
|
|
33546
|
+
return value;
|
|
33547
|
+
}
|
|
33548
|
+
return "";
|
|
33549
|
+
}
|
|
33550
|
+
|
|
33015
33551
|
// ../server/src/server/agent/handoff-mcp.ts
|
|
33016
33552
|
import { createSdkMcpServer, tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
33017
33553
|
import { z as z33 } from "zod";
|
|
@@ -33066,8 +33602,8 @@ function buildZodValidator(schema, schemaName) {
|
|
|
33066
33602
|
return { ok: true, value: result.data };
|
|
33067
33603
|
}
|
|
33068
33604
|
const errors = result.error.issues.map((issue) => {
|
|
33069
|
-
const
|
|
33070
|
-
return `${
|
|
33605
|
+
const path40 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
33606
|
+
return `${path40}: ${issue.message}`;
|
|
33071
33607
|
});
|
|
33072
33608
|
return { ok: false, errors };
|
|
33073
33609
|
}
|
|
@@ -33085,9 +33621,9 @@ function buildJsonSchemaValidator(schema) {
|
|
|
33085
33621
|
return { ok: true, value };
|
|
33086
33622
|
}
|
|
33087
33623
|
const errors = (validate.errors ?? []).map((error) => {
|
|
33088
|
-
const
|
|
33624
|
+
const path40 = error.instancePath && error.instancePath.length > 0 ? error.instancePath : "(root)";
|
|
33089
33625
|
const message = error.message ?? "is invalid";
|
|
33090
|
-
return `${
|
|
33626
|
+
return `${path40}: ${message}`;
|
|
33091
33627
|
});
|
|
33092
33628
|
return { ok: false, errors };
|
|
33093
33629
|
}
|
|
@@ -33494,8 +34030,8 @@ function scheduleAgentMetadataGeneration(options) {
|
|
|
33494
34030
|
}
|
|
33495
34031
|
|
|
33496
34032
|
// ../server/src/server/agent/write-plan-tool.ts
|
|
33497
|
-
import
|
|
33498
|
-
import
|
|
34033
|
+
import fs10 from "node:fs";
|
|
34034
|
+
import path16 from "node:path";
|
|
33499
34035
|
import { tool } from "@anthropic-ai/claude-agent-sdk";
|
|
33500
34036
|
import { z as z32 } from "zod";
|
|
33501
34037
|
var TODO_STATUSES = ["pending", "in_progress", "needs_help", "completed"];
|
|
@@ -33545,9 +34081,9 @@ function buildWritePlanTool(callerAgentId, options) {
|
|
|
33545
34081
|
]
|
|
33546
34082
|
};
|
|
33547
34083
|
}
|
|
33548
|
-
const plansDir =
|
|
34084
|
+
const plansDir = path16.join(agent.cwd, ".plans");
|
|
33549
34085
|
try {
|
|
33550
|
-
|
|
34086
|
+
fs10.mkdirSync(plansDir, { recursive: true });
|
|
33551
34087
|
} catch (err) {
|
|
33552
34088
|
log.error({ err, plansDir }, "write_plan: mkdir failed");
|
|
33553
34089
|
return {
|
|
@@ -33561,9 +34097,9 @@ function buildWritePlanTool(callerAgentId, options) {
|
|
|
33561
34097
|
};
|
|
33562
34098
|
}
|
|
33563
34099
|
const targetPath = resolvePlanFilename({
|
|
33564
|
-
originalPath:
|
|
34100
|
+
originalPath: path16.join(plansDir, `${slug}.md`),
|
|
33565
34101
|
newSlug: slug,
|
|
33566
|
-
existsSync:
|
|
34102
|
+
existsSync: fs10.existsSync
|
|
33567
34103
|
});
|
|
33568
34104
|
const frontmatter = serializeFrontmatter({
|
|
33569
34105
|
name: args.name,
|
|
@@ -33575,7 +34111,7 @@ function buildWritePlanTool(callerAgentId, options) {
|
|
|
33575
34111
|
${args.body}`;
|
|
33576
34112
|
const fileContent = `${frontmatter}${body}${body.endsWith("\n") ? "" : "\n"}`;
|
|
33577
34113
|
try {
|
|
33578
|
-
|
|
34114
|
+
fs10.writeFileSync(targetPath, fileContent, "utf8");
|
|
33579
34115
|
} catch (err) {
|
|
33580
34116
|
log.error({ err, targetPath }, "write_plan: writeFile failed");
|
|
33581
34117
|
return {
|
|
@@ -33722,18 +34258,7 @@ function buildHandoffMcpServer(callerAgentId, options) {
|
|
|
33722
34258
|
);
|
|
33723
34259
|
const reportTool = tool2(
|
|
33724
34260
|
"report",
|
|
33725
|
-
|
|
33726
|
-
"Send a curated message from this handoff child session back to its parent.",
|
|
33727
|
-
"Use this when the user types `>report` \u2014 they want to deliver a specific update",
|
|
33728
|
-
"to the parent right now, in their own words.",
|
|
33729
|
-
"",
|
|
33730
|
-
"Independent of the automatic on-finish notification: the parent still receives",
|
|
33731
|
-
"the auto-return when this child eventually finishes. You can call this tool any",
|
|
33732
|
-
"number of times over the lifetime of the handoff \u2014 each call is a separate",
|
|
33733
|
-
"update.",
|
|
33734
|
-
"",
|
|
33735
|
-
"Only works in sessions that were spawned via `handoff`. Errors otherwise."
|
|
33736
|
-
].join("\n"),
|
|
34261
|
+
'**ALWAYS call this tool as your final action before finishing your task, to deliver your findings to the parent agent.** Without this call, the parent only receives a content-free "finished" notification and is blind to whatever you did or learned. The first sentence of your report should be a one-line verdict; the rest can fill in detail. Also call this tool any time the user explicitly types `>report` mid-task to send an interim update \u2014 the auto-return on session end is independent from manual reports, and you can call it any number of times over the session\'s lifetime. Only works in sessions that were spawned via `handoff`; errors otherwise.',
|
|
33737
34262
|
{
|
|
33738
34263
|
message: z33.string().min(1).describe(
|
|
33739
34264
|
"The curated report to deliver to the parent session. Self-contained: the parent does not see this conversation's timeline."
|
|
@@ -34317,6 +34842,7 @@ var AgentManager = class {
|
|
|
34317
34842
|
this.mcpBaseUrl = options?.mcpBaseUrl ?? null;
|
|
34318
34843
|
this.appostleHome = options?.appostleHome ?? null;
|
|
34319
34844
|
this.chromeEnabled = options?.chromeEnabled ?? true;
|
|
34845
|
+
this.playwrightEnabled = options?.playwrightEnabled ?? true;
|
|
34320
34846
|
this.ownerUserId = options?.ownerUserId ?? null;
|
|
34321
34847
|
this.logger = options.logger.child({ module: "agent", component: "agent-manager" });
|
|
34322
34848
|
this.agentStreamCoalescer = new AgentStreamCoalescer({
|
|
@@ -34540,6 +35066,9 @@ var AgentManager = class {
|
|
|
34540
35066
|
}
|
|
34541
35067
|
async listDraftCommands(config) {
|
|
34542
35068
|
const normalizedConfig = await this.normalizeConfig(config);
|
|
35069
|
+
if (normalizedConfig.provider === "claude") {
|
|
35070
|
+
return enumerateClaudeDraftCommands(normalizedConfig.cwd);
|
|
35071
|
+
}
|
|
34543
35072
|
const client = this.requireClient(normalizedConfig.provider);
|
|
34544
35073
|
const available = await client.isAvailable();
|
|
34545
35074
|
if (!available) {
|
|
@@ -36591,7 +37120,7 @@ var AgentManager = class {
|
|
|
36591
37120
|
"using shared user claude profile"
|
|
36592
37121
|
);
|
|
36593
37122
|
}
|
|
36594
|
-
return { env, chromeEnabled: this.chromeEnabled };
|
|
37123
|
+
return { env, chromeEnabled: this.chromeEnabled, playwrightEnabled: this.playwrightEnabled };
|
|
36595
37124
|
}
|
|
36596
37125
|
requireClient(provider) {
|
|
36597
37126
|
const client = this.clients.get(provider);
|
|
@@ -37001,8 +37530,8 @@ async function buildProjectPlacementForCwd(input) {
|
|
|
37001
37530
|
|
|
37002
37531
|
// ../server/src/server/workspace-registry.ts
|
|
37003
37532
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
37004
|
-
import { promises as
|
|
37005
|
-
import
|
|
37533
|
+
import { promises as fs11 } from "node:fs";
|
|
37534
|
+
import path17 from "node:path";
|
|
37006
37535
|
import { z as z35 } from "zod";
|
|
37007
37536
|
var PersistedProjectRecordSchema = z35.object({
|
|
37008
37537
|
projectId: z35.string(),
|
|
@@ -37041,7 +37570,7 @@ var FileBackedRegistry = class {
|
|
|
37041
37570
|
}
|
|
37042
37571
|
async existsOnDisk() {
|
|
37043
37572
|
try {
|
|
37044
|
-
await
|
|
37573
|
+
await fs11.access(this.filePath);
|
|
37045
37574
|
return true;
|
|
37046
37575
|
} catch {
|
|
37047
37576
|
return false;
|
|
@@ -37088,7 +37617,7 @@ var FileBackedRegistry = class {
|
|
|
37088
37617
|
}
|
|
37089
37618
|
this.cache.clear();
|
|
37090
37619
|
try {
|
|
37091
|
-
const raw = await
|
|
37620
|
+
const raw = await fs11.readFile(this.filePath, "utf8");
|
|
37092
37621
|
const parsed = z35.array(this.schema).parse(JSON.parse(raw));
|
|
37093
37622
|
for (const record of parsed) {
|
|
37094
37623
|
this.cache.set(this.getId(record), record);
|
|
@@ -37103,10 +37632,10 @@ var FileBackedRegistry = class {
|
|
|
37103
37632
|
}
|
|
37104
37633
|
async persist() {
|
|
37105
37634
|
const records = Array.from(this.cache.values());
|
|
37106
|
-
await
|
|
37635
|
+
await fs11.mkdir(path17.dirname(this.filePath), { recursive: true });
|
|
37107
37636
|
const tempPath = `${this.filePath}.${process.pid}.${Date.now()}.${randomUUID9()}.tmp`;
|
|
37108
|
-
await
|
|
37109
|
-
await
|
|
37637
|
+
await fs11.writeFile(tempPath, JSON.stringify(records, null, 2), "utf8");
|
|
37638
|
+
await fs11.rename(tempPath, this.filePath);
|
|
37110
37639
|
}
|
|
37111
37640
|
async enqueuePersist() {
|
|
37112
37641
|
const nextPersist = this.persistQueue.then(() => this.persist());
|
|
@@ -37213,8 +37742,8 @@ function isVoicePermissionAllowed(request) {
|
|
|
37213
37742
|
}
|
|
37214
37743
|
|
|
37215
37744
|
// ../server/src/server/file-explorer/service.ts
|
|
37216
|
-
import { promises as
|
|
37217
|
-
import
|
|
37745
|
+
import { promises as fs12 } from "fs";
|
|
37746
|
+
import path18 from "path";
|
|
37218
37747
|
|
|
37219
37748
|
// ../server/src/server/path-utils.ts
|
|
37220
37749
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -37261,14 +37790,14 @@ async function listDirectoryEntries({
|
|
|
37261
37790
|
relativePath = "."
|
|
37262
37791
|
}) {
|
|
37263
37792
|
const directoryPath = await resolveScopedPath({ root, relativePath });
|
|
37264
|
-
const stats = await
|
|
37793
|
+
const stats = await fs12.stat(directoryPath);
|
|
37265
37794
|
if (!stats.isDirectory()) {
|
|
37266
37795
|
throw new Error("Requested path is not a directory");
|
|
37267
37796
|
}
|
|
37268
|
-
const dirents = await
|
|
37797
|
+
const dirents = await fs12.readdir(directoryPath, { withFileTypes: true });
|
|
37269
37798
|
const entriesWithNulls = await Promise.all(
|
|
37270
37799
|
dirents.map(async (dirent) => {
|
|
37271
|
-
const targetPath =
|
|
37800
|
+
const targetPath = path18.join(directoryPath, dirent.name);
|
|
37272
37801
|
const kind = dirent.isDirectory() ? "directory" : "file";
|
|
37273
37802
|
try {
|
|
37274
37803
|
return await buildEntryPayload({
|
|
@@ -37303,18 +37832,18 @@ async function readExplorerFile({
|
|
|
37303
37832
|
relativePath
|
|
37304
37833
|
}) {
|
|
37305
37834
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
37306
|
-
const stats = await
|
|
37835
|
+
const stats = await fs12.stat(filePath);
|
|
37307
37836
|
if (!stats.isFile()) {
|
|
37308
37837
|
throw new Error("Requested path is not a file");
|
|
37309
37838
|
}
|
|
37310
|
-
const ext =
|
|
37839
|
+
const ext = path18.extname(filePath).toLowerCase();
|
|
37311
37840
|
const basePayload = {
|
|
37312
37841
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
37313
37842
|
size: stats.size,
|
|
37314
37843
|
modifiedAt: stats.mtime.toISOString()
|
|
37315
37844
|
};
|
|
37316
37845
|
if (ext in IMAGE_MIME_TYPES) {
|
|
37317
|
-
const buffer2 = await
|
|
37846
|
+
const buffer2 = await fs12.readFile(filePath);
|
|
37318
37847
|
return {
|
|
37319
37848
|
...basePayload,
|
|
37320
37849
|
kind: "image",
|
|
@@ -37323,7 +37852,7 @@ async function readExplorerFile({
|
|
|
37323
37852
|
mimeType: IMAGE_MIME_TYPES[ext]
|
|
37324
37853
|
};
|
|
37325
37854
|
}
|
|
37326
|
-
const buffer = await
|
|
37855
|
+
const buffer = await fs12.readFile(filePath);
|
|
37327
37856
|
if (isLikelyBinary(buffer)) {
|
|
37328
37857
|
return {
|
|
37329
37858
|
...basePayload,
|
|
@@ -37345,34 +37874,34 @@ async function writeTextFile({
|
|
|
37345
37874
|
relativePath,
|
|
37346
37875
|
content
|
|
37347
37876
|
}) {
|
|
37348
|
-
const ext =
|
|
37877
|
+
const ext = path18.extname(relativePath).toLowerCase();
|
|
37349
37878
|
if (ext in IMAGE_MIME_TYPES) {
|
|
37350
37879
|
throw new Error(`Refusing to write '${relativePath}': binary/image file`);
|
|
37351
37880
|
}
|
|
37352
37881
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
37353
37882
|
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
37354
|
-
await
|
|
37355
|
-
await
|
|
37883
|
+
await fs12.writeFile(tempPath, content, "utf8");
|
|
37884
|
+
await fs12.rename(tempPath, filePath);
|
|
37356
37885
|
}
|
|
37357
37886
|
async function deleteFile({ root, relativePath }) {
|
|
37358
|
-
const ext =
|
|
37887
|
+
const ext = path18.extname(relativePath).toLowerCase();
|
|
37359
37888
|
if (ext !== ".md") {
|
|
37360
37889
|
throw new Error(`Refusing to delete '${relativePath}': only .md files allowed`);
|
|
37361
37890
|
}
|
|
37362
37891
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
37363
|
-
const stats = await
|
|
37892
|
+
const stats = await fs12.stat(filePath);
|
|
37364
37893
|
if (!stats.isFile()) {
|
|
37365
37894
|
throw new Error("Requested path is not a file");
|
|
37366
37895
|
}
|
|
37367
|
-
await
|
|
37896
|
+
await fs12.unlink(filePath);
|
|
37368
37897
|
}
|
|
37369
37898
|
async function deleteEntry({ root, relativePath }) {
|
|
37370
37899
|
const entryPath = await resolveScopedPath({ root, relativePath });
|
|
37371
|
-
await
|
|
37900
|
+
await fs12.rm(entryPath, { recursive: true, force: false });
|
|
37372
37901
|
}
|
|
37373
37902
|
async function createDirectory({ root, relativePath }) {
|
|
37374
37903
|
const dirPath = await resolveScopedPath({ root, relativePath });
|
|
37375
|
-
await
|
|
37904
|
+
await fs12.mkdir(dirPath, { recursive: true });
|
|
37376
37905
|
}
|
|
37377
37906
|
async function moveEntry({
|
|
37378
37907
|
root,
|
|
@@ -37381,22 +37910,22 @@ async function moveEntry({
|
|
|
37381
37910
|
}) {
|
|
37382
37911
|
const src = await resolveScopedPath({ root, relativePath: sourcePath });
|
|
37383
37912
|
const dest = await resolveScopedPath({ root, relativePath: destinationPath });
|
|
37384
|
-
await
|
|
37913
|
+
await fs12.rename(src, dest);
|
|
37385
37914
|
}
|
|
37386
37915
|
async function getDownloadableFileInfo({ root, relativePath }) {
|
|
37387
37916
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
37388
|
-
const stats = await
|
|
37917
|
+
const stats = await fs12.stat(filePath);
|
|
37389
37918
|
if (!stats.isFile()) {
|
|
37390
37919
|
throw new Error("Requested path is not a file");
|
|
37391
37920
|
}
|
|
37392
|
-
const ext =
|
|
37921
|
+
const ext = path18.extname(filePath).toLowerCase();
|
|
37393
37922
|
let mimeType = "application/octet-stream";
|
|
37394
37923
|
if (ext in IMAGE_MIME_TYPES) {
|
|
37395
37924
|
mimeType = IMAGE_MIME_TYPES[ext] ?? mimeType;
|
|
37396
37925
|
} else if (ext in FONT_MIME_TYPES) {
|
|
37397
37926
|
mimeType = FONT_MIME_TYPES[ext] ?? mimeType;
|
|
37398
37927
|
} else {
|
|
37399
|
-
const handle = await
|
|
37928
|
+
const handle = await fs12.open(filePath, "r");
|
|
37400
37929
|
const sample = Buffer.alloc(8192);
|
|
37401
37930
|
try {
|
|
37402
37931
|
const { bytesRead } = await handle.read(sample, 0, sample.length, 0);
|
|
@@ -37411,23 +37940,23 @@ async function getDownloadableFileInfo({ root, relativePath }) {
|
|
|
37411
37940
|
return {
|
|
37412
37941
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
37413
37942
|
absolutePath: filePath,
|
|
37414
|
-
fileName:
|
|
37943
|
+
fileName: path18.basename(filePath),
|
|
37415
37944
|
mimeType,
|
|
37416
37945
|
size: stats.size
|
|
37417
37946
|
};
|
|
37418
37947
|
}
|
|
37419
37948
|
async function resolveScopedPath({ root, relativePath = "." }) {
|
|
37420
|
-
const normalizedRoot =
|
|
37949
|
+
const normalizedRoot = path18.resolve(root);
|
|
37421
37950
|
const requestedPath = resolvePathFromBase(normalizedRoot, relativePath);
|
|
37422
|
-
const relative =
|
|
37423
|
-
if (relative !== "" && (relative.startsWith("..") ||
|
|
37951
|
+
const relative = path18.relative(normalizedRoot, requestedPath);
|
|
37952
|
+
if (relative !== "" && (relative.startsWith("..") || path18.isAbsolute(relative))) {
|
|
37424
37953
|
throw new Error("Access outside of workspace is not allowed");
|
|
37425
37954
|
}
|
|
37426
|
-
const realRoot = await
|
|
37955
|
+
const realRoot = await fs12.realpath(normalizedRoot);
|
|
37427
37956
|
try {
|
|
37428
|
-
const realPath = await
|
|
37429
|
-
const realRelative =
|
|
37430
|
-
if (realRelative !== "" && (realRelative.startsWith("..") ||
|
|
37957
|
+
const realPath = await fs12.realpath(requestedPath);
|
|
37958
|
+
const realRelative = path18.relative(realRoot, realPath);
|
|
37959
|
+
if (realRelative !== "" && (realRelative.startsWith("..") || path18.isAbsolute(realRelative))) {
|
|
37431
37960
|
throw new Error("Access outside of workspace is not allowed");
|
|
37432
37961
|
}
|
|
37433
37962
|
return requestedPath;
|
|
@@ -37444,7 +37973,7 @@ async function buildEntryPayload({
|
|
|
37444
37973
|
name,
|
|
37445
37974
|
kind
|
|
37446
37975
|
}) {
|
|
37447
|
-
const stats = await
|
|
37976
|
+
const stats = await fs12.stat(targetPath);
|
|
37448
37977
|
return {
|
|
37449
37978
|
name,
|
|
37450
37979
|
path: normalizeRelativePath({ root, targetPath }),
|
|
@@ -37458,10 +37987,10 @@ function isMissingEntryError(error) {
|
|
|
37458
37987
|
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
37459
37988
|
}
|
|
37460
37989
|
function normalizeRelativePath({ root, targetPath }) {
|
|
37461
|
-
const normalizedRoot =
|
|
37462
|
-
const normalizedTarget =
|
|
37463
|
-
const relative =
|
|
37464
|
-
return relative === "" ? "." : relative.split(
|
|
37990
|
+
const normalizedRoot = path18.resolve(root);
|
|
37991
|
+
const normalizedTarget = path18.resolve(targetPath);
|
|
37992
|
+
const relative = path18.relative(normalizedRoot, normalizedTarget);
|
|
37993
|
+
return relative === "" ? "." : relative.split(path18.sep).join("/");
|
|
37465
37994
|
}
|
|
37466
37995
|
function textMimeTypeForExtension(ext) {
|
|
37467
37996
|
return TEXT_MIME_TYPES[ext] ?? DEFAULT_TEXT_MIME_TYPE;
|
|
@@ -37814,345 +38343,21 @@ async function getProjectIcon(projectDir) {
|
|
|
37814
38343
|
}
|
|
37815
38344
|
|
|
37816
38345
|
// ../server/src/utils/path.ts
|
|
37817
|
-
import
|
|
37818
|
-
function expandTilde(
|
|
37819
|
-
if (
|
|
37820
|
-
const homeDir3 = process.env.HOME ||
|
|
37821
|
-
return
|
|
38346
|
+
import os8 from "os";
|
|
38347
|
+
function expandTilde(path40) {
|
|
38348
|
+
if (path40.startsWith("~/")) {
|
|
38349
|
+
const homeDir3 = process.env.HOME || os8.homedir();
|
|
38350
|
+
return path40.replace("~", homeDir3);
|
|
37822
38351
|
}
|
|
37823
|
-
if (
|
|
37824
|
-
return process.env.HOME ||
|
|
38352
|
+
if (path40 === "~") {
|
|
38353
|
+
return process.env.HOME || os8.homedir();
|
|
37825
38354
|
}
|
|
37826
|
-
return
|
|
37827
|
-
}
|
|
37828
|
-
|
|
37829
|
-
// ../server/src/server/skills/scanner.ts
|
|
37830
|
-
import fs11 from "node:fs/promises";
|
|
37831
|
-
import os7 from "node:os";
|
|
37832
|
-
import path17 from "node:path";
|
|
37833
|
-
var NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
37834
|
-
function homeDir() {
|
|
37835
|
-
return process.env.HOME || os7.homedir();
|
|
37836
|
-
}
|
|
37837
|
-
function codexHomeDir() {
|
|
37838
|
-
return process.env.CODEX_HOME || path17.join(homeDir(), ".codex");
|
|
37839
|
-
}
|
|
37840
|
-
function resolveScopeDir(provider, scope, workspaceRoot) {
|
|
37841
|
-
if (scope === "codex-prompts") {
|
|
37842
|
-
if (provider !== "codex") {
|
|
37843
|
-
throw new Error(`Scope "codex-prompts" is only valid for provider "codex"`);
|
|
37844
|
-
}
|
|
37845
|
-
return path17.join(codexHomeDir(), "prompts");
|
|
37846
|
-
}
|
|
37847
|
-
if (scope === "project") {
|
|
37848
|
-
if (!workspaceRoot) {
|
|
37849
|
-
throw new Error(`workspaceRoot is required for scope "project"`);
|
|
37850
|
-
}
|
|
37851
|
-
const dotDir = provider === "claude" ? ".claude" : ".codex";
|
|
37852
|
-
return path17.join(workspaceRoot, dotDir, "skills");
|
|
37853
|
-
}
|
|
37854
|
-
if (provider === "claude") {
|
|
37855
|
-
return path17.join(homeDir(), ".claude", "skills");
|
|
37856
|
-
}
|
|
37857
|
-
return path17.join(codexHomeDir(), "skills");
|
|
37858
|
-
}
|
|
37859
|
-
function allowedRoots(workspaceRoot) {
|
|
37860
|
-
const roots = [
|
|
37861
|
-
path17.join(homeDir(), ".claude", "skills"),
|
|
37862
|
-
path17.join(codexHomeDir(), "skills"),
|
|
37863
|
-
path17.join(codexHomeDir(), "prompts")
|
|
37864
|
-
];
|
|
37865
|
-
if (workspaceRoot) {
|
|
37866
|
-
roots.push(path17.join(workspaceRoot, ".claude", "skills"));
|
|
37867
|
-
roots.push(path17.join(workspaceRoot, ".codex", "skills"));
|
|
37868
|
-
}
|
|
37869
|
-
return roots.map((r) => path17.resolve(r));
|
|
37870
|
-
}
|
|
37871
|
-
function isInsideAllowedRoot(absPath, workspaceRoot) {
|
|
37872
|
-
const resolved = path17.resolve(absPath);
|
|
37873
|
-
for (const root of allowedRoots(workspaceRoot)) {
|
|
37874
|
-
const rel = path17.relative(root, resolved);
|
|
37875
|
-
if (rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel)) {
|
|
37876
|
-
return true;
|
|
37877
|
-
}
|
|
37878
|
-
}
|
|
37879
|
-
return false;
|
|
37880
|
-
}
|
|
37881
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
37882
|
-
function parseSkillFile(text) {
|
|
37883
|
-
const match = FRONTMATTER_RE.exec(text);
|
|
37884
|
-
if (!match) {
|
|
37885
|
-
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
37886
|
-
}
|
|
37887
|
-
const yamlBlock = match[1] ?? "";
|
|
37888
|
-
const body = match[2] ?? "";
|
|
37889
|
-
return {
|
|
37890
|
-
rawFrontmatterLines: yamlBlock.split(/\r?\n/),
|
|
37891
|
-
body,
|
|
37892
|
-
hadFrontmatter: true
|
|
37893
|
-
};
|
|
37894
|
-
}
|
|
37895
|
-
function readDescription(text) {
|
|
37896
|
-
const parsed = parseSkillFile(text);
|
|
37897
|
-
if (!parsed.hadFrontmatter) return "";
|
|
37898
|
-
for (const line of parsed.rawFrontmatterLines) {
|
|
37899
|
-
const m = /^description\s*:\s*(.*)$/.exec(line);
|
|
37900
|
-
if (m) {
|
|
37901
|
-
const value = (m[1] ?? "").trim();
|
|
37902
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
37903
|
-
return value.slice(1, -1);
|
|
37904
|
-
}
|
|
37905
|
-
return value;
|
|
37906
|
-
}
|
|
37907
|
-
}
|
|
37908
|
-
return "";
|
|
37909
|
-
}
|
|
37910
|
-
function findOwnedSpans(lines) {
|
|
37911
|
-
const spans = [];
|
|
37912
|
-
let i = 0;
|
|
37913
|
-
while (i < lines.length) {
|
|
37914
|
-
const line = lines[i] ?? "";
|
|
37915
|
-
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
37916
|
-
if (!keyMatch) {
|
|
37917
|
-
i++;
|
|
37918
|
-
continue;
|
|
37919
|
-
}
|
|
37920
|
-
const key = keyMatch[1];
|
|
37921
|
-
const value = keyMatch[2];
|
|
37922
|
-
if (key !== "description" && key !== "argument-hint" && key !== "allowed-tools") {
|
|
37923
|
-
i++;
|
|
37924
|
-
continue;
|
|
37925
|
-
}
|
|
37926
|
-
let end = i + 1;
|
|
37927
|
-
if (value === "") {
|
|
37928
|
-
while (end < lines.length) {
|
|
37929
|
-
const next = lines[end] ?? "";
|
|
37930
|
-
if (/^\s+-\s+/.test(next) || /^\s*$/.test(next)) {
|
|
37931
|
-
end++;
|
|
37932
|
-
} else {
|
|
37933
|
-
break;
|
|
37934
|
-
}
|
|
37935
|
-
}
|
|
37936
|
-
}
|
|
37937
|
-
spans.push({ key, startLine: i, endLine: end });
|
|
37938
|
-
i = end;
|
|
37939
|
-
}
|
|
37940
|
-
return spans;
|
|
37941
|
-
}
|
|
37942
|
-
function emitFrontmatterValue(key, value) {
|
|
37943
|
-
if (key === "allowed-tools") {
|
|
37944
|
-
if (!Array.isArray(value) || value.length === 0) return [];
|
|
37945
|
-
return [`allowed-tools:`, ...value.map((v) => ` - ${v}`)];
|
|
37946
|
-
}
|
|
37947
|
-
const text = typeof value === "string" ? value : "";
|
|
37948
|
-
if (text.length === 0) return [];
|
|
37949
|
-
const needsQuote = /[:#\n]/.test(text) || text.startsWith(" ") || text.endsWith(" ");
|
|
37950
|
-
const formatted = needsQuote ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
37951
|
-
return [`${key}: ${formatted}`];
|
|
37952
|
-
}
|
|
37953
|
-
function rewriteFrontmatter(originalLines, next) {
|
|
37954
|
-
const spans = findOwnedSpans(originalLines);
|
|
37955
|
-
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
37956
|
-
const replacements = /* @__PURE__ */ new Map();
|
|
37957
|
-
if (next.description !== void 0) {
|
|
37958
|
-
replacements.set("description", emitFrontmatterValue("description", next.description));
|
|
37959
|
-
}
|
|
37960
|
-
if (next.argumentHint !== void 0) {
|
|
37961
|
-
replacements.set("argument-hint", emitFrontmatterValue("argument-hint", next.argumentHint));
|
|
37962
|
-
}
|
|
37963
|
-
if (next.allowedTools !== void 0) {
|
|
37964
|
-
replacements.set("allowed-tools", emitFrontmatterValue("allowed-tools", next.allowedTools));
|
|
37965
|
-
}
|
|
37966
|
-
const out = [];
|
|
37967
|
-
let i = 0;
|
|
37968
|
-
while (i < originalLines.length) {
|
|
37969
|
-
const span = spans.find((s) => s.startLine === i);
|
|
37970
|
-
if (span) {
|
|
37971
|
-
if (replacements.has(span.key)) {
|
|
37972
|
-
out.push(...replacements.get(span.key) ?? []);
|
|
37973
|
-
} else {
|
|
37974
|
-
for (let j = span.startLine; j < span.endLine; j++) {
|
|
37975
|
-
out.push(originalLines[j] ?? "");
|
|
37976
|
-
}
|
|
37977
|
-
}
|
|
37978
|
-
i = span.endLine;
|
|
37979
|
-
continue;
|
|
37980
|
-
}
|
|
37981
|
-
out.push(originalLines[i] ?? "");
|
|
37982
|
-
i++;
|
|
37983
|
-
}
|
|
37984
|
-
for (const key of ["description", "argument-hint", "allowed-tools"]) {
|
|
37985
|
-
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
37986
|
-
out.push(...replacements.get(key) ?? []);
|
|
37987
|
-
}
|
|
37988
|
-
}
|
|
37989
|
-
return out;
|
|
37990
|
-
}
|
|
37991
|
-
async function listSkills(args) {
|
|
37992
|
-
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
37993
|
-
let entries;
|
|
37994
|
-
try {
|
|
37995
|
-
entries = await fs11.readdir(dir, { withFileTypes: true });
|
|
37996
|
-
} catch {
|
|
37997
|
-
return [];
|
|
37998
|
-
}
|
|
37999
|
-
const results = [];
|
|
38000
|
-
if (args.scope === "codex-prompts") {
|
|
38001
|
-
for (const entry of entries) {
|
|
38002
|
-
if (!entry.isFile()) continue;
|
|
38003
|
-
if (!entry.name.endsWith(".md")) continue;
|
|
38004
|
-
const name = entry.name.slice(0, -".md".length);
|
|
38005
|
-
if (!name) continue;
|
|
38006
|
-
const fullPath = path17.join(dir, entry.name);
|
|
38007
|
-
const stat10 = await safeStat(fullPath);
|
|
38008
|
-
if (!stat10) continue;
|
|
38009
|
-
const description = await readDescriptionSafely(fullPath);
|
|
38010
|
-
results.push({
|
|
38011
|
-
id: `${args.provider}:${args.scope}:${name}`,
|
|
38012
|
-
provider: args.provider,
|
|
38013
|
-
scope: args.scope,
|
|
38014
|
-
name,
|
|
38015
|
-
path: fullPath,
|
|
38016
|
-
description,
|
|
38017
|
-
modifiedAt: stat10.mtime.toISOString(),
|
|
38018
|
-
size: stat10.size
|
|
38019
|
-
});
|
|
38020
|
-
}
|
|
38021
|
-
} else {
|
|
38022
|
-
for (const entry of entries) {
|
|
38023
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
38024
|
-
const skillDir = path17.join(dir, entry.name);
|
|
38025
|
-
const skillPath = path17.join(skillDir, "SKILL.md");
|
|
38026
|
-
const stat10 = await safeStat(skillPath);
|
|
38027
|
-
if (!stat10) continue;
|
|
38028
|
-
const description = await readDescriptionSafely(skillPath);
|
|
38029
|
-
results.push({
|
|
38030
|
-
id: `${args.provider}:${args.scope}:${entry.name}`,
|
|
38031
|
-
provider: args.provider,
|
|
38032
|
-
scope: args.scope,
|
|
38033
|
-
name: entry.name,
|
|
38034
|
-
path: skillPath,
|
|
38035
|
-
description,
|
|
38036
|
-
modifiedAt: stat10.mtime.toISOString(),
|
|
38037
|
-
size: stat10.size
|
|
38038
|
-
});
|
|
38039
|
-
}
|
|
38040
|
-
}
|
|
38041
|
-
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
38042
|
-
return results;
|
|
38043
|
-
}
|
|
38044
|
-
async function safeStat(filePath) {
|
|
38045
|
-
try {
|
|
38046
|
-
const s = await fs11.stat(filePath);
|
|
38047
|
-
return { mtime: s.mtime, size: s.size };
|
|
38048
|
-
} catch {
|
|
38049
|
-
return null;
|
|
38050
|
-
}
|
|
38051
|
-
}
|
|
38052
|
-
async function readDescriptionSafely(filePath) {
|
|
38053
|
-
try {
|
|
38054
|
-
const text = await fs11.readFile(filePath, "utf8");
|
|
38055
|
-
return readDescription(text);
|
|
38056
|
-
} catch {
|
|
38057
|
-
return "";
|
|
38058
|
-
}
|
|
38059
|
-
}
|
|
38060
|
-
async function createSkill(args) {
|
|
38061
|
-
if (!NAME_REGEX.test(args.name)) {
|
|
38062
|
-
throw new Error(
|
|
38063
|
-
`Invalid skill name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
38064
|
-
);
|
|
38065
|
-
}
|
|
38066
|
-
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
38067
|
-
throw new Error(`Skill name must not contain path separators or "..".`);
|
|
38068
|
-
}
|
|
38069
|
-
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
38070
|
-
await fs11.mkdir(dir, { recursive: true });
|
|
38071
|
-
if (args.scope === "codex-prompts") {
|
|
38072
|
-
const filePath2 = path17.join(dir, `${args.name}.md`);
|
|
38073
|
-
try {
|
|
38074
|
-
await fs11.access(filePath2);
|
|
38075
|
-
throw new Error(`A prompt named "${args.name}" already exists at ${filePath2}`);
|
|
38076
|
-
} catch (err) {
|
|
38077
|
-
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
38078
|
-
throw err;
|
|
38079
|
-
}
|
|
38080
|
-
if (err instanceof Error && err.message.includes("already exists")) {
|
|
38081
|
-
throw err;
|
|
38082
|
-
}
|
|
38083
|
-
}
|
|
38084
|
-
const initial2 = buildStarterPrompt(args.name);
|
|
38085
|
-
await fs11.writeFile(filePath2, initial2, "utf8");
|
|
38086
|
-
return { path: filePath2 };
|
|
38087
|
-
}
|
|
38088
|
-
const skillDir = path17.join(dir, args.name);
|
|
38089
|
-
let dirExists = false;
|
|
38090
|
-
try {
|
|
38091
|
-
const stat10 = await fs11.stat(skillDir);
|
|
38092
|
-
dirExists = stat10.isDirectory();
|
|
38093
|
-
} catch {
|
|
38094
|
-
dirExists = false;
|
|
38095
|
-
}
|
|
38096
|
-
if (dirExists) {
|
|
38097
|
-
throw new Error(`A skill named "${args.name}" already exists at ${skillDir}`);
|
|
38098
|
-
}
|
|
38099
|
-
await fs11.mkdir(skillDir, { recursive: true });
|
|
38100
|
-
const filePath = path17.join(skillDir, "SKILL.md");
|
|
38101
|
-
const initial = buildStarterSkill(args.name);
|
|
38102
|
-
await fs11.writeFile(filePath, initial, "utf8");
|
|
38103
|
-
return { path: filePath };
|
|
38104
|
-
}
|
|
38105
|
-
function buildStarterSkill(name) {
|
|
38106
|
-
return `---
|
|
38107
|
-
name: ${name}
|
|
38108
|
-
description: ""
|
|
38109
|
-
---
|
|
38110
|
-
|
|
38111
|
-
# ${name}
|
|
38112
|
-
|
|
38113
|
-
Describe when this skill should be used and what it does. The body of this
|
|
38114
|
-
file is loaded into the agent's context when the skill is invoked.
|
|
38115
|
-
`;
|
|
38116
|
-
}
|
|
38117
|
-
function buildStarterPrompt(name) {
|
|
38118
|
-
return `---
|
|
38119
|
-
description: ""
|
|
38120
|
-
argument-hint: ""
|
|
38121
|
-
---
|
|
38122
|
-
|
|
38123
|
-
# ${name}
|
|
38124
|
-
|
|
38125
|
-
Body of the prompt. Use \`$1\`, \`$2\`, ... or \`$ARGUMENTS\` for parameter expansion.
|
|
38126
|
-
`;
|
|
38127
|
-
}
|
|
38128
|
-
async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
38129
|
-
if (!path17.isAbsolute(args.path)) {
|
|
38130
|
-
throw new Error(`writeSkillFrontmatter expects an absolute path; got "${args.path}"`);
|
|
38131
|
-
}
|
|
38132
|
-
if (!isInsideAllowedRoot(args.path, workspaceRoot)) {
|
|
38133
|
-
throw new Error(`Path "${args.path}" is not inside an allowlisted skill root`);
|
|
38134
|
-
}
|
|
38135
|
-
let original;
|
|
38136
|
-
try {
|
|
38137
|
-
original = await fs11.readFile(args.path, "utf8");
|
|
38138
|
-
} catch (err) {
|
|
38139
|
-
throw new Error(
|
|
38140
|
-
`Failed to read skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
38141
|
-
);
|
|
38142
|
-
}
|
|
38143
|
-
const parsed = parseSkillFile(original);
|
|
38144
|
-
const newLines = rewriteFrontmatter(parsed.rawFrontmatterLines, args.frontmatter);
|
|
38145
|
-
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
38146
|
-
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
38147
|
-
${parsed.body}` : `${nextFrontmatter}
|
|
38148
|
-
|
|
38149
|
-
${original}`;
|
|
38150
|
-
await fs11.writeFile(args.path, nextContent, "utf8");
|
|
38355
|
+
return path40;
|
|
38151
38356
|
}
|
|
38152
38357
|
|
|
38153
38358
|
// ../server/src/utils/directory-suggestions.ts
|
|
38154
38359
|
import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
|
|
38155
|
-
import
|
|
38360
|
+
import path19 from "node:path";
|
|
38156
38361
|
var DEFAULT_LIMIT = 30;
|
|
38157
38362
|
var MAX_LIMIT = 100;
|
|
38158
38363
|
var DEFAULT_MAX_DEPTH = 6;
|
|
@@ -38238,7 +38443,7 @@ function normalizeLimit(limit) {
|
|
|
38238
38443
|
return Math.max(1, Math.min(MAX_LIMIT, bounded));
|
|
38239
38444
|
}
|
|
38240
38445
|
async function searchWithinParentDirectory(input) {
|
|
38241
|
-
const parentPath =
|
|
38446
|
+
const parentPath = path19.resolve(input.homeRoot, input.parentPart || ".");
|
|
38242
38447
|
const parentRoot = await resolveDirectory(parentPath);
|
|
38243
38448
|
if (!parentRoot || !isPathInsideRoot(input.homeRoot, parentRoot)) {
|
|
38244
38449
|
return [];
|
|
@@ -38303,7 +38508,7 @@ async function searchAcrossHomeTree(input) {
|
|
|
38303
38508
|
return dedupeAndSort(ranked).slice(0, input.limit);
|
|
38304
38509
|
}
|
|
38305
38510
|
async function searchWorkspaceWithinParentDirectory(input) {
|
|
38306
|
-
const parentPath =
|
|
38511
|
+
const parentPath = path19.resolve(input.workspaceRoot, input.parentPart || ".");
|
|
38307
38512
|
const parentRoot = await resolveDirectory(parentPath);
|
|
38308
38513
|
if (!parentRoot || !isPathInsideRoot(input.workspaceRoot, parentRoot)) {
|
|
38309
38514
|
return [];
|
|
@@ -38549,15 +38754,15 @@ function findSegmentMatchIndex(segments, predicate) {
|
|
|
38549
38754
|
return -1;
|
|
38550
38755
|
}
|
|
38551
38756
|
function normalizeRelativePath2(homeRoot, absolutePath) {
|
|
38552
|
-
const relative =
|
|
38757
|
+
const relative = path19.relative(homeRoot, absolutePath);
|
|
38553
38758
|
if (!relative) {
|
|
38554
38759
|
return ".";
|
|
38555
38760
|
}
|
|
38556
|
-
return relative.split(
|
|
38761
|
+
return relative.split(path19.sep).join("/");
|
|
38557
38762
|
}
|
|
38558
38763
|
function isPathInsideRoot(root, target) {
|
|
38559
|
-
const relative =
|
|
38560
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
38764
|
+
const relative = path19.relative(root, target);
|
|
38765
|
+
return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
|
|
38561
38766
|
}
|
|
38562
38767
|
function normalizeQueryParts(query2, homeRoot) {
|
|
38563
38768
|
const typedQuery = query2.trim().replace(/\\/g, "/");
|
|
@@ -38573,9 +38778,9 @@ function normalizeQueryParts(query2, homeRoot) {
|
|
|
38573
38778
|
normalized = normalized.slice(1);
|
|
38574
38779
|
}
|
|
38575
38780
|
}
|
|
38576
|
-
if (
|
|
38781
|
+
if (path19.isAbsolute(normalized)) {
|
|
38577
38782
|
isRooted = true;
|
|
38578
|
-
const absolute =
|
|
38783
|
+
const absolute = path19.resolve(normalized);
|
|
38579
38784
|
if (!isPathInsideRoot(homeRoot, absolute)) {
|
|
38580
38785
|
return null;
|
|
38581
38786
|
}
|
|
@@ -38614,8 +38819,8 @@ function normalizeQueryParts(query2, homeRoot) {
|
|
|
38614
38819
|
}
|
|
38615
38820
|
function normalizeWorkspaceQueryParts(query2, workspaceRoot) {
|
|
38616
38821
|
let normalized = query2.trim().replace(/\\/g, "/");
|
|
38617
|
-
if (
|
|
38618
|
-
const absolute =
|
|
38822
|
+
if (path19.isAbsolute(normalized)) {
|
|
38823
|
+
const absolute = path19.resolve(normalized);
|
|
38619
38824
|
if (!isPathInsideRoot(workspaceRoot, absolute)) {
|
|
38620
38825
|
return null;
|
|
38621
38826
|
}
|
|
@@ -38641,7 +38846,7 @@ function normalizeWorkspaceQueryParts(query2, workspaceRoot) {
|
|
|
38641
38846
|
}
|
|
38642
38847
|
async function resolveDirectory(inputPath) {
|
|
38643
38848
|
try {
|
|
38644
|
-
const resolved = await realpath(
|
|
38849
|
+
const resolved = await realpath(path19.resolve(inputPath));
|
|
38645
38850
|
const stats = await stat4(resolved);
|
|
38646
38851
|
if (!stats.isDirectory()) {
|
|
38647
38852
|
return null;
|
|
@@ -38668,7 +38873,7 @@ async function listChildDirectories(input) {
|
|
|
38668
38873
|
if (!dirent.isDirectory() && !dirent.isSymbolicLink()) {
|
|
38669
38874
|
continue;
|
|
38670
38875
|
}
|
|
38671
|
-
const candidatePath =
|
|
38876
|
+
const candidatePath = path19.join(input.directory, dirent.name);
|
|
38672
38877
|
const absolutePath = await resolveDirectoryCandidate({
|
|
38673
38878
|
candidatePath,
|
|
38674
38879
|
dirent,
|
|
@@ -38705,7 +38910,7 @@ async function listWorkspaceChildEntries(input) {
|
|
|
38705
38910
|
if (isIgnoredWorkspaceDirectoryName(dirent.name)) {
|
|
38706
38911
|
continue;
|
|
38707
38912
|
}
|
|
38708
|
-
const candidatePath =
|
|
38913
|
+
const candidatePath = path19.join(input.directory, dirent.name);
|
|
38709
38914
|
const entry = await resolveWorkspaceCandidate({
|
|
38710
38915
|
candidatePath,
|
|
38711
38916
|
dirent,
|
|
@@ -38728,7 +38933,7 @@ async function listWorkspaceChildEntries(input) {
|
|
|
38728
38933
|
}
|
|
38729
38934
|
async function resolveDirectoryCandidate(input) {
|
|
38730
38935
|
if (input.dirent.isDirectory()) {
|
|
38731
|
-
const resolved2 =
|
|
38936
|
+
const resolved2 = path19.resolve(input.candidatePath);
|
|
38732
38937
|
return isPathInsideRoot(input.homeRoot, resolved2) ? resolved2 : null;
|
|
38733
38938
|
}
|
|
38734
38939
|
const resolved = await resolveDirectory(input.candidatePath);
|
|
@@ -38739,14 +38944,14 @@ async function resolveDirectoryCandidate(input) {
|
|
|
38739
38944
|
}
|
|
38740
38945
|
async function resolveWorkspaceCandidate(input) {
|
|
38741
38946
|
if (input.dirent.isDirectory()) {
|
|
38742
|
-
const resolved =
|
|
38947
|
+
const resolved = path19.resolve(input.candidatePath);
|
|
38743
38948
|
if (!isPathInsideRoot(input.workspaceRoot, resolved)) {
|
|
38744
38949
|
return null;
|
|
38745
38950
|
}
|
|
38746
38951
|
return { absolutePath: resolved, kind: "directory" };
|
|
38747
38952
|
}
|
|
38748
38953
|
if (input.dirent.isFile()) {
|
|
38749
|
-
const resolved =
|
|
38954
|
+
const resolved = path19.resolve(input.candidatePath);
|
|
38750
38955
|
if (!isPathInsideRoot(input.workspaceRoot, resolved)) {
|
|
38751
38956
|
return null;
|
|
38752
38957
|
}
|
|
@@ -38826,7 +39031,7 @@ function pruneWorkspaceEntryListCache() {
|
|
|
38826
39031
|
// ../server/src/utils/directory-listing.ts
|
|
38827
39032
|
import { readdir as readdir3, stat as stat5, realpath as realpath2 } from "node:fs/promises";
|
|
38828
39033
|
import { homedir as homedir4 } from "node:os";
|
|
38829
|
-
import
|
|
39034
|
+
import path20 from "node:path";
|
|
38830
39035
|
var DEFAULT_LIMIT2 = 500;
|
|
38831
39036
|
async function listDirectoryContents(options) {
|
|
38832
39037
|
const includeFiles = options.includeFiles ?? false;
|
|
@@ -38837,7 +39042,7 @@ async function listDirectoryContents(options) {
|
|
|
38837
39042
|
const collected = [];
|
|
38838
39043
|
for (const dirent of dirents) {
|
|
38839
39044
|
if (!includeHidden && dirent.name.startsWith(".")) continue;
|
|
38840
|
-
const childPath =
|
|
39045
|
+
const childPath = path20.join(resolvedPath, dirent.name);
|
|
38841
39046
|
const kind = await classifyEntry(dirent, childPath);
|
|
38842
39047
|
if (!kind) continue;
|
|
38843
39048
|
if (kind === "file" && !includeFiles) continue;
|
|
@@ -38845,7 +39050,7 @@ async function listDirectoryContents(options) {
|
|
|
38845
39050
|
if (collected.length >= limit) break;
|
|
38846
39051
|
}
|
|
38847
39052
|
collected.sort(compareEntries);
|
|
38848
|
-
const parent =
|
|
39053
|
+
const parent = path20.dirname(resolvedPath);
|
|
38849
39054
|
return {
|
|
38850
39055
|
path: resolvedPath,
|
|
38851
39056
|
parent: parent === resolvedPath ? null : parent,
|
|
@@ -38856,12 +39061,12 @@ async function resolveAbsolutePath(rawPath) {
|
|
|
38856
39061
|
const home = process.env.HOME ?? homedir4();
|
|
38857
39062
|
const trimmed = rawPath.trim();
|
|
38858
39063
|
if (trimmed === "" || trimmed === "~") {
|
|
38859
|
-
return
|
|
39064
|
+
return path20.resolve(home);
|
|
38860
39065
|
}
|
|
38861
39066
|
if (trimmed.startsWith("~/")) {
|
|
38862
|
-
return
|
|
39067
|
+
return path20.resolve(home, trimmed.slice(2));
|
|
38863
39068
|
}
|
|
38864
|
-
if (!
|
|
39069
|
+
if (!path20.isAbsolute(trimmed)) {
|
|
38865
39070
|
throw new Error(
|
|
38866
39071
|
`list_directory requires an absolute path, an empty string, or a "~"-prefixed path; got ${JSON.stringify(rawPath)}`
|
|
38867
39072
|
);
|
|
@@ -38869,7 +39074,7 @@ async function resolveAbsolutePath(rawPath) {
|
|
|
38869
39074
|
try {
|
|
38870
39075
|
return await realpath2(trimmed);
|
|
38871
39076
|
} catch {
|
|
38872
|
-
return
|
|
39077
|
+
return path20.resolve(trimmed);
|
|
38873
39078
|
}
|
|
38874
39079
|
}
|
|
38875
39080
|
async function classifyEntry(dirent, fullPath) {
|
|
@@ -38910,8 +39115,8 @@ function resolveClientMessageId(clientMessageId, generateId = uuidv45) {
|
|
|
38910
39115
|
|
|
38911
39116
|
// ../server/src/server/chat/chat-service.ts
|
|
38912
39117
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
38913
|
-
import { promises as
|
|
38914
|
-
import
|
|
39118
|
+
import { promises as fs13 } from "node:fs";
|
|
39119
|
+
import path21 from "node:path";
|
|
38915
39120
|
import { z as z36 } from "zod";
|
|
38916
39121
|
var ChatStorePayloadSchema = z36.object({
|
|
38917
39122
|
rooms: z36.array(ChatRoomSchema),
|
|
@@ -38957,7 +39162,7 @@ var FileBackedChatService = class {
|
|
|
38957
39162
|
this.messagesByRoomId = /* @__PURE__ */ new Map();
|
|
38958
39163
|
this.persistQueue = Promise.resolve();
|
|
38959
39164
|
this.waitersByRoomId = /* @__PURE__ */ new Map();
|
|
38960
|
-
this.filePath =
|
|
39165
|
+
this.filePath = path21.join(options.appostleHome, "chat", "rooms.json");
|
|
38961
39166
|
this.logger = options.logger.child({ component: "chat-service" });
|
|
38962
39167
|
}
|
|
38963
39168
|
async initialize() {
|
|
@@ -39152,7 +39357,7 @@ var FileBackedChatService = class {
|
|
|
39152
39357
|
this.rooms.clear();
|
|
39153
39358
|
this.messagesByRoomId.clear();
|
|
39154
39359
|
try {
|
|
39155
|
-
const raw = await
|
|
39360
|
+
const raw = await fs13.readFile(this.filePath, "utf8");
|
|
39156
39361
|
const parsed = ChatStorePayloadSchema.parse(JSON.parse(raw));
|
|
39157
39362
|
for (const room of parsed.rooms) {
|
|
39158
39363
|
this.rooms.set(room.id, room);
|
|
@@ -39183,10 +39388,10 @@ var FileBackedChatService = class {
|
|
|
39183
39388
|
),
|
|
39184
39389
|
messages: Array.from(this.messagesByRoomId.values()).flat().sort((left, right) => left.createdAt.localeCompare(right.createdAt))
|
|
39185
39390
|
};
|
|
39186
|
-
await
|
|
39391
|
+
await fs13.mkdir(path21.dirname(this.filePath), { recursive: true });
|
|
39187
39392
|
const tempPath = `${this.filePath}.${process.pid}.${Date.now()}.${randomUUID10()}.tmp`;
|
|
39188
|
-
await
|
|
39189
|
-
await
|
|
39393
|
+
await fs13.writeFile(tempPath, JSON.stringify(payload, null, 2), "utf8");
|
|
39394
|
+
await fs13.rename(tempPath, this.filePath);
|
|
39190
39395
|
}
|
|
39191
39396
|
/**
|
|
39192
39397
|
* Look up a room by name, scoped to a specific owner (used by createRoom's
|
|
@@ -39358,42 +39563,42 @@ function buildChatMentionNotification(input) {
|
|
|
39358
39563
|
}
|
|
39359
39564
|
|
|
39360
39565
|
// ../server/src/server/roles/scanner.ts
|
|
39361
|
-
import
|
|
39362
|
-
import
|
|
39363
|
-
import
|
|
39566
|
+
import fs14 from "node:fs/promises";
|
|
39567
|
+
import os9 from "node:os";
|
|
39568
|
+
import path22 from "node:path";
|
|
39364
39569
|
var NAME_REGEX2 = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
39365
39570
|
function homeDir2() {
|
|
39366
|
-
return process.env.HOME ||
|
|
39571
|
+
return process.env.HOME || os9.homedir();
|
|
39367
39572
|
}
|
|
39368
39573
|
function resolveScopeDir2(scope, workspaceRoot) {
|
|
39369
39574
|
if (scope === "project") {
|
|
39370
39575
|
if (!workspaceRoot) {
|
|
39371
39576
|
throw new Error('workspaceRoot is required for scope "project"');
|
|
39372
39577
|
}
|
|
39373
|
-
return
|
|
39578
|
+
return path22.join(workspaceRoot, ".roles");
|
|
39374
39579
|
}
|
|
39375
|
-
return
|
|
39580
|
+
return path22.join(homeDir2(), ".appostle", ".roles");
|
|
39376
39581
|
}
|
|
39377
39582
|
function allowedRoots2(workspaceRoot) {
|
|
39378
|
-
const roots = [
|
|
39583
|
+
const roots = [path22.join(homeDir2(), ".appostle", ".roles")];
|
|
39379
39584
|
if (workspaceRoot) {
|
|
39380
|
-
roots.push(
|
|
39585
|
+
roots.push(path22.join(workspaceRoot, ".roles"));
|
|
39381
39586
|
}
|
|
39382
|
-
return roots.map((r) =>
|
|
39587
|
+
return roots.map((r) => path22.resolve(r));
|
|
39383
39588
|
}
|
|
39384
39589
|
function isInsideAllowedRoot2(absPath, workspaceRoot) {
|
|
39385
|
-
const resolved =
|
|
39590
|
+
const resolved = path22.resolve(absPath);
|
|
39386
39591
|
for (const root of allowedRoots2(workspaceRoot)) {
|
|
39387
|
-
const rel =
|
|
39388
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
39592
|
+
const rel = path22.relative(root, resolved);
|
|
39593
|
+
if (rel === "" || !rel.startsWith("..") && !path22.isAbsolute(rel)) {
|
|
39389
39594
|
return true;
|
|
39390
39595
|
}
|
|
39391
39596
|
}
|
|
39392
39597
|
return false;
|
|
39393
39598
|
}
|
|
39394
|
-
var
|
|
39599
|
+
var FRONTMATTER_RE3 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
39395
39600
|
function parseRoleFile(text) {
|
|
39396
|
-
const match =
|
|
39601
|
+
const match = FRONTMATTER_RE3.exec(text);
|
|
39397
39602
|
if (!match) {
|
|
39398
39603
|
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
39399
39604
|
}
|
|
@@ -39573,7 +39778,7 @@ function rewriteFrontmatter2(originalLines, next) {
|
|
|
39573
39778
|
async function readRolesFromDir(scope, dir, category) {
|
|
39574
39779
|
let entries;
|
|
39575
39780
|
try {
|
|
39576
|
-
entries = await
|
|
39781
|
+
entries = await fs14.readdir(dir, { withFileTypes: true });
|
|
39577
39782
|
} catch {
|
|
39578
39783
|
return [];
|
|
39579
39784
|
}
|
|
@@ -39583,17 +39788,17 @@ async function readRolesFromDir(scope, dir, category) {
|
|
|
39583
39788
|
if (!entry.name.endsWith(".md")) continue;
|
|
39584
39789
|
const name = entry.name.slice(0, -".md".length);
|
|
39585
39790
|
if (!name || !NAME_REGEX2.test(name)) continue;
|
|
39586
|
-
const fullPath =
|
|
39791
|
+
const fullPath = path22.join(dir, entry.name);
|
|
39587
39792
|
let stat10;
|
|
39588
39793
|
try {
|
|
39589
|
-
const s = await
|
|
39794
|
+
const s = await fs14.stat(fullPath);
|
|
39590
39795
|
stat10 = { mtime: s.mtime, size: s.size };
|
|
39591
39796
|
} catch {
|
|
39592
39797
|
continue;
|
|
39593
39798
|
}
|
|
39594
39799
|
let parsed;
|
|
39595
39800
|
try {
|
|
39596
|
-
const text = await
|
|
39801
|
+
const text = await fs14.readFile(fullPath, "utf8");
|
|
39597
39802
|
parsed = parseFrontmatter(text);
|
|
39598
39803
|
} catch {
|
|
39599
39804
|
parsed = {
|
|
@@ -39627,13 +39832,13 @@ async function readRolesFromDir(scope, dir, category) {
|
|
|
39627
39832
|
async function readRolesFromScopeDir(scope, scopeDir) {
|
|
39628
39833
|
let topEntries;
|
|
39629
39834
|
try {
|
|
39630
|
-
topEntries = await
|
|
39835
|
+
topEntries = await fs14.readdir(scopeDir, { withFileTypes: true });
|
|
39631
39836
|
} catch {
|
|
39632
39837
|
return [];
|
|
39633
39838
|
}
|
|
39634
39839
|
const flat = await readRolesFromDir(scope, scopeDir, null);
|
|
39635
39840
|
const categoryResults = await Promise.all(
|
|
39636
|
-
topEntries.filter((e) => e.isDirectory() && NAME_REGEX2.test(e.name)).map((e) => readRolesFromDir(scope,
|
|
39841
|
+
topEntries.filter((e) => e.isDirectory() && NAME_REGEX2.test(e.name)).map((e) => readRolesFromDir(scope, path22.join(scopeDir, e.name), e.name))
|
|
39637
39842
|
);
|
|
39638
39843
|
const all = [...flat, ...categoryResults.flat()];
|
|
39639
39844
|
all.sort((a, b) => {
|
|
@@ -39668,7 +39873,7 @@ async function resolveRole(args) {
|
|
|
39668
39873
|
const match = roles.find((r) => r.name === args.roleName);
|
|
39669
39874
|
if (!match) return null;
|
|
39670
39875
|
try {
|
|
39671
|
-
const text = await
|
|
39876
|
+
const text = await fs14.readFile(match.path, "utf8");
|
|
39672
39877
|
const parsed = parseRoleFile(text);
|
|
39673
39878
|
const fm = parseFrontmatter(text);
|
|
39674
39879
|
const body = parsed.body.trim();
|
|
@@ -39688,11 +39893,11 @@ async function createRole(args) {
|
|
|
39688
39893
|
throw new Error(`Role name must not contain path separators or "..".`);
|
|
39689
39894
|
}
|
|
39690
39895
|
const scopeDir = resolveScopeDir2(args.scope, args.workspaceRoot);
|
|
39691
|
-
const dir = args.category ?
|
|
39692
|
-
await
|
|
39693
|
-
const filePath =
|
|
39896
|
+
const dir = args.category ? path22.join(scopeDir, args.category) : scopeDir;
|
|
39897
|
+
await fs14.mkdir(dir, { recursive: true });
|
|
39898
|
+
const filePath = path22.join(dir, `${args.name}.md`);
|
|
39694
39899
|
try {
|
|
39695
|
-
await
|
|
39900
|
+
await fs14.access(filePath);
|
|
39696
39901
|
throw new Error(`A role named "${args.name}" already exists at ${filePath}`);
|
|
39697
39902
|
} catch (err) {
|
|
39698
39903
|
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
@@ -39703,7 +39908,7 @@ async function createRole(args) {
|
|
|
39703
39908
|
}
|
|
39704
39909
|
}
|
|
39705
39910
|
const initial = buildStarterRole(args.name);
|
|
39706
|
-
await
|
|
39911
|
+
await fs14.writeFile(filePath, initial, "utf8");
|
|
39707
39912
|
return { path: filePath };
|
|
39708
39913
|
}
|
|
39709
39914
|
function buildStarterRole(name) {
|
|
@@ -39724,7 +39929,7 @@ the role is invoked.
|
|
|
39724
39929
|
`;
|
|
39725
39930
|
}
|
|
39726
39931
|
async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
39727
|
-
if (!
|
|
39932
|
+
if (!path22.isAbsolute(args.path)) {
|
|
39728
39933
|
throw new Error(`writeRoleFrontmatter expects an absolute path; got "${args.path}"`);
|
|
39729
39934
|
}
|
|
39730
39935
|
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
@@ -39732,7 +39937,7 @@ async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
|
39732
39937
|
}
|
|
39733
39938
|
let original;
|
|
39734
39939
|
try {
|
|
39735
|
-
original = await
|
|
39940
|
+
original = await fs14.readFile(args.path, "utf8");
|
|
39736
39941
|
} catch (err) {
|
|
39737
39942
|
throw new Error(
|
|
39738
39943
|
`Failed to read role file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -39745,23 +39950,23 @@ async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
|
39745
39950
|
${parsed.body}` : `${nextFrontmatter}
|
|
39746
39951
|
|
|
39747
39952
|
${original}`;
|
|
39748
|
-
await
|
|
39953
|
+
await fs14.writeFile(args.path, nextContent, "utf8");
|
|
39749
39954
|
}
|
|
39750
39955
|
async function moveRole(args, workspaceRoot) {
|
|
39751
|
-
if (!
|
|
39956
|
+
if (!path22.isAbsolute(args.path)) {
|
|
39752
39957
|
throw new Error(`moveRole expects an absolute path; got "${args.path}"`);
|
|
39753
39958
|
}
|
|
39754
39959
|
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
39755
39960
|
throw new Error(`Path "${args.path}" is not inside an allowlisted role root`);
|
|
39756
39961
|
}
|
|
39757
|
-
const oldDir =
|
|
39758
|
-
const oldFilename =
|
|
39962
|
+
const oldDir = path22.dirname(args.path);
|
|
39963
|
+
const oldFilename = path22.basename(args.path, ".md");
|
|
39759
39964
|
const roots = allowedRoots2(workspaceRoot);
|
|
39760
|
-
const rolesRoot = roots.find((r) =>
|
|
39965
|
+
const rolesRoot = roots.find((r) => path22.resolve(args.path).startsWith(r));
|
|
39761
39966
|
if (!rolesRoot) {
|
|
39762
39967
|
throw new Error(`Cannot determine roles root for "${args.path}"`);
|
|
39763
39968
|
}
|
|
39764
|
-
const relFromRoot =
|
|
39969
|
+
const relFromRoot = path22.relative(rolesRoot, path22.dirname(args.path));
|
|
39765
39970
|
const currentCategory = relFromRoot && relFromRoot !== "." ? relFromRoot : "";
|
|
39766
39971
|
const newName = args.newName ?? oldFilename;
|
|
39767
39972
|
const newCategory = args.newCategory !== void 0 ? args.newCategory : currentCategory;
|
|
@@ -39771,19 +39976,19 @@ async function moveRole(args, workspaceRoot) {
|
|
|
39771
39976
|
if (newCategory && !NAME_REGEX2.test(newCategory)) {
|
|
39772
39977
|
throw new Error(`Invalid category name: "${newCategory}"`);
|
|
39773
39978
|
}
|
|
39774
|
-
const newDir = newCategory ?
|
|
39775
|
-
const newPath =
|
|
39776
|
-
if (
|
|
39979
|
+
const newDir = newCategory ? path22.join(rolesRoot, newCategory) : rolesRoot;
|
|
39980
|
+
const newPath = path22.join(newDir, `${newName}.md`);
|
|
39981
|
+
if (path22.resolve(newPath) === path22.resolve(args.path)) {
|
|
39777
39982
|
return { path: args.path };
|
|
39778
39983
|
}
|
|
39779
|
-
await
|
|
39984
|
+
await fs14.mkdir(newDir, { recursive: true });
|
|
39780
39985
|
try {
|
|
39781
|
-
await
|
|
39986
|
+
await fs14.access(newPath);
|
|
39782
39987
|
throw new Error(`A role already exists at "${newPath}"`);
|
|
39783
39988
|
} catch (err) {
|
|
39784
39989
|
if (err.code !== "ENOENT") throw err;
|
|
39785
39990
|
}
|
|
39786
|
-
await
|
|
39991
|
+
await fs14.rename(args.path, newPath);
|
|
39787
39992
|
const frontmatterPatch = {};
|
|
39788
39993
|
if (newName !== oldFilename) {
|
|
39789
39994
|
frontmatterPatch.description = void 0;
|
|
@@ -39799,9 +40004,9 @@ async function moveRole(args, workspaceRoot) {
|
|
|
39799
40004
|
);
|
|
39800
40005
|
if (oldDir !== rolesRoot) {
|
|
39801
40006
|
try {
|
|
39802
|
-
const remaining = await
|
|
40007
|
+
const remaining = await fs14.readdir(oldDir);
|
|
39803
40008
|
if (remaining.length === 0) {
|
|
39804
|
-
await
|
|
40009
|
+
await fs14.rmdir(oldDir);
|
|
39805
40010
|
}
|
|
39806
40011
|
} catch {
|
|
39807
40012
|
}
|
|
@@ -39810,18 +40015,18 @@ async function moveRole(args, workspaceRoot) {
|
|
|
39810
40015
|
}
|
|
39811
40016
|
|
|
39812
40017
|
// ../server/src/server/brands/scanner.ts
|
|
39813
|
-
import
|
|
39814
|
-
import
|
|
40018
|
+
import fs15 from "node:fs/promises";
|
|
40019
|
+
import path23 from "node:path";
|
|
39815
40020
|
var CATEGORY_REGEX = /^[a-z0-9][a-z0-9_-]*$/i;
|
|
39816
40021
|
function resolveAssetsDir(workspaceRoot, category) {
|
|
39817
|
-
const base =
|
|
40022
|
+
const base = path23.join(workspaceRoot, ".appostle", "brand", "assets");
|
|
39818
40023
|
if (!category) return base;
|
|
39819
40024
|
if (!CATEGORY_REGEX.test(category) || category.includes("..")) {
|
|
39820
40025
|
throw new Error(
|
|
39821
40026
|
`Invalid asset category "${category}". Use letters, digits, dot, underscore, dash.`
|
|
39822
40027
|
);
|
|
39823
40028
|
}
|
|
39824
|
-
return
|
|
40029
|
+
return path23.join(base, category);
|
|
39825
40030
|
}
|
|
39826
40031
|
function relativePathFor(category, fileName) {
|
|
39827
40032
|
return category ? `assets/${category}/${fileName}` : `assets/${fileName}`;
|
|
@@ -39829,7 +40034,7 @@ function relativePathFor(category, fileName) {
|
|
|
39829
40034
|
async function removeSiblingExtensions(dir, targetName, keepFileName) {
|
|
39830
40035
|
let entries;
|
|
39831
40036
|
try {
|
|
39832
|
-
entries = await
|
|
40037
|
+
entries = await fs15.readdir(dir);
|
|
39833
40038
|
} catch {
|
|
39834
40039
|
return;
|
|
39835
40040
|
}
|
|
@@ -39837,9 +40042,9 @@ async function removeSiblingExtensions(dir, targetName, keepFileName) {
|
|
|
39837
40042
|
if (entry === keepFileName) continue;
|
|
39838
40043
|
if (!entry.startsWith(`${targetName}.`)) continue;
|
|
39839
40044
|
try {
|
|
39840
|
-
const stat10 = await
|
|
40045
|
+
const stat10 = await fs15.stat(path23.join(dir, entry));
|
|
39841
40046
|
if (!stat10.isFile()) continue;
|
|
39842
|
-
await
|
|
40047
|
+
await fs15.unlink(path23.join(dir, entry));
|
|
39843
40048
|
} catch {
|
|
39844
40049
|
}
|
|
39845
40050
|
}
|
|
@@ -39850,7 +40055,7 @@ function convertSvgToMono(svg, color) {
|
|
|
39850
40055
|
async function runDerivations(options) {
|
|
39851
40056
|
const { primaryAbsolutePath, assetsDir, category, derive } = options;
|
|
39852
40057
|
if (derive.length === 0) return [];
|
|
39853
|
-
const ext =
|
|
40058
|
+
const ext = path23.extname(primaryAbsolutePath).toLowerCase();
|
|
39854
40059
|
const isSvg = ext === ".svg";
|
|
39855
40060
|
const results = [];
|
|
39856
40061
|
for (const spec of derive) {
|
|
@@ -39882,12 +40087,12 @@ async function runDerivations(options) {
|
|
|
39882
40087
|
continue;
|
|
39883
40088
|
}
|
|
39884
40089
|
try {
|
|
39885
|
-
const sourceText = await
|
|
40090
|
+
const sourceText = await fs15.readFile(primaryAbsolutePath, "utf8");
|
|
39886
40091
|
const monoText = convertSvgToMono(sourceText, spec.color);
|
|
39887
40092
|
const fileName = `${spec.targetName}${ext}`;
|
|
39888
|
-
const destAbs =
|
|
39889
|
-
const rel =
|
|
39890
|
-
if (rel.startsWith("..") ||
|
|
40093
|
+
const destAbs = path23.resolve(assetsDir, fileName);
|
|
40094
|
+
const rel = path23.relative(path23.resolve(assetsDir), destAbs);
|
|
40095
|
+
if (rel.startsWith("..") || path23.isAbsolute(rel)) {
|
|
39891
40096
|
results.push({
|
|
39892
40097
|
targetName: spec.targetName,
|
|
39893
40098
|
relativePath: "",
|
|
@@ -39896,7 +40101,7 @@ async function runDerivations(options) {
|
|
|
39896
40101
|
});
|
|
39897
40102
|
continue;
|
|
39898
40103
|
}
|
|
39899
|
-
await
|
|
40104
|
+
await fs15.writeFile(destAbs, monoText, "utf8");
|
|
39900
40105
|
results.push({
|
|
39901
40106
|
targetName: spec.targetName,
|
|
39902
40107
|
relativePath: relativePathFor(category, fileName),
|
|
@@ -39921,30 +40126,30 @@ function resolveScopeDir3(scope, workspaceRoot) {
|
|
|
39921
40126
|
if (!workspaceRoot) {
|
|
39922
40127
|
throw new Error('workspaceRoot is required for scope "project"');
|
|
39923
40128
|
}
|
|
39924
|
-
return
|
|
40129
|
+
return path23.join(workspaceRoot, ".appostle", "brand");
|
|
39925
40130
|
}
|
|
39926
40131
|
throw new Error(`Unknown scope: ${scope}`);
|
|
39927
40132
|
}
|
|
39928
40133
|
function allowedRoots3(workspaceRoot) {
|
|
39929
40134
|
const roots = [];
|
|
39930
40135
|
if (workspaceRoot) {
|
|
39931
|
-
roots.push(
|
|
40136
|
+
roots.push(path23.join(workspaceRoot, ".appostle", "brand"));
|
|
39932
40137
|
}
|
|
39933
|
-
return roots.map((r) =>
|
|
40138
|
+
return roots.map((r) => path23.resolve(r));
|
|
39934
40139
|
}
|
|
39935
40140
|
function isInsideAllowedRoot3(absPath, workspaceRoot) {
|
|
39936
|
-
const resolved =
|
|
40141
|
+
const resolved = path23.resolve(absPath);
|
|
39937
40142
|
for (const root of allowedRoots3(workspaceRoot)) {
|
|
39938
|
-
const rel =
|
|
39939
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
40143
|
+
const rel = path23.relative(root, resolved);
|
|
40144
|
+
if (rel === "" || !rel.startsWith("..") && !path23.isAbsolute(rel)) {
|
|
39940
40145
|
return true;
|
|
39941
40146
|
}
|
|
39942
40147
|
}
|
|
39943
40148
|
return false;
|
|
39944
40149
|
}
|
|
39945
|
-
var
|
|
40150
|
+
var FRONTMATTER_RE4 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
39946
40151
|
function parseBrandFile(text) {
|
|
39947
|
-
const match =
|
|
40152
|
+
const match = FRONTMATTER_RE4.exec(text);
|
|
39948
40153
|
if (!match) {
|
|
39949
40154
|
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
39950
40155
|
}
|
|
@@ -40140,7 +40345,7 @@ function rewriteFrontmatter3(originalLines, next) {
|
|
|
40140
40345
|
async function readBrandsFromDir(scope, dir) {
|
|
40141
40346
|
let entries;
|
|
40142
40347
|
try {
|
|
40143
|
-
entries = await
|
|
40348
|
+
entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
40144
40349
|
} catch {
|
|
40145
40350
|
return [];
|
|
40146
40351
|
}
|
|
@@ -40150,19 +40355,21 @@ async function readBrandsFromDir(scope, dir) {
|
|
|
40150
40355
|
if (!entry.name.endsWith(".md")) continue;
|
|
40151
40356
|
const name = entry.name.slice(0, -".md".length);
|
|
40152
40357
|
if (!name || !NAME_REGEX3.test(name)) continue;
|
|
40153
|
-
const fullPath =
|
|
40358
|
+
const fullPath = path23.join(dir, entry.name);
|
|
40154
40359
|
let stat10;
|
|
40155
40360
|
try {
|
|
40156
|
-
const s = await
|
|
40361
|
+
const s = await fs15.stat(fullPath);
|
|
40157
40362
|
stat10 = { mtime: s.mtime, size: s.size };
|
|
40158
40363
|
} catch {
|
|
40159
40364
|
continue;
|
|
40160
40365
|
}
|
|
40161
40366
|
let parsed;
|
|
40367
|
+
let body = "";
|
|
40162
40368
|
try {
|
|
40163
|
-
const text = await
|
|
40164
|
-
const { rawFrontmatterLines, hadFrontmatter } = parseBrandFile(text);
|
|
40369
|
+
const text = await fs15.readFile(fullPath, "utf8");
|
|
40370
|
+
const { rawFrontmatterLines, body: parsedBody, hadFrontmatter } = parseBrandFile(text);
|
|
40165
40371
|
parsed = hadFrontmatter ? parseFrontmatter2(rawFrontmatterLines) : { description: "", variables: [] };
|
|
40372
|
+
body = parsedBody;
|
|
40166
40373
|
} catch {
|
|
40167
40374
|
parsed = { description: "", variables: [] };
|
|
40168
40375
|
}
|
|
@@ -40174,7 +40381,8 @@ async function readBrandsFromDir(scope, dir) {
|
|
|
40174
40381
|
variables: parsed.variables,
|
|
40175
40382
|
path: fullPath,
|
|
40176
40383
|
modifiedAt: stat10.mtime.toISOString(),
|
|
40177
|
-
size: stat10.size
|
|
40384
|
+
size: stat10.size,
|
|
40385
|
+
body
|
|
40178
40386
|
});
|
|
40179
40387
|
}
|
|
40180
40388
|
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -40193,10 +40401,10 @@ async function createBrand(args) {
|
|
|
40193
40401
|
throw new Error(`Brand name must not contain path separators or "..".`);
|
|
40194
40402
|
}
|
|
40195
40403
|
const dir = resolveScopeDir3("project", args.workspaceRoot);
|
|
40196
|
-
await
|
|
40197
|
-
const filePath =
|
|
40404
|
+
await fs15.mkdir(dir, { recursive: true });
|
|
40405
|
+
const filePath = path23.join(dir, `${args.name}.md`);
|
|
40198
40406
|
try {
|
|
40199
|
-
await
|
|
40407
|
+
await fs15.access(filePath);
|
|
40200
40408
|
throw new Error(`A brand named "${args.name}" already exists at ${filePath}`);
|
|
40201
40409
|
} catch (err) {
|
|
40202
40410
|
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
@@ -40206,7 +40414,7 @@ async function createBrand(args) {
|
|
|
40206
40414
|
throw err;
|
|
40207
40415
|
}
|
|
40208
40416
|
}
|
|
40209
|
-
await
|
|
40417
|
+
await fs15.writeFile(filePath, buildStarterBrand(args.name), "utf8");
|
|
40210
40418
|
return { path: filePath };
|
|
40211
40419
|
}
|
|
40212
40420
|
function buildStarterBrand(name) {
|
|
@@ -40250,7 +40458,7 @@ async function copyBrandAsset(args) {
|
|
|
40250
40458
|
if (!args.workspaceRoot) {
|
|
40251
40459
|
throw new Error("workspaceRoot is required to copy a brand asset");
|
|
40252
40460
|
}
|
|
40253
|
-
if (!args.sourcePath || !
|
|
40461
|
+
if (!args.sourcePath || !path23.isAbsolute(args.sourcePath)) {
|
|
40254
40462
|
throw new Error(`copyBrandAsset expects an absolute sourcePath; got "${args.sourcePath}"`);
|
|
40255
40463
|
}
|
|
40256
40464
|
if (!TARGET_NAME_REGEX.test(args.targetName) || args.targetName.includes("..")) {
|
|
@@ -40258,21 +40466,21 @@ async function copyBrandAsset(args) {
|
|
|
40258
40466
|
`Invalid targetName "${args.targetName}". Use letters, digits, dot, underscore, dash.`
|
|
40259
40467
|
);
|
|
40260
40468
|
}
|
|
40261
|
-
const stats = await
|
|
40469
|
+
const stats = await fs15.stat(args.sourcePath);
|
|
40262
40470
|
if (!stats.isFile()) {
|
|
40263
40471
|
throw new Error(`Source path is not a regular file: ${args.sourcePath}`);
|
|
40264
40472
|
}
|
|
40265
|
-
const ext =
|
|
40473
|
+
const ext = path23.extname(args.sourcePath).toLowerCase();
|
|
40266
40474
|
const fileName = `${args.targetName}${ext}`;
|
|
40267
40475
|
const assetsDir = resolveAssetsDir(args.workspaceRoot, args.category);
|
|
40268
|
-
const destAbs =
|
|
40269
|
-
const rel =
|
|
40270
|
-
if (rel.startsWith("..") ||
|
|
40476
|
+
const destAbs = path23.resolve(assetsDir, fileName);
|
|
40477
|
+
const rel = path23.relative(path23.resolve(assetsDir), destAbs);
|
|
40478
|
+
if (rel.startsWith("..") || path23.isAbsolute(rel)) {
|
|
40271
40479
|
throw new Error(`Refusing to write outside of .appostle/brand/assets: ${destAbs}`);
|
|
40272
40480
|
}
|
|
40273
|
-
await
|
|
40481
|
+
await fs15.mkdir(assetsDir, { recursive: true });
|
|
40274
40482
|
await removeSiblingExtensions(assetsDir, args.targetName, fileName);
|
|
40275
|
-
await
|
|
40483
|
+
await fs15.copyFile(args.sourcePath, destAbs);
|
|
40276
40484
|
const derived = await runDerivations({
|
|
40277
40485
|
primaryAbsolutePath: destAbs,
|
|
40278
40486
|
assetsDir,
|
|
@@ -40297,13 +40505,13 @@ async function uploadBrandAsset(args) {
|
|
|
40297
40505
|
if (!args.dataBase64 || args.dataBase64.trim().length === 0) {
|
|
40298
40506
|
throw new Error("No file data provided for brand asset upload");
|
|
40299
40507
|
}
|
|
40300
|
-
const extFromSource = args.sourceName ?
|
|
40508
|
+
const extFromSource = args.sourceName ? path23.extname(args.sourceName).toLowerCase() : "";
|
|
40301
40509
|
const ext = extFromSource || ".png";
|
|
40302
40510
|
const fileName = `${args.targetName}${ext}`;
|
|
40303
40511
|
const assetsDir = resolveAssetsDir(args.workspaceRoot, args.category);
|
|
40304
|
-
const destAbs =
|
|
40305
|
-
const rel =
|
|
40306
|
-
if (rel.startsWith("..") ||
|
|
40512
|
+
const destAbs = path23.resolve(assetsDir, fileName);
|
|
40513
|
+
const rel = path23.relative(path23.resolve(assetsDir), destAbs);
|
|
40514
|
+
if (rel.startsWith("..") || path23.isAbsolute(rel)) {
|
|
40307
40515
|
throw new Error(`Refusing to write outside of .appostle/brand/assets: ${destAbs}`);
|
|
40308
40516
|
}
|
|
40309
40517
|
let data;
|
|
@@ -40315,9 +40523,9 @@ async function uploadBrandAsset(args) {
|
|
|
40315
40523
|
if (data.length === 0) {
|
|
40316
40524
|
throw new Error("Uploaded brand asset is empty");
|
|
40317
40525
|
}
|
|
40318
|
-
await
|
|
40526
|
+
await fs15.mkdir(assetsDir, { recursive: true });
|
|
40319
40527
|
await removeSiblingExtensions(assetsDir, args.targetName, fileName);
|
|
40320
|
-
await
|
|
40528
|
+
await fs15.writeFile(destAbs, data);
|
|
40321
40529
|
const derived = await runDerivations({
|
|
40322
40530
|
primaryAbsolutePath: destAbs,
|
|
40323
40531
|
assetsDir,
|
|
@@ -40331,7 +40539,7 @@ async function uploadBrandAsset(args) {
|
|
|
40331
40539
|
};
|
|
40332
40540
|
}
|
|
40333
40541
|
async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
40334
|
-
if (!
|
|
40542
|
+
if (!path23.isAbsolute(args.path)) {
|
|
40335
40543
|
throw new Error(`writeBrandFrontmatter expects an absolute path; got "${args.path}"`);
|
|
40336
40544
|
}
|
|
40337
40545
|
if (!isInsideAllowedRoot3(args.path, workspaceRoot)) {
|
|
@@ -40339,7 +40547,7 @@ async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
|
40339
40547
|
}
|
|
40340
40548
|
let original;
|
|
40341
40549
|
try {
|
|
40342
|
-
original = await
|
|
40550
|
+
original = await fs15.readFile(args.path, "utf8");
|
|
40343
40551
|
} catch (err) {
|
|
40344
40552
|
throw new Error(
|
|
40345
40553
|
`Failed to read brand file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -40352,7 +40560,7 @@ async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
|
40352
40560
|
${parsed.body}` : `${nextFrontmatter}
|
|
40353
40561
|
|
|
40354
40562
|
${original}`;
|
|
40355
|
-
await
|
|
40563
|
+
await fs15.writeFile(args.path, nextContent, "utf8");
|
|
40356
40564
|
}
|
|
40357
40565
|
|
|
40358
40566
|
// ../server/src/server/brand/token-generator.ts
|
|
@@ -40613,73 +40821,194 @@ async function generateAndApplyBrandTokens(options) {
|
|
|
40613
40821
|
return { generatedCount: acceptedUpdates.size };
|
|
40614
40822
|
}
|
|
40615
40823
|
|
|
40824
|
+
// ../server/src/server/brand/photography-analyzer.ts
|
|
40825
|
+
import { z as z38 } from "zod";
|
|
40826
|
+
var PhotographyResponseSchema = z38.object({
|
|
40827
|
+
dna: z38.string().min(1),
|
|
40828
|
+
dials: z38.record(z38.string(), z38.string())
|
|
40829
|
+
});
|
|
40830
|
+
function buildPrompt3(args) {
|
|
40831
|
+
const dialLines = args.selectVars.map((v) => {
|
|
40832
|
+
const options = (v.options ?? []).join(", ");
|
|
40833
|
+
return `- ${v.key} (${v.label || v.key}): current="${v.value || "(unset)"}" options=[${options}]`;
|
|
40834
|
+
}).join("\n");
|
|
40835
|
+
const refSection = args.referenceDescriptions.length > 0 ? [
|
|
40836
|
+
"",
|
|
40837
|
+
"Reference image filenames/descriptions provided by the user:",
|
|
40838
|
+
...args.referenceDescriptions.map((r) => `- ${r}`)
|
|
40839
|
+
].join("\n") : "";
|
|
40840
|
+
return [
|
|
40841
|
+
"You are a photography director analyzing a brand's photographic identity.",
|
|
40842
|
+
"The user has provided a brief describing the visual direction they want.",
|
|
40843
|
+
"Your job is to:",
|
|
40844
|
+
"1. Write a rich, specific Style DNA (2-3 sentences describing the photographic",
|
|
40845
|
+
" identity in concrete visual terms \u2014 lighting quality, tonal mood, depth",
|
|
40846
|
+
" characteristics, color rendering. No marketing fluff.)",
|
|
40847
|
+
"2. Pre-dial every camera control to match that style direction.",
|
|
40848
|
+
" ONLY use values that appear in the options list for each dial.",
|
|
40849
|
+
"",
|
|
40850
|
+
"User brief:",
|
|
40851
|
+
args.brief,
|
|
40852
|
+
refSection,
|
|
40853
|
+
"",
|
|
40854
|
+
"Camera dials (key, current value, valid options):",
|
|
40855
|
+
dialLines,
|
|
40856
|
+
"",
|
|
40857
|
+
"Return JSON only with the shape:",
|
|
40858
|
+
'{ "dna": "Rich style DNA description...", "dials": { "key": "value", ... } }',
|
|
40859
|
+
"",
|
|
40860
|
+
"Rules:",
|
|
40861
|
+
"- The dna field must be a non-empty string, 2-3 sentences.",
|
|
40862
|
+
"- Every value in dials must be one of the listed options for that key.",
|
|
40863
|
+
"- Include ALL dial keys in dials, even if you keep the current value.",
|
|
40864
|
+
"- Do not invent new keys. Do not output anything outside the JSON."
|
|
40865
|
+
].filter((line) => line !== "").join("\n");
|
|
40866
|
+
}
|
|
40867
|
+
async function analyzeAndApplyPhotographyStyle(options) {
|
|
40868
|
+
const { agentManager, workspaceRoot, brandPath, brief, referenceDescriptions, logger } = options;
|
|
40869
|
+
const brands = await listBrands({ workspaceRoot });
|
|
40870
|
+
const photoBrand = brands.find((b) => b.path === brandPath) ?? null;
|
|
40871
|
+
if (!photoBrand) {
|
|
40872
|
+
throw new Error(`Brand file not found at ${brandPath}`);
|
|
40873
|
+
}
|
|
40874
|
+
const allVars = photoBrand.variables;
|
|
40875
|
+
const selectVars = allVars.filter(
|
|
40876
|
+
(v) => v.type === "select" && v.options && v.options.length > 0
|
|
40877
|
+
);
|
|
40878
|
+
if (selectVars.length === 0) {
|
|
40879
|
+
throw new Error("No camera dials found in photography brand file");
|
|
40880
|
+
}
|
|
40881
|
+
const validOptions = /* @__PURE__ */ new Map();
|
|
40882
|
+
for (const v of selectVars) {
|
|
40883
|
+
validOptions.set(v.key, new Set(v.options ?? []));
|
|
40884
|
+
}
|
|
40885
|
+
const agentPrompt = buildPrompt3({
|
|
40886
|
+
brief,
|
|
40887
|
+
referenceDescriptions: referenceDescriptions ?? [],
|
|
40888
|
+
selectVars
|
|
40889
|
+
});
|
|
40890
|
+
let response;
|
|
40891
|
+
try {
|
|
40892
|
+
response = await generateStructuredAgentResponseWithFallback({
|
|
40893
|
+
manager: agentManager,
|
|
40894
|
+
cwd: workspaceRoot,
|
|
40895
|
+
prompt: agentPrompt,
|
|
40896
|
+
schema: PhotographyResponseSchema,
|
|
40897
|
+
schemaName: "PhotographyAnalysis",
|
|
40898
|
+
maxRetries: 2,
|
|
40899
|
+
providers: DEFAULT_STRUCTURED_GENERATION_PROVIDERS,
|
|
40900
|
+
agentConfigOverrides: {
|
|
40901
|
+
title: "Photography style analyzer",
|
|
40902
|
+
internal: true
|
|
40903
|
+
}
|
|
40904
|
+
});
|
|
40905
|
+
} catch (error) {
|
|
40906
|
+
if (error instanceof StructuredAgentResponseError || error instanceof StructuredAgentFallbackError) {
|
|
40907
|
+
logger.warn({ err: error, brandPath }, "Structured photography analysis failed");
|
|
40908
|
+
throw new Error("Photography analysis failed \u2014 the agent did not return valid JSON");
|
|
40909
|
+
}
|
|
40910
|
+
throw error;
|
|
40911
|
+
}
|
|
40912
|
+
const acceptedUpdates = /* @__PURE__ */ new Map();
|
|
40913
|
+
for (const [key, value] of Object.entries(response.dials)) {
|
|
40914
|
+
if (typeof value !== "string") continue;
|
|
40915
|
+
const trimmed = value.trim();
|
|
40916
|
+
const opts = validOptions.get(key);
|
|
40917
|
+
if (!opts) continue;
|
|
40918
|
+
if (!opts.has(trimmed)) continue;
|
|
40919
|
+
acceptedUpdates.set(key, trimmed);
|
|
40920
|
+
}
|
|
40921
|
+
acceptedUpdates.set("photo.dna", response.dna);
|
|
40922
|
+
acceptedUpdates.set("photo.brief", brief);
|
|
40923
|
+
const nextVariables = allVars.map((v) => {
|
|
40924
|
+
const update = acceptedUpdates.get(v.key);
|
|
40925
|
+
if (update === void 0) return v;
|
|
40926
|
+
return { ...v, value: update };
|
|
40927
|
+
});
|
|
40928
|
+
await writeBrandFrontmatter(
|
|
40929
|
+
{
|
|
40930
|
+
path: brandPath,
|
|
40931
|
+
frontmatter: {
|
|
40932
|
+
description: photoBrand.description,
|
|
40933
|
+
variables: nextVariables
|
|
40934
|
+
}
|
|
40935
|
+
},
|
|
40936
|
+
workspaceRoot
|
|
40937
|
+
);
|
|
40938
|
+
logger.info(
|
|
40939
|
+
{ brandPath, updatedCount: acceptedUpdates.size },
|
|
40940
|
+
"photography-analyzer: applied style analysis"
|
|
40941
|
+
);
|
|
40942
|
+
return { updatedCount: acceptedUpdates.size };
|
|
40943
|
+
}
|
|
40944
|
+
|
|
40616
40945
|
// ../server/src/server/brand/layout-generator.ts
|
|
40617
|
-
import { promises as
|
|
40618
|
-
import
|
|
40946
|
+
import { promises as fs16 } from "node:fs";
|
|
40947
|
+
import path24 from "node:path";
|
|
40619
40948
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
40620
|
-
import { z as
|
|
40949
|
+
import { z as z39 } from "zod";
|
|
40621
40950
|
var QA_FILENAME = "layout-qa.md";
|
|
40622
40951
|
var PROMPT_FILENAME = "layout-prompt.md";
|
|
40623
40952
|
var MAX_LOOKUP_LEVELS = 10;
|
|
40624
40953
|
var ROLE_FILE_RELATIVE = ".appostle/brand/assets/role/brand-layout-role.md";
|
|
40625
40954
|
async function findFileUpward(filename) {
|
|
40626
|
-
let dir =
|
|
40955
|
+
let dir = path24.dirname(fileURLToPath3(import.meta.url));
|
|
40627
40956
|
for (let i = 0; i < MAX_LOOKUP_LEVELS; i++) {
|
|
40628
|
-
const candidate =
|
|
40957
|
+
const candidate = path24.join(dir, filename);
|
|
40629
40958
|
try {
|
|
40630
|
-
await
|
|
40959
|
+
await fs16.access(candidate);
|
|
40631
40960
|
return candidate;
|
|
40632
40961
|
} catch {
|
|
40633
40962
|
}
|
|
40634
|
-
const parent =
|
|
40963
|
+
const parent = path24.dirname(dir);
|
|
40635
40964
|
if (parent === dir) break;
|
|
40636
40965
|
dir = parent;
|
|
40637
40966
|
}
|
|
40638
40967
|
return null;
|
|
40639
40968
|
}
|
|
40640
40969
|
function getRoleFilePath(workspaceRoot) {
|
|
40641
|
-
return
|
|
40970
|
+
return path24.join(workspaceRoot, ROLE_FILE_RELATIVE);
|
|
40642
40971
|
}
|
|
40643
40972
|
async function readRoleFile(workspaceRoot) {
|
|
40644
40973
|
try {
|
|
40645
|
-
return await
|
|
40974
|
+
return await fs16.readFile(getRoleFilePath(workspaceRoot), "utf8");
|
|
40646
40975
|
} catch {
|
|
40647
40976
|
return null;
|
|
40648
40977
|
}
|
|
40649
40978
|
}
|
|
40650
40979
|
async function writeRoleFile(workspaceRoot, content) {
|
|
40651
40980
|
const filePath = getRoleFilePath(workspaceRoot);
|
|
40652
|
-
await
|
|
40653
|
-
await
|
|
40654
|
-
}
|
|
40655
|
-
var QaNextResponseSchema =
|
|
40656
|
-
done:
|
|
40657
|
-
question:
|
|
40658
|
-
options:
|
|
40659
|
-
roleDocument:
|
|
40660
|
-
});
|
|
40661
|
-
var RefinementResponseSchema =
|
|
40662
|
-
roleDocument:
|
|
40663
|
-
});
|
|
40664
|
-
var WireframeBlockSchema =
|
|
40665
|
-
type:
|
|
40666
|
-
widthFraction:
|
|
40667
|
-
heightVh:
|
|
40668
|
-
});
|
|
40669
|
-
var WireframeSectionSchema =
|
|
40670
|
-
type:
|
|
40671
|
-
widthMode:
|
|
40672
|
-
heightVh:
|
|
40673
|
-
arrangement:
|
|
40674
|
-
blocks:
|
|
40675
|
-
bg:
|
|
40676
|
-
gapAfterVh:
|
|
40677
|
-
});
|
|
40678
|
-
var WireframeDataSchema =
|
|
40679
|
-
viewport:
|
|
40680
|
-
maxWidth:
|
|
40681
|
-
pageBg:
|
|
40682
|
-
sections:
|
|
40981
|
+
await fs16.mkdir(path24.dirname(filePath), { recursive: true });
|
|
40982
|
+
await fs16.writeFile(filePath, content, "utf8");
|
|
40983
|
+
}
|
|
40984
|
+
var QaNextResponseSchema = z39.object({
|
|
40985
|
+
done: z39.boolean(),
|
|
40986
|
+
question: z39.string().optional(),
|
|
40987
|
+
options: z39.array(z39.string()).optional(),
|
|
40988
|
+
roleDocument: z39.string().optional()
|
|
40989
|
+
});
|
|
40990
|
+
var RefinementResponseSchema = z39.object({
|
|
40991
|
+
roleDocument: z39.string()
|
|
40992
|
+
});
|
|
40993
|
+
var WireframeBlockSchema = z39.object({
|
|
40994
|
+
type: z39.enum(["headline", "subheadline", "text", "image", "cta", "spacer"]),
|
|
40995
|
+
widthFraction: z39.number(),
|
|
40996
|
+
heightVh: z39.number()
|
|
40997
|
+
});
|
|
40998
|
+
var WireframeSectionSchema = z39.object({
|
|
40999
|
+
type: z39.enum(["hero", "features", "content", "cta", "footer", "divider"]),
|
|
41000
|
+
widthMode: z39.enum(["full-bleed", "contained"]),
|
|
41001
|
+
heightVh: z39.number(),
|
|
41002
|
+
arrangement: z39.enum(["single-column", "split", "asymmetric-grid", "bento", "stacked"]),
|
|
41003
|
+
blocks: z39.array(WireframeBlockSchema),
|
|
41004
|
+
bg: z39.enum(["neutral", "alt", "accent", "image"]),
|
|
41005
|
+
gapAfterVh: z39.number()
|
|
41006
|
+
});
|
|
41007
|
+
var WireframeDataSchema = z39.object({
|
|
41008
|
+
viewport: z39.object({ width: z39.number(), height: z39.number() }),
|
|
41009
|
+
maxWidth: z39.number(),
|
|
41010
|
+
pageBg: z39.string(),
|
|
41011
|
+
sections: z39.array(WireframeSectionSchema)
|
|
40683
41012
|
});
|
|
40684
41013
|
function buildFullBrandContext(allBrands) {
|
|
40685
41014
|
const lines = [];
|
|
@@ -40736,7 +41065,7 @@ async function loadSpecFile(filename, logger) {
|
|
|
40736
41065
|
);
|
|
40737
41066
|
}
|
|
40738
41067
|
try {
|
|
40739
|
-
const content = await
|
|
41068
|
+
const content = await fs16.readFile(filePath, "utf8");
|
|
40740
41069
|
logger.debug({ filePath }, `layout-generator: loaded ${filename} from disk`);
|
|
40741
41070
|
return content;
|
|
40742
41071
|
} catch (err) {
|
|
@@ -41208,57 +41537,57 @@ async function fetchGitLabUsername(fetchImpl, accessToken) {
|
|
|
41208
41537
|
return null;
|
|
41209
41538
|
}
|
|
41210
41539
|
}
|
|
41211
|
-
async function readCredential(
|
|
41540
|
+
async function readCredential(path40, log) {
|
|
41212
41541
|
try {
|
|
41213
|
-
const raw = await readFile3(
|
|
41542
|
+
const raw = await readFile3(path40, "utf8");
|
|
41214
41543
|
const parsed = JSON.parse(raw);
|
|
41215
41544
|
return parsed.gitlab ?? null;
|
|
41216
41545
|
} catch (error) {
|
|
41217
41546
|
if (isNotFound(error)) return null;
|
|
41218
|
-
log.warn({ err: error, path:
|
|
41547
|
+
log.warn({ err: error, path: path40 }, "oauth.credentials.read_failed");
|
|
41219
41548
|
return null;
|
|
41220
41549
|
}
|
|
41221
41550
|
}
|
|
41222
|
-
async function persistCredential(
|
|
41223
|
-
await mkdir4(dirname6(
|
|
41551
|
+
async function persistCredential(path40, credential, log) {
|
|
41552
|
+
await mkdir4(dirname6(path40), { recursive: true });
|
|
41224
41553
|
let current = {};
|
|
41225
41554
|
try {
|
|
41226
|
-
const raw = await readFile3(
|
|
41555
|
+
const raw = await readFile3(path40, "utf8");
|
|
41227
41556
|
current = JSON.parse(raw);
|
|
41228
41557
|
} catch (error) {
|
|
41229
41558
|
if (!isNotFound(error)) {
|
|
41230
|
-
log.warn({ err: error, path:
|
|
41559
|
+
log.warn({ err: error, path: path40 }, "oauth.credentials.read_failed_overwriting");
|
|
41231
41560
|
}
|
|
41232
41561
|
}
|
|
41233
41562
|
const next = { ...current, gitlab: credential };
|
|
41234
|
-
const tmpPath = `${
|
|
41563
|
+
const tmpPath = `${path40}.tmp-${process.pid}-${Date.now()}`;
|
|
41235
41564
|
await writeFile4(tmpPath, JSON.stringify(next, null, 2), { mode: 384 });
|
|
41236
|
-
await rename(tmpPath,
|
|
41565
|
+
await rename(tmpPath, path40);
|
|
41237
41566
|
}
|
|
41238
|
-
async function deleteCredential(
|
|
41567
|
+
async function deleteCredential(path40, log) {
|
|
41239
41568
|
let current = {};
|
|
41240
41569
|
try {
|
|
41241
|
-
const raw = await readFile3(
|
|
41570
|
+
const raw = await readFile3(path40, "utf8");
|
|
41242
41571
|
current = JSON.parse(raw);
|
|
41243
41572
|
} catch (error) {
|
|
41244
41573
|
if (isNotFound(error)) return;
|
|
41245
|
-
log.warn({ err: error, path:
|
|
41574
|
+
log.warn({ err: error, path: path40 }, "oauth.credentials.delete_read_failed");
|
|
41246
41575
|
return;
|
|
41247
41576
|
}
|
|
41248
41577
|
delete current.gitlab;
|
|
41249
41578
|
if (Object.keys(current).length === 0) {
|
|
41250
41579
|
try {
|
|
41251
|
-
await unlink(
|
|
41580
|
+
await unlink(path40);
|
|
41252
41581
|
} catch (error) {
|
|
41253
41582
|
if (!isNotFound(error)) {
|
|
41254
|
-
log.warn({ err: error, path:
|
|
41583
|
+
log.warn({ err: error, path: path40 }, "oauth.credentials.unlink_failed");
|
|
41255
41584
|
}
|
|
41256
41585
|
}
|
|
41257
41586
|
return;
|
|
41258
41587
|
}
|
|
41259
|
-
const tmpPath = `${
|
|
41588
|
+
const tmpPath = `${path40}.tmp-${process.pid}-${Date.now()}`;
|
|
41260
41589
|
await writeFile4(tmpPath, JSON.stringify(current, null, 2), { mode: 384 });
|
|
41261
|
-
await rename(tmpPath,
|
|
41590
|
+
await rename(tmpPath, path40);
|
|
41262
41591
|
}
|
|
41263
41592
|
function defaultGlabConfigPath() {
|
|
41264
41593
|
const home = homedir5();
|
|
@@ -41273,15 +41602,15 @@ function defaultGlabConfigPath() {
|
|
|
41273
41602
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
41274
41603
|
return join13(xdg && xdg.length > 0 ? xdg : join13(home, ".config"), "glab-cli", "config.yml");
|
|
41275
41604
|
}
|
|
41276
|
-
async function writeGlabConfig(
|
|
41277
|
-
await mkdir4(dirname6(
|
|
41605
|
+
async function writeGlabConfig(path40, credential, log) {
|
|
41606
|
+
await mkdir4(dirname6(path40), { recursive: true });
|
|
41278
41607
|
let doc;
|
|
41279
41608
|
try {
|
|
41280
|
-
const raw = await readFile3(
|
|
41609
|
+
const raw = await readFile3(path40, "utf8");
|
|
41281
41610
|
doc = YAML.parseDocument(raw);
|
|
41282
41611
|
if (doc.errors.length > 0) {
|
|
41283
41612
|
log.warn(
|
|
41284
|
-
{ errors: doc.errors.map((e) => e.message), path:
|
|
41613
|
+
{ errors: doc.errors.map((e) => e.message), path: path40 },
|
|
41285
41614
|
"oauth.glab.parse_errors_replacing"
|
|
41286
41615
|
);
|
|
41287
41616
|
doc = YAML.parseDocument("{}");
|
|
@@ -41290,7 +41619,7 @@ async function writeGlabConfig(path39, credential, log) {
|
|
|
41290
41619
|
if (isNotFound(error)) {
|
|
41291
41620
|
doc = YAML.parseDocument("{}");
|
|
41292
41621
|
} else {
|
|
41293
|
-
log.warn({ err: error, path:
|
|
41622
|
+
log.warn({ err: error, path: path40 }, "oauth.glab.read_failed_replacing");
|
|
41294
41623
|
doc = YAML.parseDocument("{}");
|
|
41295
41624
|
}
|
|
41296
41625
|
}
|
|
@@ -41303,18 +41632,18 @@ async function writeGlabConfig(path39, credential, log) {
|
|
|
41303
41632
|
if (credential.username) {
|
|
41304
41633
|
hostEntry.set("user", credential.username);
|
|
41305
41634
|
}
|
|
41306
|
-
const tmpPath = `${
|
|
41635
|
+
const tmpPath = `${path40}.tmp-${process.pid}-${Date.now()}`;
|
|
41307
41636
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
41308
|
-
await rename(tmpPath,
|
|
41637
|
+
await rename(tmpPath, path40);
|
|
41309
41638
|
}
|
|
41310
|
-
async function removeGlabHost(
|
|
41639
|
+
async function removeGlabHost(path40, log) {
|
|
41311
41640
|
let doc;
|
|
41312
41641
|
try {
|
|
41313
|
-
const raw = await readFile3(
|
|
41642
|
+
const raw = await readFile3(path40, "utf8");
|
|
41314
41643
|
doc = YAML.parseDocument(raw);
|
|
41315
41644
|
} catch (error) {
|
|
41316
41645
|
if (isNotFound(error)) return;
|
|
41317
|
-
log.warn({ err: error, path:
|
|
41646
|
+
log.warn({ err: error, path: path40 }, "oauth.glab.remove_read_failed");
|
|
41318
41647
|
return;
|
|
41319
41648
|
}
|
|
41320
41649
|
const hosts = doc.get("hosts");
|
|
@@ -41323,9 +41652,9 @@ async function removeGlabHost(path39, log) {
|
|
|
41323
41652
|
if (hosts.items.length === 0) {
|
|
41324
41653
|
doc.delete("hosts");
|
|
41325
41654
|
}
|
|
41326
|
-
const tmpPath = `${
|
|
41655
|
+
const tmpPath = `${path40}.tmp-${process.pid}-${Date.now()}`;
|
|
41327
41656
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
41328
|
-
await rename(tmpPath,
|
|
41657
|
+
await rename(tmpPath, path40);
|
|
41329
41658
|
}
|
|
41330
41659
|
function ensureMap(doc, key) {
|
|
41331
41660
|
const existing = doc.get(key);
|
|
@@ -42364,7 +42693,7 @@ var MIN_STREAMING_SEGMENT_DURATION_MS = 1e3;
|
|
|
42364
42693
|
var MIN_STREAMING_SEGMENT_BYTES = Math.round(
|
|
42365
42694
|
PCM_BYTES_PER_MS * MIN_STREAMING_SEGMENT_DURATION_MS
|
|
42366
42695
|
);
|
|
42367
|
-
var AgentIdSchema2 =
|
|
42696
|
+
var AgentIdSchema2 = z40.string().uuid();
|
|
42368
42697
|
var VOICE_INTERRUPT_CONFIRMATION_MS = 500;
|
|
42369
42698
|
var VoiceFeatureUnavailableError = class extends Error {
|
|
42370
42699
|
constructor(context) {
|
|
@@ -43745,6 +44074,9 @@ var Session = class _Session {
|
|
|
43745
44074
|
case "brands/generate-wireframe":
|
|
43746
44075
|
await this.handleBrandGenerateWireframeRequest(msg);
|
|
43747
44076
|
break;
|
|
44077
|
+
case "brands/analyze-photography":
|
|
44078
|
+
await this.handleBrandAnalyzePhotographyRequest(msg);
|
|
44079
|
+
break;
|
|
43748
44080
|
case "rightfont/library":
|
|
43749
44081
|
await this.handleRightFontLibraryRequest(msg);
|
|
43750
44082
|
break;
|
|
@@ -45404,8 +45736,8 @@ var Session = class _Session {
|
|
|
45404
45736
|
}
|
|
45405
45737
|
async generateCommitMessage(cwd) {
|
|
45406
45738
|
const files = await listUncommittedFiles(cwd);
|
|
45407
|
-
const schema =
|
|
45408
|
-
message:
|
|
45739
|
+
const schema = z40.object({
|
|
45740
|
+
message: z40.string().min(1).max(100).describe(
|
|
45409
45741
|
"Short feature-level summary, lowercase, imperative mood, no trailing period, no filename references."
|
|
45410
45742
|
)
|
|
45411
45743
|
});
|
|
@@ -45450,9 +45782,9 @@ var Session = class _Session {
|
|
|
45450
45782
|
},
|
|
45451
45783
|
{ appostleHome: this.appostleHome }
|
|
45452
45784
|
);
|
|
45453
|
-
const schema =
|
|
45454
|
-
title:
|
|
45455
|
-
body:
|
|
45785
|
+
const schema = z40.object({
|
|
45786
|
+
title: z40.string().min(1).max(72),
|
|
45787
|
+
body: z40.string().min(1)
|
|
45456
45788
|
});
|
|
45457
45789
|
const fileList = diff.structured && diff.structured.length > 0 ? [
|
|
45458
45790
|
"Files changed:",
|
|
@@ -46393,7 +46725,7 @@ var Session = class _Session {
|
|
|
46393
46725
|
homeDir: process.env.HOME ?? homedir6(),
|
|
46394
46726
|
query: query2,
|
|
46395
46727
|
limit
|
|
46396
|
-
})).map((
|
|
46728
|
+
})).map((path40) => ({ path: path40, kind: "directory" }));
|
|
46397
46729
|
const directories = entries.filter((entry) => entry.kind === "directory").map((entry) => entry.path);
|
|
46398
46730
|
this.emit({
|
|
46399
46731
|
type: "directory_suggestions_response",
|
|
@@ -50944,6 +51276,38 @@ ${details}`.trim());
|
|
|
50944
51276
|
});
|
|
50945
51277
|
}
|
|
50946
51278
|
}
|
|
51279
|
+
async handleBrandAnalyzePhotographyRequest(request) {
|
|
51280
|
+
const { requestId, workspaceRoot, brandPath, brief, referenceDescriptions } = request;
|
|
51281
|
+
try {
|
|
51282
|
+
const result = await analyzeAndApplyPhotographyStyle({
|
|
51283
|
+
agentManager: this.agentManager,
|
|
51284
|
+
workspaceRoot: expandTilde(workspaceRoot),
|
|
51285
|
+
brandPath: expandTilde(brandPath),
|
|
51286
|
+
brief,
|
|
51287
|
+
referenceDescriptions,
|
|
51288
|
+
logger: this.sessionLogger
|
|
51289
|
+
});
|
|
51290
|
+
this.emit({
|
|
51291
|
+
type: "brands/analyze-photography/response",
|
|
51292
|
+
payload: {
|
|
51293
|
+
requestId,
|
|
51294
|
+
updatedCount: result.updatedCount,
|
|
51295
|
+
error: null
|
|
51296
|
+
}
|
|
51297
|
+
});
|
|
51298
|
+
} catch (error) {
|
|
51299
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51300
|
+
this.sessionLogger.error({ err: error, brandPath }, "Failed to analyze photography style");
|
|
51301
|
+
this.emit({
|
|
51302
|
+
type: "brands/analyze-photography/response",
|
|
51303
|
+
payload: {
|
|
51304
|
+
requestId,
|
|
51305
|
+
updatedCount: 0,
|
|
51306
|
+
error: message
|
|
51307
|
+
}
|
|
51308
|
+
});
|
|
51309
|
+
}
|
|
51310
|
+
}
|
|
50947
51311
|
async handleRightFontLibraryRequest(request) {
|
|
50948
51312
|
const { requestId, libraryPath } = request;
|
|
50949
51313
|
try {
|
|
@@ -53802,13 +54166,13 @@ import { join as join19 } from "node:path";
|
|
|
53802
54166
|
// ../server/src/server/speech/providers/local/sherpa/model-downloader.ts
|
|
53803
54167
|
import { createWriteStream } from "node:fs";
|
|
53804
54168
|
import { mkdir as mkdir6, rename as rename2, rm as rm2, stat as stat6 } from "node:fs/promises";
|
|
53805
|
-
import
|
|
54169
|
+
import path25 from "node:path";
|
|
53806
54170
|
import { Readable as Readable2 } from "node:stream";
|
|
53807
54171
|
import { pipeline } from "node:stream/promises";
|
|
53808
54172
|
import { spawn as spawn7 } from "node:child_process";
|
|
53809
54173
|
|
|
53810
54174
|
// ../server/src/server/speech/providers/local/sherpa/model-catalog.ts
|
|
53811
|
-
import { z as
|
|
54175
|
+
import { z as z41 } from "zod";
|
|
53812
54176
|
var SHERPA_ONNX_MODEL_CATALOG = {
|
|
53813
54177
|
"zipformer-bilingual-zh-en-2023-02-20": {
|
|
53814
54178
|
kind: "stt-online",
|
|
@@ -53901,7 +54265,7 @@ function buildAliasMap(modelIds) {
|
|
|
53901
54265
|
}
|
|
53902
54266
|
function createAliasedModelIdSchema(params) {
|
|
53903
54267
|
const validIds = new Set(params.modelIds);
|
|
53904
|
-
return
|
|
54268
|
+
return z41.string().trim().toLowerCase().refine(
|
|
53905
54269
|
(value) => validIds.has(value) || Object.prototype.hasOwnProperty.call(params.aliases, value),
|
|
53906
54270
|
{
|
|
53907
54271
|
message: "Invalid model id"
|
|
@@ -53938,11 +54302,11 @@ function getSherpaOnnxModelSpec(id) {
|
|
|
53938
54302
|
// ../server/src/server/speech/providers/local/sherpa/model-downloader.ts
|
|
53939
54303
|
function getSherpaOnnxModelDir(modelsDir, modelId) {
|
|
53940
54304
|
const spec = getSherpaOnnxModelSpec(modelId);
|
|
53941
|
-
return
|
|
54305
|
+
return path25.join(modelsDir, spec.extractedDir);
|
|
53942
54306
|
}
|
|
53943
54307
|
async function hasRequiredFiles(modelDir, requiredFiles) {
|
|
53944
54308
|
for (const rel of requiredFiles) {
|
|
53945
|
-
const abs =
|
|
54309
|
+
const abs = path25.join(modelDir, rel);
|
|
53946
54310
|
try {
|
|
53947
54311
|
const s = await stat6(abs);
|
|
53948
54312
|
if (s.isDirectory()) {
|
|
@@ -53968,7 +54332,7 @@ async function downloadToFile(options) {
|
|
|
53968
54332
|
throw new Error(`Failed to download ${url}: missing response body`);
|
|
53969
54333
|
}
|
|
53970
54334
|
const tmpPath = `${outputPath}.tmp-${Date.now()}`;
|
|
53971
|
-
await mkdir6(
|
|
54335
|
+
await mkdir6(path25.dirname(outputPath), { recursive: true });
|
|
53972
54336
|
const nodeStream = Readable2.fromWeb(res.body);
|
|
53973
54337
|
try {
|
|
53974
54338
|
await pipeline(nodeStream, createWriteStream(tmpPath));
|
|
@@ -54005,16 +54369,16 @@ async function ensureSherpaOnnxModel(options) {
|
|
|
54005
54369
|
modelId: options.modelId
|
|
54006
54370
|
});
|
|
54007
54371
|
const spec = getSherpaOnnxModelSpec(options.modelId);
|
|
54008
|
-
const modelDir =
|
|
54372
|
+
const modelDir = path25.join(options.modelsDir, spec.extractedDir);
|
|
54009
54373
|
if (await hasRequiredFiles(modelDir, spec.requiredFiles)) {
|
|
54010
54374
|
return modelDir;
|
|
54011
54375
|
}
|
|
54012
54376
|
logger.info({ modelsDir: options.modelsDir }, "Starting model download");
|
|
54013
54377
|
try {
|
|
54014
54378
|
if (spec.archiveUrl) {
|
|
54015
|
-
const downloadsDir =
|
|
54016
|
-
const archiveFilename =
|
|
54017
|
-
const archivePath =
|
|
54379
|
+
const downloadsDir = path25.join(options.modelsDir, ".downloads");
|
|
54380
|
+
const archiveFilename = path25.basename(new URL(spec.archiveUrl).pathname);
|
|
54381
|
+
const archivePath = path25.join(downloadsDir, archiveFilename);
|
|
54018
54382
|
if (!await isNonEmptyFile(archivePath)) {
|
|
54019
54383
|
await downloadToFile({
|
|
54020
54384
|
url: spec.archiveUrl,
|
|
@@ -54059,7 +54423,7 @@ async function ensureSherpaOnnxModel(options) {
|
|
|
54059
54423
|
if (spec.downloadFiles && spec.downloadFiles.length > 0) {
|
|
54060
54424
|
await mkdir6(modelDir, { recursive: true });
|
|
54061
54425
|
for (const file of spec.downloadFiles) {
|
|
54062
|
-
const dst =
|
|
54426
|
+
const dst = path25.join(modelDir, file.relPath);
|
|
54063
54427
|
if (await isNonEmptyFile(dst)) {
|
|
54064
54428
|
continue;
|
|
54065
54429
|
}
|
|
@@ -54122,13 +54486,13 @@ import { existsSync as existsSync16 } from "node:fs";
|
|
|
54122
54486
|
|
|
54123
54487
|
// ../server/src/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.ts
|
|
54124
54488
|
import { createRequire as createRequire4 } from "node:module";
|
|
54125
|
-
import
|
|
54489
|
+
import path27 from "node:path";
|
|
54126
54490
|
import { existsSync as existsSync15 } from "node:fs";
|
|
54127
54491
|
import { spawnSync } from "node:child_process";
|
|
54128
54492
|
|
|
54129
54493
|
// ../server/src/server/speech/providers/local/sherpa/sherpa-runtime-env.ts
|
|
54130
54494
|
import { createRequire as createRequire3 } from "node:module";
|
|
54131
|
-
import
|
|
54495
|
+
import path26 from "node:path";
|
|
54132
54496
|
function sherpaPlatformArch(platform = process.platform, arch = process.arch) {
|
|
54133
54497
|
const normalizedPlatform = platform === "win32" ? "win" : platform;
|
|
54134
54498
|
return `${normalizedPlatform}-${arch}`;
|
|
@@ -54149,11 +54513,11 @@ function sherpaLoaderEnvKey(platform = process.platform) {
|
|
|
54149
54513
|
return null;
|
|
54150
54514
|
}
|
|
54151
54515
|
function prependEnvPath(existing, value) {
|
|
54152
|
-
const parts = (existing ?? "").split(
|
|
54516
|
+
const parts = (existing ?? "").split(path26.delimiter).filter(Boolean);
|
|
54153
54517
|
if (parts.includes(value)) {
|
|
54154
|
-
return parts.join(
|
|
54518
|
+
return parts.join(path26.delimiter);
|
|
54155
54519
|
}
|
|
54156
|
-
return [value, ...parts].join(
|
|
54520
|
+
return [value, ...parts].join(path26.delimiter);
|
|
54157
54521
|
}
|
|
54158
54522
|
function resolveSherpaLoaderEnv(platform = process.platform, arch = process.arch) {
|
|
54159
54523
|
const key = sherpaLoaderEnvKey(platform);
|
|
@@ -54166,7 +54530,7 @@ function resolveSherpaLoaderEnv(platform = process.platform, arch = process.arch
|
|
|
54166
54530
|
const pkgJson = require4.resolve(`${packageName}/package.json`);
|
|
54167
54531
|
return {
|
|
54168
54532
|
key,
|
|
54169
|
-
libDir:
|
|
54533
|
+
libDir: path26.dirname(pkgJson),
|
|
54170
54534
|
packageName
|
|
54171
54535
|
};
|
|
54172
54536
|
} catch {
|
|
@@ -54275,7 +54639,7 @@ function loadSherpaOnnxNode() {
|
|
|
54275
54639
|
const platformPkgDir = resolvedEnv?.libDir ?? null;
|
|
54276
54640
|
if (platformPkgDir) {
|
|
54277
54641
|
applySherpaLoaderEnv(process.env);
|
|
54278
|
-
const addonPath =
|
|
54642
|
+
const addonPath = path27.join(platformPkgDir, "sherpa-onnx.node");
|
|
54279
54643
|
if (existsSync15(addonPath)) {
|
|
54280
54644
|
const byPath = loadWithRequire(require4, addonPath, attempts);
|
|
54281
54645
|
if (byPath) {
|
|
@@ -55047,7 +55411,7 @@ var SherpaOnnxTTS = class {
|
|
|
55047
55411
|
|
|
55048
55412
|
// ../server/src/server/speech/providers/local/sherpa/silero-vad-provider.ts
|
|
55049
55413
|
import { copyFile, mkdir as mkdir7, stat as stat7 } from "node:fs/promises";
|
|
55050
|
-
import
|
|
55414
|
+
import path28 from "node:path";
|
|
55051
55415
|
|
|
55052
55416
|
// ../server/src/server/speech/providers/local/sherpa/silero-vad-session.ts
|
|
55053
55417
|
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
@@ -55239,8 +55603,8 @@ var SherpaSileroVadSession = class extends EventEmitter6 {
|
|
|
55239
55603
|
var SILERO_VAD_DIR = "silero-vad";
|
|
55240
55604
|
var SILERO_VAD_FILE = "silero_vad.onnx";
|
|
55241
55605
|
async function ensureSileroVadModel(modelsDir, logger) {
|
|
55242
|
-
const destDir =
|
|
55243
|
-
const destPath =
|
|
55606
|
+
const destDir = path28.join(modelsDir, SILERO_VAD_DIR);
|
|
55607
|
+
const destPath = path28.join(destDir, SILERO_VAD_FILE);
|
|
55244
55608
|
try {
|
|
55245
55609
|
const s = await stat7(destPath);
|
|
55246
55610
|
if (s.isFile() && s.size > 0) return destPath;
|
|
@@ -55533,20 +55897,20 @@ async function initializeLocalSpeechServices(params) {
|
|
|
55533
55897
|
}
|
|
55534
55898
|
|
|
55535
55899
|
// ../server/src/server/speech/providers/openai/config.ts
|
|
55536
|
-
import { z as
|
|
55900
|
+
import { z as z42 } from "zod";
|
|
55537
55901
|
var DEFAULT_OPENAI_REALTIME_TRANSCRIPTION_MODEL = "gpt-4o-transcribe";
|
|
55538
55902
|
var DEFAULT_OPENAI_TTS_MODEL = "tts-1";
|
|
55539
|
-
var OpenAiTtsVoiceSchema =
|
|
55540
|
-
var OpenAiTtsModelSchema =
|
|
55541
|
-
var NumberLikeSchema =
|
|
55542
|
-
var OptionalFiniteNumberSchema = NumberLikeSchema.pipe(
|
|
55543
|
-
var OptionalTrimmedStringSchema =
|
|
55544
|
-
var OpenAiSpeechResolutionSchema =
|
|
55903
|
+
var OpenAiTtsVoiceSchema = z42.enum(["alloy", "echo", "fable", "onyx", "nova", "shimmer"]);
|
|
55904
|
+
var OpenAiTtsModelSchema = z42.enum(["tts-1", "tts-1-hd"]);
|
|
55905
|
+
var NumberLikeSchema = z42.union([z42.number(), z42.string().trim().min(1)]);
|
|
55906
|
+
var OptionalFiniteNumberSchema = NumberLikeSchema.pipe(z42.coerce.number().finite()).optional();
|
|
55907
|
+
var OptionalTrimmedStringSchema = z42.string().trim().optional().transform((value) => value && value.length > 0 ? value : void 0);
|
|
55908
|
+
var OpenAiSpeechResolutionSchema = z42.object({
|
|
55545
55909
|
apiKey: OptionalTrimmedStringSchema,
|
|
55546
55910
|
sttConfidenceThreshold: OptionalFiniteNumberSchema,
|
|
55547
55911
|
sttModel: OptionalTrimmedStringSchema,
|
|
55548
|
-
ttsVoice:
|
|
55549
|
-
ttsModel:
|
|
55912
|
+
ttsVoice: z42.string().trim().toLowerCase().pipe(OpenAiTtsVoiceSchema).default("alloy"),
|
|
55913
|
+
ttsModel: z42.string().trim().toLowerCase().pipe(OpenAiTtsModelSchema).default(DEFAULT_OPENAI_TTS_MODEL),
|
|
55550
55914
|
realtimeTranscriptionModel: OptionalTrimmedStringSchema.default(
|
|
55551
55915
|
DEFAULT_OPENAI_REALTIME_TRANSCRIPTION_MODEL
|
|
55552
55916
|
)
|
|
@@ -55894,7 +56258,7 @@ var OpenAISTT = class {
|
|
|
55894
56258
|
const supportsLogprobs = modelToUse === "gpt-4o-transcribe" || modelToUse === "gpt-4o-mini-transcribe";
|
|
55895
56259
|
const includeLogprobs = ["logprobs"];
|
|
55896
56260
|
const response = await this.openaiClient.audio.transcriptions.create({
|
|
55897
|
-
file: await import("fs").then((
|
|
56261
|
+
file: await import("fs").then((fs20) => fs20.createReadStream(tempFilePath)),
|
|
55898
56262
|
language,
|
|
55899
56263
|
model: modelToUse,
|
|
55900
56264
|
...supportsLogprobs ? { include: includeLogprobs } : {},
|
|
@@ -56658,72 +57022,72 @@ function createSpeechService(params) {
|
|
|
56658
57022
|
|
|
56659
57023
|
// ../server/src/server/agent/agent-storage.ts
|
|
56660
57024
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
56661
|
-
import { promises as
|
|
56662
|
-
import
|
|
56663
|
-
import { z as
|
|
56664
|
-
var SERIALIZABLE_CONFIG_SCHEMA =
|
|
56665
|
-
title:
|
|
56666
|
-
modeId:
|
|
56667
|
-
model:
|
|
56668
|
-
thinkingOptionId:
|
|
56669
|
-
featureValues:
|
|
56670
|
-
extra:
|
|
56671
|
-
systemPrompt:
|
|
56672
|
-
mcpServers:
|
|
57025
|
+
import { promises as fs17 } from "node:fs";
|
|
57026
|
+
import path29 from "node:path";
|
|
57027
|
+
import { z as z43 } from "zod";
|
|
57028
|
+
var SERIALIZABLE_CONFIG_SCHEMA = z43.object({
|
|
57029
|
+
title: z43.string().nullable().optional(),
|
|
57030
|
+
modeId: z43.string().nullable().optional(),
|
|
57031
|
+
model: z43.string().nullable().optional(),
|
|
57032
|
+
thinkingOptionId: z43.string().nullable().optional(),
|
|
57033
|
+
featureValues: z43.record(z43.unknown()).nullable().optional(),
|
|
57034
|
+
extra: z43.record(z43.any()).nullable().optional(),
|
|
57035
|
+
systemPrompt: z43.string().nullable().optional(),
|
|
57036
|
+
mcpServers: z43.record(z43.any()).nullable().optional()
|
|
56673
57037
|
}).nullable().optional();
|
|
56674
|
-
var PERSISTENCE_HANDLE_SCHEMA =
|
|
56675
|
-
provider:
|
|
56676
|
-
sessionId:
|
|
56677
|
-
nativeHandle:
|
|
56678
|
-
metadata:
|
|
57038
|
+
var PERSISTENCE_HANDLE_SCHEMA = z43.object({
|
|
57039
|
+
provider: z43.string(),
|
|
57040
|
+
sessionId: z43.string(),
|
|
57041
|
+
nativeHandle: z43.any().optional(),
|
|
57042
|
+
metadata: z43.record(z43.any()).optional()
|
|
56679
57043
|
}).nullable().optional();
|
|
56680
|
-
var STORED_AGENT_SCHEMA =
|
|
56681
|
-
id:
|
|
56682
|
-
provider:
|
|
56683
|
-
cwd:
|
|
56684
|
-
createdAt:
|
|
56685
|
-
updatedAt:
|
|
56686
|
-
lastActivityAt:
|
|
56687
|
-
lastUserMessageAt:
|
|
56688
|
-
title:
|
|
56689
|
-
labels:
|
|
57044
|
+
var STORED_AGENT_SCHEMA = z43.object({
|
|
57045
|
+
id: z43.string(),
|
|
57046
|
+
provider: z43.string(),
|
|
57047
|
+
cwd: z43.string(),
|
|
57048
|
+
createdAt: z43.string(),
|
|
57049
|
+
updatedAt: z43.string(),
|
|
57050
|
+
lastActivityAt: z43.string().optional(),
|
|
57051
|
+
lastUserMessageAt: z43.string().nullable().optional(),
|
|
57052
|
+
title: z43.string().nullable().optional(),
|
|
57053
|
+
labels: z43.record(z43.string()).default({}),
|
|
56690
57054
|
lastStatus: AgentStatusSchema.default("closed"),
|
|
56691
|
-
lastModeId:
|
|
57055
|
+
lastModeId: z43.string().nullable().optional(),
|
|
56692
57056
|
config: SERIALIZABLE_CONFIG_SCHEMA,
|
|
56693
|
-
runtimeInfo:
|
|
56694
|
-
provider:
|
|
56695
|
-
sessionId:
|
|
56696
|
-
model:
|
|
56697
|
-
thinkingOptionId:
|
|
56698
|
-
modeId:
|
|
56699
|
-
extra:
|
|
57057
|
+
runtimeInfo: z43.object({
|
|
57058
|
+
provider: z43.string(),
|
|
57059
|
+
sessionId: z43.string().nullable(),
|
|
57060
|
+
model: z43.string().nullable().optional(),
|
|
57061
|
+
thinkingOptionId: z43.string().nullable().optional(),
|
|
57062
|
+
modeId: z43.string().nullable().optional(),
|
|
57063
|
+
extra: z43.record(z43.unknown()).optional()
|
|
56700
57064
|
}).optional(),
|
|
56701
|
-
features:
|
|
57065
|
+
features: z43.array(AgentFeatureSchema).optional(),
|
|
56702
57066
|
persistence: PERSISTENCE_HANDLE_SCHEMA,
|
|
56703
|
-
lastError:
|
|
56704
|
-
requiresAttention:
|
|
56705
|
-
attentionReason:
|
|
56706
|
-
attentionTimestamp:
|
|
56707
|
-
internal:
|
|
56708
|
-
archivedAt:
|
|
57067
|
+
lastError: z43.string().nullable().optional(),
|
|
57068
|
+
requiresAttention: z43.boolean().optional(),
|
|
57069
|
+
attentionReason: z43.enum(["finished", "error", "permission"]).nullable().optional(),
|
|
57070
|
+
attentionTimestamp: z43.string().nullable().optional(),
|
|
57071
|
+
internal: z43.boolean().optional(),
|
|
57072
|
+
archivedAt: z43.string().nullable().optional(),
|
|
56709
57073
|
// Fork lineage (optional for backward compat with pre-fork records).
|
|
56710
|
-
parentAgentId:
|
|
56711
|
-
forkedFromMessageUuid:
|
|
57074
|
+
parentAgentId: z43.string().optional(),
|
|
57075
|
+
forkedFromMessageUuid: z43.string().optional(),
|
|
56712
57076
|
// Multi-tenant session isolation: the auth-server user-id that created
|
|
56713
57077
|
// (and therefore owns) this agent + the additive ACL of other users
|
|
56714
57078
|
// granted access. Both optional/null-default for backward compatibility
|
|
56715
57079
|
// with pre-Phase-2c records — those load with `null` owner and stay
|
|
56716
57080
|
// visible to every connecting user (matches today's single-tenant
|
|
56717
57081
|
// behavior). Set on new agents at create time via Session.ownerUserId.
|
|
56718
|
-
ownerUserId:
|
|
56719
|
-
sharedWithUserIds:
|
|
57082
|
+
ownerUserId: z43.string().nullable().optional(),
|
|
57083
|
+
sharedWithUserIds: z43.array(z43.string()).default([]),
|
|
56720
57084
|
// Owner's display username — needed on resume to route to the correct
|
|
56721
57085
|
// `CLAUDE_CONFIG_DIR` via `ensureClaudeProfile(username)` for agents
|
|
56722
57086
|
// created by a shared (non-owner) user. Without this, a resumed shared-
|
|
56723
57087
|
// user agent would silently fall back to the daemon owner's Claude
|
|
56724
57088
|
// profile/subscription. Optional — pre-Phase-3 records rehydrate without
|
|
56725
57089
|
// per-user profile routing.
|
|
56726
|
-
ownerUsername:
|
|
57090
|
+
ownerUsername: z43.string().optional()
|
|
56727
57091
|
});
|
|
56728
57092
|
function parseStoredAgentRecord(value) {
|
|
56729
57093
|
return STORED_AGENT_SCHEMA.parse(value);
|
|
@@ -56761,12 +57125,12 @@ var AgentStorage = class {
|
|
|
56761
57125
|
}
|
|
56762
57126
|
const nextPath = this.buildRecordPath(record);
|
|
56763
57127
|
const previousPath = this.pathById.get(agentId);
|
|
56764
|
-
await
|
|
57128
|
+
await fs17.mkdir(path29.dirname(nextPath), { recursive: true });
|
|
56765
57129
|
await writeFileAtomically(nextPath, JSON.stringify(record, null, 2));
|
|
56766
57130
|
this.addIndexedPath(agentId, nextPath);
|
|
56767
57131
|
if (previousPath && previousPath !== nextPath) {
|
|
56768
57132
|
try {
|
|
56769
|
-
await
|
|
57133
|
+
await fs17.unlink(previousPath);
|
|
56770
57134
|
} catch {
|
|
56771
57135
|
}
|
|
56772
57136
|
this.removeIndexedPath(agentId, previousPath);
|
|
@@ -56794,7 +57158,7 @@ var AgentStorage = class {
|
|
|
56794
57158
|
const paths = Array.from(this.pathsById.get(agentId) ?? []);
|
|
56795
57159
|
for (const filePath of paths) {
|
|
56796
57160
|
try {
|
|
56797
|
-
await
|
|
57161
|
+
await fs17.unlink(filePath);
|
|
56798
57162
|
} catch (error) {
|
|
56799
57163
|
const code = error.code;
|
|
56800
57164
|
if (code && code !== "ENOENT") {
|
|
@@ -56868,7 +57232,7 @@ var AgentStorage = class {
|
|
|
56868
57232
|
const records = [];
|
|
56869
57233
|
let entries = [];
|
|
56870
57234
|
try {
|
|
56871
|
-
entries = await
|
|
57235
|
+
entries = await fs17.readdir(this.baseDir, { withFileTypes: true });
|
|
56872
57236
|
} catch (error) {
|
|
56873
57237
|
if (error.code === "ENOENT") {
|
|
56874
57238
|
return [];
|
|
@@ -56877,7 +57241,7 @@ var AgentStorage = class {
|
|
|
56877
57241
|
}
|
|
56878
57242
|
for (const entry of entries) {
|
|
56879
57243
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
56880
|
-
const rootPath =
|
|
57244
|
+
const rootPath = path29.join(this.baseDir, entry.name);
|
|
56881
57245
|
const rootRecord = await this.readRecordFile(rootPath);
|
|
56882
57246
|
if (!rootRecord) {
|
|
56883
57247
|
continue;
|
|
@@ -56891,10 +57255,10 @@ var AgentStorage = class {
|
|
|
56891
57255
|
if (!entry.isDirectory()) {
|
|
56892
57256
|
continue;
|
|
56893
57257
|
}
|
|
56894
|
-
const projectDir =
|
|
57258
|
+
const projectDir = path29.join(this.baseDir, entry.name);
|
|
56895
57259
|
let files = [];
|
|
56896
57260
|
try {
|
|
56897
|
-
files = await
|
|
57261
|
+
files = await fs17.readdir(projectDir, { withFileTypes: true });
|
|
56898
57262
|
} catch {
|
|
56899
57263
|
continue;
|
|
56900
57264
|
}
|
|
@@ -56902,7 +57266,7 @@ var AgentStorage = class {
|
|
|
56902
57266
|
if (!file.isFile() || !file.name.endsWith(".json")) {
|
|
56903
57267
|
continue;
|
|
56904
57268
|
}
|
|
56905
|
-
const filePath =
|
|
57269
|
+
const filePath = path29.join(projectDir, file.name);
|
|
56906
57270
|
const record = await this.readRecordFile(filePath);
|
|
56907
57271
|
if (!record) {
|
|
56908
57272
|
continue;
|
|
@@ -56917,7 +57281,7 @@ var AgentStorage = class {
|
|
|
56917
57281
|
}
|
|
56918
57282
|
async readRecordFile(filePath) {
|
|
56919
57283
|
try {
|
|
56920
|
-
const content = await
|
|
57284
|
+
const content = await fs17.readFile(filePath, "utf8");
|
|
56921
57285
|
const parsed = JSON.parse(content);
|
|
56922
57286
|
return parseStoredAgentRecord(parsed);
|
|
56923
57287
|
} catch (error) {
|
|
@@ -56927,7 +57291,7 @@ var AgentStorage = class {
|
|
|
56927
57291
|
}
|
|
56928
57292
|
buildRecordPath(record) {
|
|
56929
57293
|
const projectDir = projectDirNameFromCwd(record.cwd);
|
|
56930
|
-
return
|
|
57294
|
+
return path29.join(this.baseDir, projectDir, `${record.id}.json`);
|
|
56931
57295
|
}
|
|
56932
57296
|
addIndexedPath(agentId, filePath) {
|
|
56933
57297
|
const paths = this.pathsById.get(agentId) ?? /* @__PURE__ */ new Set();
|
|
@@ -56949,7 +57313,7 @@ var AgentStorage = class {
|
|
|
56949
57313
|
}
|
|
56950
57314
|
};
|
|
56951
57315
|
function projectDirNameFromCwd(cwd) {
|
|
56952
|
-
const { root } =
|
|
57316
|
+
const { root } = path29.win32.parse(cwd);
|
|
56953
57317
|
const withoutRoot = cwd.slice(root.length).replace(/[\\/]+$/, "");
|
|
56954
57318
|
const sanitizedRoot = root.replace(/[:\\/]+/g, "-").replace(/^-+|-+$/g, "");
|
|
56955
57319
|
const prefix = sanitizedRoot ? sanitizedRoot + "-" : "";
|
|
@@ -56959,15 +57323,15 @@ function projectDirNameFromCwd(cwd) {
|
|
|
56959
57323
|
return prefix + withoutRoot.replace(/[\\/]+/g, "-");
|
|
56960
57324
|
}
|
|
56961
57325
|
async function writeFileAtomically(targetPath, payload) {
|
|
56962
|
-
const directory =
|
|
56963
|
-
const tempPath =
|
|
56964
|
-
await
|
|
56965
|
-
await
|
|
57326
|
+
const directory = path29.dirname(targetPath);
|
|
57327
|
+
const tempPath = path29.join(directory, `.agent.tmp-${process.pid}-${Date.now()}-${randomUUID12()}`);
|
|
57328
|
+
await fs17.writeFile(tempPath, payload, "utf8");
|
|
57329
|
+
await fs17.rename(tempPath, targetPath);
|
|
56966
57330
|
}
|
|
56967
57331
|
|
|
56968
57332
|
// ../server/src/server/agent/mcp-server.ts
|
|
56969
57333
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
56970
|
-
import { z as
|
|
57334
|
+
import { z as z44 } from "zod";
|
|
56971
57335
|
|
|
56972
57336
|
// ../server/src/server/json-utils.ts
|
|
56973
57337
|
function ensureValidJson(value) {
|
|
@@ -57100,16 +57464,16 @@ function resolveChildAgentCwd(params) {
|
|
|
57100
57464
|
}
|
|
57101
57465
|
return resolvePathFromBase(params.parentCwd, requestedCwd);
|
|
57102
57466
|
}
|
|
57103
|
-
var TerminalSummarySchema =
|
|
57104
|
-
id:
|
|
57105
|
-
name:
|
|
57106
|
-
cwd:
|
|
57467
|
+
var TerminalSummarySchema = z44.object({
|
|
57468
|
+
id: z44.string(),
|
|
57469
|
+
name: z44.string(),
|
|
57470
|
+
cwd: z44.string()
|
|
57107
57471
|
});
|
|
57108
|
-
var WorktreeSummarySchema =
|
|
57109
|
-
path:
|
|
57110
|
-
createdAt:
|
|
57111
|
-
branchName:
|
|
57112
|
-
head:
|
|
57472
|
+
var WorktreeSummarySchema = z44.object({
|
|
57473
|
+
path: z44.string(),
|
|
57474
|
+
createdAt: z44.string(),
|
|
57475
|
+
branchName: z44.string().optional(),
|
|
57476
|
+
head: z44.string().optional()
|
|
57113
57477
|
});
|
|
57114
57478
|
function resolveTerminalKeyToken(key, literal) {
|
|
57115
57479
|
if (literal) {
|
|
@@ -57246,48 +57610,48 @@ async function createAgentMcpServer(options) {
|
|
|
57246
57610
|
};
|
|
57247
57611
|
};
|
|
57248
57612
|
const agentToAgentInputSchema = {
|
|
57249
|
-
cwd:
|
|
57250
|
-
title:
|
|
57613
|
+
cwd: z44.string().optional().describe("Optional working directory. Defaults to the caller agent working directory."),
|
|
57614
|
+
title: z44.string().trim().min(1, "Title is required").max(60, "Title must be 60 characters or fewer").describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
|
|
57251
57615
|
provider: AgentProviderEnum.optional().describe(
|
|
57252
57616
|
"Optional agent implementation to spawn. Defaults to 'claude'."
|
|
57253
57617
|
),
|
|
57254
|
-
model:
|
|
57255
|
-
thinking:
|
|
57256
|
-
labels:
|
|
57257
|
-
initialPrompt:
|
|
57258
|
-
background:
|
|
57618
|
+
model: z44.string().optional().describe("Model to use (e.g. claude-sonnet-4-20250514)"),
|
|
57619
|
+
thinking: z44.string().optional().describe("Thinking option ID"),
|
|
57620
|
+
labels: z44.record(z44.string(), z44.string()).optional().describe("Labels to set on the agent"),
|
|
57621
|
+
initialPrompt: z44.string().trim().min(1, "initialPrompt is required").describe("Required first task to run immediately after creation."),
|
|
57622
|
+
background: z44.boolean().optional().default(false).describe(
|
|
57259
57623
|
"Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."
|
|
57260
57624
|
),
|
|
57261
|
-
notifyOnFinish:
|
|
57625
|
+
notifyOnFinish: z44.boolean().optional().default(false).describe(
|
|
57262
57626
|
"Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission. Requires a caller agent context."
|
|
57263
57627
|
)
|
|
57264
57628
|
};
|
|
57265
57629
|
const topLevelInputSchema = {
|
|
57266
|
-
cwd:
|
|
57267
|
-
title:
|
|
57630
|
+
cwd: z44.string().describe("Required working directory for the agent (absolute, relative, or ~)."),
|
|
57631
|
+
title: z44.string().trim().min(1, "Title is required").max(60, "Title must be 60 characters or fewer").describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
|
|
57268
57632
|
provider: AgentProviderEnum.optional().describe(
|
|
57269
57633
|
"Optional agent implementation to spawn. Defaults to 'claude'."
|
|
57270
57634
|
),
|
|
57271
|
-
model:
|
|
57272
|
-
thinking:
|
|
57273
|
-
labels:
|
|
57274
|
-
initialPrompt:
|
|
57275
|
-
mode:
|
|
57276
|
-
worktreeName:
|
|
57277
|
-
baseBranch:
|
|
57278
|
-
refName:
|
|
57279
|
-
action:
|
|
57280
|
-
githubPrNumber:
|
|
57281
|
-
background:
|
|
57635
|
+
model: z44.string().optional().describe("Model to use (e.g. claude-sonnet-4-20250514)"),
|
|
57636
|
+
thinking: z44.string().optional().describe("Thinking option ID"),
|
|
57637
|
+
labels: z44.record(z44.string(), z44.string()).optional().describe("Labels to set on the agent"),
|
|
57638
|
+
initialPrompt: z44.string().trim().min(1, "initialPrompt is required").describe("Required first task to run immediately after creation."),
|
|
57639
|
+
mode: z44.string().optional().describe("Optional session mode to configure before the first run."),
|
|
57640
|
+
worktreeName: z44.string().optional().describe("Optional git worktree branch name (lowercase alphanumerics + hyphen)."),
|
|
57641
|
+
baseBranch: z44.string().optional().describe("Required when worktreeName is set: the base branch to diff/merge against."),
|
|
57642
|
+
refName: z44.string().min(1).optional().describe("Optional source ref for worktree creation."),
|
|
57643
|
+
action: z44.enum(["branch-off", "checkout"]).optional().describe("Optional worktree creation action."),
|
|
57644
|
+
githubPrNumber: z44.number().int().positive().optional().describe("Optional GitHub pull request number to checkout."),
|
|
57645
|
+
background: z44.boolean().optional().default(false).describe(
|
|
57282
57646
|
"Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."
|
|
57283
57647
|
),
|
|
57284
|
-
notifyOnFinish:
|
|
57648
|
+
notifyOnFinish: z44.boolean().optional().default(false).describe(
|
|
57285
57649
|
"Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission. Requires a caller agent context."
|
|
57286
57650
|
)
|
|
57287
57651
|
};
|
|
57288
57652
|
const createAgentInputSchema = callerAgentId ? agentToAgentInputSchema : topLevelInputSchema;
|
|
57289
|
-
const agentToAgentCreateAgentArgsSchema =
|
|
57290
|
-
const topLevelCreateAgentArgsSchema =
|
|
57653
|
+
const agentToAgentCreateAgentArgsSchema = z44.object(agentToAgentInputSchema);
|
|
57654
|
+
const topLevelCreateAgentArgsSchema = z44.object(topLevelInputSchema);
|
|
57291
57655
|
if (options.voiceOnly || options.enableVoiceTools || callerContext?.enableVoiceTools) {
|
|
57292
57656
|
server.registerTool(
|
|
57293
57657
|
"speak",
|
|
@@ -57295,10 +57659,10 @@ async function createAgentMcpServer(options) {
|
|
|
57295
57659
|
title: "Speak",
|
|
57296
57660
|
description: "Speak text to the user via daemon-managed voice output. Blocks until playback completes.",
|
|
57297
57661
|
inputSchema: {
|
|
57298
|
-
text:
|
|
57662
|
+
text: z44.string().trim().min(1, "text is required").max(4e3, "text must be 4000 characters or fewer")
|
|
57299
57663
|
},
|
|
57300
57664
|
outputSchema: {
|
|
57301
|
-
ok:
|
|
57665
|
+
ok: z44.boolean()
|
|
57302
57666
|
}
|
|
57303
57667
|
},
|
|
57304
57668
|
async (args, context) => {
|
|
@@ -57331,19 +57695,19 @@ async function createAgentMcpServer(options) {
|
|
|
57331
57695
|
description: "Create a new Claude or Codex agent tied to a working directory. Optionally run an initial prompt immediately or create a git worktree for the agent.",
|
|
57332
57696
|
inputSchema: createAgentInputSchema,
|
|
57333
57697
|
outputSchema: {
|
|
57334
|
-
agentId:
|
|
57698
|
+
agentId: z44.string(),
|
|
57335
57699
|
type: AgentProviderEnum,
|
|
57336
57700
|
status: AgentStatusEnum,
|
|
57337
|
-
cwd:
|
|
57338
|
-
currentModeId:
|
|
57339
|
-
availableModes:
|
|
57340
|
-
|
|
57341
|
-
id:
|
|
57342
|
-
label:
|
|
57343
|
-
description:
|
|
57701
|
+
cwd: z44.string(),
|
|
57702
|
+
currentModeId: z44.string().nullable(),
|
|
57703
|
+
availableModes: z44.array(
|
|
57704
|
+
z44.object({
|
|
57705
|
+
id: z44.string(),
|
|
57706
|
+
label: z44.string(),
|
|
57707
|
+
description: z44.string().nullable().optional()
|
|
57344
57708
|
})
|
|
57345
57709
|
),
|
|
57346
|
-
lastMessage:
|
|
57710
|
+
lastMessage: z44.string().nullable().optional(),
|
|
57347
57711
|
permission: AgentPermissionRequestPayloadSchema.nullable().optional()
|
|
57348
57712
|
}
|
|
57349
57713
|
},
|
|
@@ -57530,13 +57894,13 @@ async function createAgentMcpServer(options) {
|
|
|
57530
57894
|
title: "Wait for agent",
|
|
57531
57895
|
description: "Block until the agent requests permission or the current run completes. Returns the pending permission (if any) and recent activity summary.",
|
|
57532
57896
|
inputSchema: {
|
|
57533
|
-
agentId:
|
|
57897
|
+
agentId: z44.string().describe("Agent identifier returned by the create_agent tool")
|
|
57534
57898
|
},
|
|
57535
57899
|
outputSchema: {
|
|
57536
|
-
agentId:
|
|
57900
|
+
agentId: z44.string(),
|
|
57537
57901
|
status: AgentStatusEnum,
|
|
57538
57902
|
permission: AgentPermissionRequestPayloadSchema.nullable(),
|
|
57539
|
-
lastMessage:
|
|
57903
|
+
lastMessage: z44.string().nullable()
|
|
57540
57904
|
}
|
|
57541
57905
|
},
|
|
57542
57906
|
async ({ agentId }, { signal }) => {
|
|
@@ -57597,20 +57961,20 @@ async function createAgentMcpServer(options) {
|
|
|
57597
57961
|
title: "Send agent prompt",
|
|
57598
57962
|
description: "Send a task to a running agent. Returns immediately after the agent begins processing.",
|
|
57599
57963
|
inputSchema: {
|
|
57600
|
-
agentId:
|
|
57601
|
-
prompt:
|
|
57602
|
-
sessionMode:
|
|
57603
|
-
background:
|
|
57964
|
+
agentId: z44.string(),
|
|
57965
|
+
prompt: z44.string(),
|
|
57966
|
+
sessionMode: z44.string().optional().describe("Optional mode to set before running the prompt."),
|
|
57967
|
+
background: z44.boolean().optional().default(false).describe(
|
|
57604
57968
|
"Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."
|
|
57605
57969
|
),
|
|
57606
|
-
notifyOnFinish:
|
|
57970
|
+
notifyOnFinish: z44.boolean().optional().default(false).describe(
|
|
57607
57971
|
"Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission."
|
|
57608
57972
|
)
|
|
57609
57973
|
},
|
|
57610
57974
|
outputSchema: {
|
|
57611
|
-
success:
|
|
57975
|
+
success: z44.boolean(),
|
|
57612
57976
|
status: AgentStatusEnum,
|
|
57613
|
-
lastMessage:
|
|
57977
|
+
lastMessage: z44.string().nullable().optional(),
|
|
57614
57978
|
permission: AgentPermissionRequestPayloadSchema.nullable().optional()
|
|
57615
57979
|
}
|
|
57616
57980
|
},
|
|
@@ -57674,7 +58038,7 @@ async function createAgentMcpServer(options) {
|
|
|
57674
58038
|
title: "Get agent status",
|
|
57675
58039
|
description: "Return the latest snapshot for an agent, including lifecycle state, capabilities, and pending permissions.",
|
|
57676
58040
|
inputSchema: {
|
|
57677
|
-
agentId:
|
|
58041
|
+
agentId: z44.string()
|
|
57678
58042
|
},
|
|
57679
58043
|
outputSchema: {
|
|
57680
58044
|
status: AgentStatusEnum,
|
|
@@ -57721,10 +58085,10 @@ async function createAgentMcpServer(options) {
|
|
|
57721
58085
|
title: "List agents",
|
|
57722
58086
|
description: "List all live agents managed by the server.",
|
|
57723
58087
|
inputSchema: {
|
|
57724
|
-
includeArchived:
|
|
58088
|
+
includeArchived: z44.boolean().optional().default(false)
|
|
57725
58089
|
},
|
|
57726
58090
|
outputSchema: {
|
|
57727
|
-
agents:
|
|
58091
|
+
agents: z44.array(AgentSnapshotPayloadSchema)
|
|
57728
58092
|
}
|
|
57729
58093
|
},
|
|
57730
58094
|
async ({ includeArchived }) => {
|
|
@@ -57749,10 +58113,10 @@ async function createAgentMcpServer(options) {
|
|
|
57749
58113
|
title: "Cancel agent run",
|
|
57750
58114
|
description: "Abort the agent's current run but keep the agent alive for future tasks.",
|
|
57751
58115
|
inputSchema: {
|
|
57752
|
-
agentId:
|
|
58116
|
+
agentId: z44.string()
|
|
57753
58117
|
},
|
|
57754
58118
|
outputSchema: {
|
|
57755
|
-
success:
|
|
58119
|
+
success: z44.boolean()
|
|
57756
58120
|
}
|
|
57757
58121
|
},
|
|
57758
58122
|
async ({ agentId }) => {
|
|
@@ -57772,10 +58136,10 @@ async function createAgentMcpServer(options) {
|
|
|
57772
58136
|
title: "Archive agent",
|
|
57773
58137
|
description: "Archive an agent (soft-delete). The agent is interrupted if running and removed from the active list.",
|
|
57774
58138
|
inputSchema: {
|
|
57775
|
-
agentId:
|
|
58139
|
+
agentId: z44.string()
|
|
57776
58140
|
},
|
|
57777
58141
|
outputSchema: {
|
|
57778
|
-
success:
|
|
58142
|
+
success: z44.boolean()
|
|
57779
58143
|
}
|
|
57780
58144
|
},
|
|
57781
58145
|
async ({ agentId }) => {
|
|
@@ -57793,10 +58157,10 @@ async function createAgentMcpServer(options) {
|
|
|
57793
58157
|
title: "Kill agent",
|
|
57794
58158
|
description: "Terminate an agent session permanently.",
|
|
57795
58159
|
inputSchema: {
|
|
57796
|
-
agentId:
|
|
58160
|
+
agentId: z44.string()
|
|
57797
58161
|
},
|
|
57798
58162
|
outputSchema: {
|
|
57799
|
-
success:
|
|
58163
|
+
success: z44.boolean()
|
|
57800
58164
|
}
|
|
57801
58165
|
},
|
|
57802
58166
|
async ({ agentId }) => {
|
|
@@ -57814,12 +58178,12 @@ async function createAgentMcpServer(options) {
|
|
|
57814
58178
|
title: "Update agent",
|
|
57815
58179
|
description: "Update an agent name and/or labels.",
|
|
57816
58180
|
inputSchema: {
|
|
57817
|
-
agentId:
|
|
57818
|
-
name:
|
|
57819
|
-
labels:
|
|
58181
|
+
agentId: z44.string(),
|
|
58182
|
+
name: z44.string().optional(),
|
|
58183
|
+
labels: z44.record(z44.string(), z44.string()).optional().describe("Labels to set on the agent")
|
|
57820
58184
|
},
|
|
57821
58185
|
outputSchema: {
|
|
57822
|
-
success:
|
|
58186
|
+
success: z44.boolean()
|
|
57823
58187
|
}
|
|
57824
58188
|
},
|
|
57825
58189
|
async ({ agentId, name, labels }) => {
|
|
@@ -57851,11 +58215,11 @@ async function createAgentMcpServer(options) {
|
|
|
57851
58215
|
title: "List terminals",
|
|
57852
58216
|
description: "List terminals for a working directory or across all working directories.",
|
|
57853
58217
|
inputSchema: {
|
|
57854
|
-
cwd:
|
|
57855
|
-
all:
|
|
58218
|
+
cwd: z44.string().optional().describe("Optional working directory. Defaults to the caller agent cwd."),
|
|
58219
|
+
all: z44.boolean().optional().describe("List terminals across all working directories.")
|
|
57856
58220
|
},
|
|
57857
58221
|
outputSchema: {
|
|
57858
|
-
terminals:
|
|
58222
|
+
terminals: z44.array(TerminalSummarySchema)
|
|
57859
58223
|
}
|
|
57860
58224
|
},
|
|
57861
58225
|
async ({ cwd, all }) => {
|
|
@@ -57889,8 +58253,8 @@ async function createAgentMcpServer(options) {
|
|
|
57889
58253
|
title: "Create terminal",
|
|
57890
58254
|
description: "Create a terminal session for a working directory.",
|
|
57891
58255
|
inputSchema: {
|
|
57892
|
-
cwd:
|
|
57893
|
-
name:
|
|
58256
|
+
cwd: z44.string().optional().describe("Optional working directory. Defaults to the caller agent cwd."),
|
|
58257
|
+
name: z44.string().optional().describe("Optional terminal name.")
|
|
57894
58258
|
},
|
|
57895
58259
|
outputSchema: TerminalSummarySchema.shape
|
|
57896
58260
|
},
|
|
@@ -57918,10 +58282,10 @@ async function createAgentMcpServer(options) {
|
|
|
57918
58282
|
title: "Kill terminal",
|
|
57919
58283
|
description: "Kill an existing terminal session.",
|
|
57920
58284
|
inputSchema: {
|
|
57921
|
-
terminalId:
|
|
58285
|
+
terminalId: z44.string()
|
|
57922
58286
|
},
|
|
57923
58287
|
outputSchema: {
|
|
57924
|
-
success:
|
|
58288
|
+
success: z44.boolean()
|
|
57925
58289
|
}
|
|
57926
58290
|
},
|
|
57927
58291
|
async ({ terminalId }) => {
|
|
@@ -57945,16 +58309,16 @@ async function createAgentMcpServer(options) {
|
|
|
57945
58309
|
title: "Capture terminal",
|
|
57946
58310
|
description: "Capture plain-text terminal output lines from a terminal session.",
|
|
57947
58311
|
inputSchema: {
|
|
57948
|
-
terminalId:
|
|
57949
|
-
start:
|
|
57950
|
-
end:
|
|
57951
|
-
scrollback:
|
|
57952
|
-
stripAnsi:
|
|
58312
|
+
terminalId: z44.string(),
|
|
58313
|
+
start: z44.number().optional(),
|
|
58314
|
+
end: z44.number().optional(),
|
|
58315
|
+
scrollback: z44.boolean().optional(),
|
|
58316
|
+
stripAnsi: z44.boolean().optional().default(true)
|
|
57953
58317
|
},
|
|
57954
58318
|
outputSchema: {
|
|
57955
|
-
terminalId:
|
|
57956
|
-
lines:
|
|
57957
|
-
totalLines:
|
|
58319
|
+
terminalId: z44.string(),
|
|
58320
|
+
lines: z44.array(z44.string()),
|
|
58321
|
+
totalLines: z44.number().int().nonnegative()
|
|
57958
58322
|
}
|
|
57959
58323
|
},
|
|
57960
58324
|
async ({ terminalId, start, end, scrollback, stripAnsi: stripAnsi3 = true }) => {
|
|
@@ -57986,12 +58350,12 @@ async function createAgentMcpServer(options) {
|
|
|
57986
58350
|
title: "Send terminal keys",
|
|
57987
58351
|
description: "Send literal text or special key tokens to a terminal session.",
|
|
57988
58352
|
inputSchema: {
|
|
57989
|
-
terminalId:
|
|
57990
|
-
keys:
|
|
57991
|
-
literal:
|
|
58353
|
+
terminalId: z44.string(),
|
|
58354
|
+
keys: z44.string(),
|
|
58355
|
+
literal: z44.boolean().optional()
|
|
57992
58356
|
},
|
|
57993
58357
|
outputSchema: {
|
|
57994
|
-
success:
|
|
58358
|
+
success: z44.boolean()
|
|
57995
58359
|
}
|
|
57996
58360
|
},
|
|
57997
58361
|
async ({ terminalId, keys, literal = false }) => {
|
|
@@ -58018,17 +58382,17 @@ async function createAgentMcpServer(options) {
|
|
|
58018
58382
|
title: "Create schedule",
|
|
58019
58383
|
description: "Create a recurring schedule that runs on an agent or a new agent.",
|
|
58020
58384
|
inputSchema: {
|
|
58021
|
-
prompt:
|
|
58022
|
-
every:
|
|
58023
|
-
cron:
|
|
58024
|
-
name:
|
|
58025
|
-
target:
|
|
58385
|
+
prompt: z44.string().trim().min(1, "prompt is required"),
|
|
58386
|
+
every: z44.string().optional(),
|
|
58387
|
+
cron: z44.string().optional(),
|
|
58388
|
+
name: z44.string().optional(),
|
|
58389
|
+
target: z44.enum(["self", "new-agent"]).optional(),
|
|
58026
58390
|
provider: AgentProviderEnum.optional().describe(
|
|
58027
58391
|
"Provider, or provider/model (for example: codex or codex/gpt-5.4)."
|
|
58028
58392
|
),
|
|
58029
|
-
cwd:
|
|
58030
|
-
maxRuns:
|
|
58031
|
-
expiresIn:
|
|
58393
|
+
cwd: z44.string().optional(),
|
|
58394
|
+
maxRuns: z44.number().int().positive().optional(),
|
|
58395
|
+
expiresIn: z44.string().optional()
|
|
58032
58396
|
},
|
|
58033
58397
|
outputSchema: ScheduleSummarySchema.shape
|
|
58034
58398
|
},
|
|
@@ -58070,7 +58434,7 @@ async function createAgentMcpServer(options) {
|
|
|
58070
58434
|
description: "List all schedules managed by the daemon.",
|
|
58071
58435
|
inputSchema: {},
|
|
58072
58436
|
outputSchema: {
|
|
58073
|
-
schedules:
|
|
58437
|
+
schedules: z44.array(ScheduleSummarySchema)
|
|
58074
58438
|
}
|
|
58075
58439
|
},
|
|
58076
58440
|
async () => {
|
|
@@ -58092,7 +58456,7 @@ async function createAgentMcpServer(options) {
|
|
|
58092
58456
|
title: "Inspect schedule",
|
|
58093
58457
|
description: "Inspect a schedule and its run history.",
|
|
58094
58458
|
inputSchema: {
|
|
58095
|
-
id:
|
|
58459
|
+
id: z44.string()
|
|
58096
58460
|
},
|
|
58097
58461
|
outputSchema: StoredScheduleSchema.shape
|
|
58098
58462
|
},
|
|
@@ -58113,10 +58477,10 @@ async function createAgentMcpServer(options) {
|
|
|
58113
58477
|
title: "Pause schedule",
|
|
58114
58478
|
description: "Pause an active schedule.",
|
|
58115
58479
|
inputSchema: {
|
|
58116
|
-
id:
|
|
58480
|
+
id: z44.string()
|
|
58117
58481
|
},
|
|
58118
58482
|
outputSchema: {
|
|
58119
|
-
success:
|
|
58483
|
+
success: z44.boolean()
|
|
58120
58484
|
}
|
|
58121
58485
|
},
|
|
58122
58486
|
async ({ id }) => {
|
|
@@ -58136,10 +58500,10 @@ async function createAgentMcpServer(options) {
|
|
|
58136
58500
|
title: "Resume schedule",
|
|
58137
58501
|
description: "Resume a paused schedule.",
|
|
58138
58502
|
inputSchema: {
|
|
58139
|
-
id:
|
|
58503
|
+
id: z44.string()
|
|
58140
58504
|
},
|
|
58141
58505
|
outputSchema: {
|
|
58142
|
-
success:
|
|
58506
|
+
success: z44.boolean()
|
|
58143
58507
|
}
|
|
58144
58508
|
},
|
|
58145
58509
|
async ({ id }) => {
|
|
@@ -58159,10 +58523,10 @@ async function createAgentMcpServer(options) {
|
|
|
58159
58523
|
title: "Delete schedule",
|
|
58160
58524
|
description: "Delete a schedule permanently.",
|
|
58161
58525
|
inputSchema: {
|
|
58162
|
-
id:
|
|
58526
|
+
id: z44.string()
|
|
58163
58527
|
},
|
|
58164
58528
|
outputSchema: {
|
|
58165
|
-
success:
|
|
58529
|
+
success: z44.boolean()
|
|
58166
58530
|
}
|
|
58167
58531
|
},
|
|
58168
58532
|
async ({ id }) => {
|
|
@@ -58183,7 +58547,7 @@ async function createAgentMcpServer(options) {
|
|
|
58183
58547
|
description: "List available agent providers and their modes.",
|
|
58184
58548
|
inputSchema: {},
|
|
58185
58549
|
outputSchema: {
|
|
58186
|
-
providers:
|
|
58550
|
+
providers: z44.array(ProviderSummarySchema)
|
|
58187
58551
|
}
|
|
58188
58552
|
},
|
|
58189
58553
|
async () => ({
|
|
@@ -58210,8 +58574,8 @@ async function createAgentMcpServer(options) {
|
|
|
58210
58574
|
provider: AgentProviderEnum
|
|
58211
58575
|
},
|
|
58212
58576
|
outputSchema: {
|
|
58213
|
-
provider:
|
|
58214
|
-
models:
|
|
58577
|
+
provider: z44.string(),
|
|
58578
|
+
models: z44.array(AgentModelSchema)
|
|
58215
58579
|
}
|
|
58216
58580
|
},
|
|
58217
58581
|
async ({ provider }) => {
|
|
@@ -58238,10 +58602,10 @@ async function createAgentMcpServer(options) {
|
|
|
58238
58602
|
title: "List worktrees",
|
|
58239
58603
|
description: "List Appostle-managed git worktrees for a repository.",
|
|
58240
58604
|
inputSchema: {
|
|
58241
|
-
cwd:
|
|
58605
|
+
cwd: z44.string().optional().describe("Optional repository cwd. Defaults to the caller agent cwd.")
|
|
58242
58606
|
},
|
|
58243
58607
|
outputSchema: {
|
|
58244
|
-
worktrees:
|
|
58608
|
+
worktrees: z44.array(WorktreeSummarySchema)
|
|
58245
58609
|
}
|
|
58246
58610
|
},
|
|
58247
58611
|
async ({ cwd }) => {
|
|
@@ -58262,16 +58626,16 @@ async function createAgentMcpServer(options) {
|
|
|
58262
58626
|
title: "Create worktree",
|
|
58263
58627
|
description: "Create a Appostle-managed git worktree.",
|
|
58264
58628
|
inputSchema: {
|
|
58265
|
-
cwd:
|
|
58266
|
-
branchName:
|
|
58267
|
-
baseBranch:
|
|
58268
|
-
refName:
|
|
58269
|
-
action:
|
|
58270
|
-
githubPrNumber:
|
|
58629
|
+
cwd: z44.string().optional().describe("Optional repository cwd. Defaults to the caller agent cwd."),
|
|
58630
|
+
branchName: z44.string().optional(),
|
|
58631
|
+
baseBranch: z44.string().optional(),
|
|
58632
|
+
refName: z44.string().min(1).optional(),
|
|
58633
|
+
action: z44.enum(["branch-off", "checkout"]).optional(),
|
|
58634
|
+
githubPrNumber: z44.number().int().positive().optional()
|
|
58271
58635
|
},
|
|
58272
58636
|
outputSchema: {
|
|
58273
|
-
branchName:
|
|
58274
|
-
worktreePath:
|
|
58637
|
+
branchName: z44.string(),
|
|
58638
|
+
worktreePath: z44.string()
|
|
58275
58639
|
}
|
|
58276
58640
|
},
|
|
58277
58641
|
async ({ cwd, branchName, baseBranch, refName, action, githubPrNumber }) => {
|
|
@@ -58308,12 +58672,12 @@ async function createAgentMcpServer(options) {
|
|
|
58308
58672
|
title: "Archive worktree",
|
|
58309
58673
|
description: "Delete a Appostle-managed git worktree.",
|
|
58310
58674
|
inputSchema: {
|
|
58311
|
-
cwd:
|
|
58312
|
-
worktreePath:
|
|
58313
|
-
worktreeSlug:
|
|
58675
|
+
cwd: z44.string().optional().describe("Optional repository cwd. Defaults to the caller agent cwd."),
|
|
58676
|
+
worktreePath: z44.string().optional(),
|
|
58677
|
+
worktreeSlug: z44.string().optional()
|
|
58314
58678
|
},
|
|
58315
58679
|
outputSchema: {
|
|
58316
|
-
success:
|
|
58680
|
+
success: z44.boolean()
|
|
58317
58681
|
}
|
|
58318
58682
|
},
|
|
58319
58683
|
async ({ cwd, worktreePath, worktreeSlug }) => {
|
|
@@ -58335,14 +58699,14 @@ async function createAgentMcpServer(options) {
|
|
|
58335
58699
|
title: "Get agent activity",
|
|
58336
58700
|
description: "Return recent agent timeline entries as a curated summary.",
|
|
58337
58701
|
inputSchema: {
|
|
58338
|
-
agentId:
|
|
58339
|
-
limit:
|
|
58702
|
+
agentId: z44.string(),
|
|
58703
|
+
limit: z44.number().optional().describe("Optional limit for number of activities to include (most recent first).")
|
|
58340
58704
|
},
|
|
58341
58705
|
outputSchema: {
|
|
58342
|
-
agentId:
|
|
58343
|
-
updateCount:
|
|
58344
|
-
currentModeId:
|
|
58345
|
-
content:
|
|
58706
|
+
agentId: z44.string(),
|
|
58707
|
+
updateCount: z44.number(),
|
|
58708
|
+
currentModeId: z44.string().nullable(),
|
|
58709
|
+
content: z44.string()
|
|
58346
58710
|
}
|
|
58347
58711
|
},
|
|
58348
58712
|
async ({ agentId, limit }) => {
|
|
@@ -58383,12 +58747,12 @@ ${curatedContent}`;
|
|
|
58383
58747
|
title: "Set agent session mode",
|
|
58384
58748
|
description: "Switch the agent's session mode (plan, bypassPermissions, read-only, auto, etc.).",
|
|
58385
58749
|
inputSchema: {
|
|
58386
|
-
agentId:
|
|
58387
|
-
modeId:
|
|
58750
|
+
agentId: z44.string(),
|
|
58751
|
+
modeId: z44.string()
|
|
58388
58752
|
},
|
|
58389
58753
|
outputSchema: {
|
|
58390
|
-
success:
|
|
58391
|
-
newMode:
|
|
58754
|
+
success: z44.boolean(),
|
|
58755
|
+
newMode: z44.string()
|
|
58392
58756
|
}
|
|
58393
58757
|
},
|
|
58394
58758
|
async ({ agentId, modeId }) => {
|
|
@@ -58406,9 +58770,9 @@ ${curatedContent}`;
|
|
|
58406
58770
|
description: "Return all pending permission requests across all agents with the normalized payloads.",
|
|
58407
58771
|
inputSchema: {},
|
|
58408
58772
|
outputSchema: {
|
|
58409
|
-
permissions:
|
|
58410
|
-
|
|
58411
|
-
agentId:
|
|
58773
|
+
permissions: z44.array(
|
|
58774
|
+
z44.object({
|
|
58775
|
+
agentId: z44.string(),
|
|
58412
58776
|
status: AgentStatusEnum,
|
|
58413
58777
|
request: AgentPermissionRequestPayloadSchema
|
|
58414
58778
|
})
|
|
@@ -58436,12 +58800,12 @@ ${curatedContent}`;
|
|
|
58436
58800
|
title: "Respond to permission",
|
|
58437
58801
|
description: "Approve or deny a pending permission request with an AgentManager-compatible response payload.",
|
|
58438
58802
|
inputSchema: {
|
|
58439
|
-
agentId:
|
|
58440
|
-
requestId:
|
|
58803
|
+
agentId: z44.string(),
|
|
58804
|
+
requestId: z44.string(),
|
|
58441
58805
|
response: AgentPermissionResponseSchema
|
|
58442
58806
|
},
|
|
58443
58807
|
outputSchema: {
|
|
58444
|
-
success:
|
|
58808
|
+
success: z44.boolean()
|
|
58445
58809
|
}
|
|
58446
58810
|
},
|
|
58447
58811
|
async ({ agentId, requestId, response }) => {
|
|
@@ -58468,7 +58832,7 @@ async function createMcpWorktree(options) {
|
|
|
58468
58832
|
}
|
|
58469
58833
|
|
|
58470
58834
|
// ../server/src/server/workspace-registry-bootstrap.ts
|
|
58471
|
-
import
|
|
58835
|
+
import path30 from "node:path";
|
|
58472
58836
|
function minIsoDate(left, right) {
|
|
58473
58837
|
if (!left) {
|
|
58474
58838
|
return right;
|
|
@@ -58565,8 +58929,8 @@ async function bootstrapWorkspaceRegistries(options) {
|
|
|
58565
58929
|
}
|
|
58566
58930
|
options.logger.info(
|
|
58567
58931
|
{
|
|
58568
|
-
projectsFile:
|
|
58569
|
-
workspacesFile:
|
|
58932
|
+
projectsFile: path30.join(options.appostleHome, "projects", "projects.json"),
|
|
58933
|
+
workspacesFile: path30.join(options.appostleHome, "projects", "workspaces.json"),
|
|
58570
58934
|
materializedProjects: projectRanges.size,
|
|
58571
58935
|
materializedWorkspaces: recordsByWorkspaceId.size
|
|
58572
58936
|
},
|
|
@@ -58762,83 +59126,83 @@ var CheckoutDiffManager = class {
|
|
|
58762
59126
|
|
|
58763
59127
|
// ../server/src/server/loop-service.ts
|
|
58764
59128
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
58765
|
-
import { promises as
|
|
58766
|
-
import
|
|
58767
|
-
import { z as
|
|
59129
|
+
import { promises as fs18 } from "node:fs";
|
|
59130
|
+
import path31 from "node:path";
|
|
59131
|
+
import { z as z45 } from "zod";
|
|
58768
59132
|
var LOOP_ID_LENGTH = 8;
|
|
58769
59133
|
var DEFAULT_LOOP_PROVIDER = "claude";
|
|
58770
59134
|
var MAX_VERIFY_OUTPUT_BYTES = 64 * 1024;
|
|
58771
|
-
var LoopVerifyPromptSchema =
|
|
58772
|
-
passed:
|
|
58773
|
-
reason:
|
|
58774
|
-
});
|
|
58775
|
-
var LoopLogEntrySchema2 =
|
|
58776
|
-
seq:
|
|
58777
|
-
timestamp:
|
|
58778
|
-
iteration:
|
|
58779
|
-
source:
|
|
58780
|
-
level:
|
|
58781
|
-
text:
|
|
58782
|
-
});
|
|
58783
|
-
var LoopVerifyCheckResultSchema2 =
|
|
58784
|
-
command:
|
|
58785
|
-
exitCode:
|
|
58786
|
-
passed:
|
|
58787
|
-
stdout:
|
|
58788
|
-
stderr:
|
|
58789
|
-
startedAt:
|
|
58790
|
-
completedAt:
|
|
58791
|
-
});
|
|
58792
|
-
var LoopVerifyPromptResultSchema2 =
|
|
58793
|
-
passed:
|
|
58794
|
-
reason:
|
|
58795
|
-
verifierAgentId:
|
|
58796
|
-
startedAt:
|
|
58797
|
-
completedAt:
|
|
58798
|
-
});
|
|
58799
|
-
var LoopIterationRecordSchema2 =
|
|
58800
|
-
index:
|
|
58801
|
-
workerAgentId:
|
|
58802
|
-
workerStartedAt:
|
|
58803
|
-
workerCompletedAt:
|
|
58804
|
-
verifierAgentId:
|
|
58805
|
-
status:
|
|
58806
|
-
workerOutcome:
|
|
58807
|
-
failureReason:
|
|
58808
|
-
verifyChecks:
|
|
59135
|
+
var LoopVerifyPromptSchema = z45.object({
|
|
59136
|
+
passed: z45.boolean(),
|
|
59137
|
+
reason: z45.string().min(1)
|
|
59138
|
+
});
|
|
59139
|
+
var LoopLogEntrySchema2 = z45.object({
|
|
59140
|
+
seq: z45.number().int().positive(),
|
|
59141
|
+
timestamp: z45.string(),
|
|
59142
|
+
iteration: z45.number().int().positive().nullable(),
|
|
59143
|
+
source: z45.enum(["loop", "worker", "verifier", "verify-check"]),
|
|
59144
|
+
level: z45.enum(["info", "error"]),
|
|
59145
|
+
text: z45.string()
|
|
59146
|
+
});
|
|
59147
|
+
var LoopVerifyCheckResultSchema2 = z45.object({
|
|
59148
|
+
command: z45.string(),
|
|
59149
|
+
exitCode: z45.number().int(),
|
|
59150
|
+
passed: z45.boolean(),
|
|
59151
|
+
stdout: z45.string(),
|
|
59152
|
+
stderr: z45.string(),
|
|
59153
|
+
startedAt: z45.string(),
|
|
59154
|
+
completedAt: z45.string()
|
|
59155
|
+
});
|
|
59156
|
+
var LoopVerifyPromptResultSchema2 = z45.object({
|
|
59157
|
+
passed: z45.boolean(),
|
|
59158
|
+
reason: z45.string(),
|
|
59159
|
+
verifierAgentId: z45.string().nullable(),
|
|
59160
|
+
startedAt: z45.string(),
|
|
59161
|
+
completedAt: z45.string()
|
|
59162
|
+
});
|
|
59163
|
+
var LoopIterationRecordSchema2 = z45.object({
|
|
59164
|
+
index: z45.number().int().positive(),
|
|
59165
|
+
workerAgentId: z45.string().nullable(),
|
|
59166
|
+
workerStartedAt: z45.string(),
|
|
59167
|
+
workerCompletedAt: z45.string().nullable(),
|
|
59168
|
+
verifierAgentId: z45.string().nullable(),
|
|
59169
|
+
status: z45.enum(["running", "succeeded", "failed", "stopped"]),
|
|
59170
|
+
workerOutcome: z45.enum(["completed", "failed", "canceled"]).nullable(),
|
|
59171
|
+
failureReason: z45.string().nullable(),
|
|
59172
|
+
verifyChecks: z45.array(LoopVerifyCheckResultSchema2),
|
|
58809
59173
|
verifyPrompt: LoopVerifyPromptResultSchema2.nullable()
|
|
58810
59174
|
});
|
|
58811
|
-
var LoopRecordSchema2 =
|
|
58812
|
-
id:
|
|
58813
|
-
name:
|
|
58814
|
-
prompt:
|
|
58815
|
-
cwd:
|
|
58816
|
-
provider:
|
|
58817
|
-
model:
|
|
58818
|
-
workerProvider:
|
|
58819
|
-
workerModel:
|
|
58820
|
-
verifierProvider:
|
|
58821
|
-
verifierModel:
|
|
58822
|
-
verifyPrompt:
|
|
58823
|
-
verifyChecks:
|
|
58824
|
-
archive:
|
|
58825
|
-
sleepMs:
|
|
58826
|
-
maxIterations:
|
|
58827
|
-
maxTimeMs:
|
|
58828
|
-
status:
|
|
58829
|
-
createdAt:
|
|
58830
|
-
updatedAt:
|
|
58831
|
-
startedAt:
|
|
58832
|
-
completedAt:
|
|
58833
|
-
stopRequestedAt:
|
|
58834
|
-
iterations:
|
|
58835
|
-
logs:
|
|
58836
|
-
nextLogSeq:
|
|
58837
|
-
activeIteration:
|
|
58838
|
-
activeWorkerAgentId:
|
|
58839
|
-
activeVerifierAgentId:
|
|
58840
|
-
});
|
|
58841
|
-
var StoredLoopsSchema =
|
|
59175
|
+
var LoopRecordSchema2 = z45.object({
|
|
59176
|
+
id: z45.string(),
|
|
59177
|
+
name: z45.string().nullable(),
|
|
59178
|
+
prompt: z45.string(),
|
|
59179
|
+
cwd: z45.string(),
|
|
59180
|
+
provider: z45.string(),
|
|
59181
|
+
model: z45.string().nullable(),
|
|
59182
|
+
workerProvider: z45.string().nullable(),
|
|
59183
|
+
workerModel: z45.string().nullable(),
|
|
59184
|
+
verifierProvider: z45.string().nullable(),
|
|
59185
|
+
verifierModel: z45.string().nullable(),
|
|
59186
|
+
verifyPrompt: z45.string().nullable(),
|
|
59187
|
+
verifyChecks: z45.array(z45.string()),
|
|
59188
|
+
archive: z45.boolean(),
|
|
59189
|
+
sleepMs: z45.number().int().nonnegative(),
|
|
59190
|
+
maxIterations: z45.number().int().positive().nullable(),
|
|
59191
|
+
maxTimeMs: z45.number().int().positive().nullable(),
|
|
59192
|
+
status: z45.enum(["running", "succeeded", "failed", "stopped"]),
|
|
59193
|
+
createdAt: z45.string(),
|
|
59194
|
+
updatedAt: z45.string(),
|
|
59195
|
+
startedAt: z45.string(),
|
|
59196
|
+
completedAt: z45.string().nullable(),
|
|
59197
|
+
stopRequestedAt: z45.string().nullable(),
|
|
59198
|
+
iterations: z45.array(LoopIterationRecordSchema2),
|
|
59199
|
+
logs: z45.array(LoopLogEntrySchema2),
|
|
59200
|
+
nextLogSeq: z45.number().int().positive(),
|
|
59201
|
+
activeIteration: z45.number().int().positive().nullable(),
|
|
59202
|
+
activeWorkerAgentId: z45.string().nullable(),
|
|
59203
|
+
activeVerifierAgentId: z45.string().nullable()
|
|
59204
|
+
});
|
|
59205
|
+
var StoredLoopsSchema = z45.array(LoopRecordSchema2);
|
|
58842
59206
|
function nowIso() {
|
|
58843
59207
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
58844
59208
|
}
|
|
@@ -58969,7 +59333,7 @@ var LoopService = class {
|
|
|
58969
59333
|
this.loops = /* @__PURE__ */ new Map();
|
|
58970
59334
|
this.persistQueue = Promise.resolve();
|
|
58971
59335
|
this.running = /* @__PURE__ */ new Map();
|
|
58972
|
-
this.storePath =
|
|
59336
|
+
this.storePath = path31.join(options.appostleHome, "loops", "loops.json");
|
|
58973
59337
|
this.logger = options.logger.child({ module: "loop-service" });
|
|
58974
59338
|
}
|
|
58975
59339
|
async initialize() {
|
|
@@ -58978,7 +59342,7 @@ var LoopService = class {
|
|
|
58978
59342
|
}
|
|
58979
59343
|
this.loops.clear();
|
|
58980
59344
|
try {
|
|
58981
|
-
const raw = await
|
|
59345
|
+
const raw = await fs18.readFile(this.storePath, "utf8");
|
|
58982
59346
|
const parsed = StoredLoopsSchema.parse(JSON.parse(raw));
|
|
58983
59347
|
for (const record of parsed) {
|
|
58984
59348
|
if (record.status === "running") {
|
|
@@ -59038,7 +59402,7 @@ var LoopService = class {
|
|
|
59038
59402
|
id: createLoopId(),
|
|
59039
59403
|
name: normalizeName(input.name),
|
|
59040
59404
|
prompt,
|
|
59041
|
-
cwd:
|
|
59405
|
+
cwd: path31.resolve(input.cwd),
|
|
59042
59406
|
provider: input.provider ?? DEFAULT_LOOP_PROVIDER,
|
|
59043
59407
|
model: normalizePrompt(input.model, "model"),
|
|
59044
59408
|
workerProvider: input.workerProvider ?? null,
|
|
@@ -59486,11 +59850,11 @@ ${output}` : `exit ${result.exitCode}`
|
|
|
59486
59850
|
}
|
|
59487
59851
|
async persist() {
|
|
59488
59852
|
const nextPersist = this.persistQueue.then(async () => {
|
|
59489
|
-
await
|
|
59853
|
+
await fs18.mkdir(path31.dirname(this.storePath), { recursive: true });
|
|
59490
59854
|
const records = Array.from(this.loops.values()).sort(
|
|
59491
59855
|
(left, right) => left.createdAt.localeCompare(right.createdAt)
|
|
59492
59856
|
);
|
|
59493
|
-
await
|
|
59857
|
+
await fs18.writeFile(this.storePath, JSON.stringify(records, null, 2), "utf8");
|
|
59494
59858
|
});
|
|
59495
59859
|
this.persistQueue = nextPersist.catch(() => {
|
|
59496
59860
|
});
|
|
@@ -60031,20 +60395,20 @@ var ScheduleService = class {
|
|
|
60031
60395
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
60032
60396
|
|
|
60033
60397
|
// ../server/src/server/quest/store.ts
|
|
60034
|
-
import { promises as
|
|
60035
|
-
import
|
|
60036
|
-
import { z as
|
|
60037
|
-
var StoredQuestsSchema =
|
|
60398
|
+
import { promises as fs19 } from "node:fs";
|
|
60399
|
+
import path32 from "node:path";
|
|
60400
|
+
import { z as z46 } from "zod";
|
|
60401
|
+
var StoredQuestsSchema = z46.array(QuestRecordSchema);
|
|
60038
60402
|
var QuestStore = class {
|
|
60039
60403
|
constructor(options) {
|
|
60040
|
-
this.filePath =
|
|
60404
|
+
this.filePath = path32.join(options.appostleHome, "quests", "quests.json");
|
|
60041
60405
|
this.logger = options.logger;
|
|
60042
60406
|
this.records = /* @__PURE__ */ new Map();
|
|
60043
60407
|
this.persistQueue = Promise.resolve();
|
|
60044
60408
|
}
|
|
60045
60409
|
async initialize() {
|
|
60046
60410
|
try {
|
|
60047
|
-
const raw = await
|
|
60411
|
+
const raw = await fs19.readFile(this.filePath, "utf8");
|
|
60048
60412
|
const parsed = StoredQuestsSchema.parse(JSON.parse(raw));
|
|
60049
60413
|
this.records = new Map(parsed.map((r) => [r.id, r]));
|
|
60050
60414
|
} catch (err) {
|
|
@@ -60078,9 +60442,9 @@ var QuestStore = class {
|
|
|
60078
60442
|
}
|
|
60079
60443
|
async persist() {
|
|
60080
60444
|
this.persistQueue = this.persistQueue.then(async () => {
|
|
60081
|
-
await
|
|
60445
|
+
await fs19.mkdir(path32.dirname(this.filePath), { recursive: true });
|
|
60082
60446
|
const payload = JSON.stringify([...this.records.values()], null, 2);
|
|
60083
|
-
await
|
|
60447
|
+
await fs19.writeFile(this.filePath, payload, "utf8");
|
|
60084
60448
|
}).catch((err) => {
|
|
60085
60449
|
this.logger.error(
|
|
60086
60450
|
{ err: err instanceof Error ? err.message : String(err) },
|
|
@@ -60138,7 +60502,7 @@ function getRulesForKind(kind) {
|
|
|
60138
60502
|
}
|
|
60139
60503
|
|
|
60140
60504
|
// ../server/src/server/quest/quest-metadata-generator.ts
|
|
60141
|
-
import { z as
|
|
60505
|
+
import { z as z47 } from "zod";
|
|
60142
60506
|
var MAX_AUTO_QUEST_NAME_CHARS = 40;
|
|
60143
60507
|
function hasExplicitName(name) {
|
|
60144
60508
|
return Boolean(name && name.trim().length > 0);
|
|
@@ -60150,7 +60514,7 @@ function normalizeAutoName(name) {
|
|
|
60150
60514
|
}
|
|
60151
60515
|
return normalized.slice(0, MAX_AUTO_QUEST_NAME_CHARS).trim() || null;
|
|
60152
60516
|
}
|
|
60153
|
-
function
|
|
60517
|
+
function buildPrompt4(prompt) {
|
|
60154
60518
|
return [
|
|
60155
60519
|
"Generate a short label for a multi-agent quest based on the user prompt.",
|
|
60156
60520
|
`Name: short descriptive label (<= ${MAX_AUTO_QUEST_NAME_CHARS} chars).`,
|
|
@@ -60168,8 +60532,8 @@ async function generateAndApplyQuestMetadata(options) {
|
|
|
60168
60532
|
if (hasExplicitName(options.explicitName)) {
|
|
60169
60533
|
return;
|
|
60170
60534
|
}
|
|
60171
|
-
const schema =
|
|
60172
|
-
name:
|
|
60535
|
+
const schema = z47.object({
|
|
60536
|
+
name: z47.string().min(1).max(MAX_AUTO_QUEST_NAME_CHARS)
|
|
60173
60537
|
});
|
|
60174
60538
|
const generator = options.deps?.generateStructuredAgentResponseWithFallback ?? generateStructuredAgentResponseWithFallback;
|
|
60175
60539
|
let result;
|
|
@@ -60177,7 +60541,7 @@ async function generateAndApplyQuestMetadata(options) {
|
|
|
60177
60541
|
result = await generator({
|
|
60178
60542
|
manager: options.questService.agentManager,
|
|
60179
60543
|
cwd: options.cwd,
|
|
60180
|
-
prompt:
|
|
60544
|
+
prompt: buildPrompt4(prompt),
|
|
60181
60545
|
schema,
|
|
60182
60546
|
schemaName: "QuestMetadata",
|
|
60183
60547
|
maxRetries: 2,
|
|
@@ -60844,17 +61208,17 @@ ${text || "(no output)"}`).join("\n\n");
|
|
|
60844
61208
|
|
|
60845
61209
|
// ../server/src/server/quest/runner-orchestrator.ts
|
|
60846
61210
|
import { access, readFile as readFile5 } from "node:fs/promises";
|
|
60847
|
-
import
|
|
61211
|
+
import path33 from "node:path";
|
|
60848
61212
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
60849
61213
|
|
|
60850
61214
|
// ../server/src/server/quest/orchestrator-mcp.ts
|
|
60851
61215
|
import { createSdkMcpServer as createSdkMcpServer2, tool as tool3 } from "@anthropic-ai/claude-agent-sdk";
|
|
60852
|
-
import { z as
|
|
61216
|
+
import { z as z48 } from "zod";
|
|
60853
61217
|
var HANDOFF_INPUT_SHAPE = {
|
|
60854
|
-
rolePath:
|
|
61218
|
+
rolePath: z48.string().min(1).describe(
|
|
60855
61219
|
"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."
|
|
60856
61220
|
),
|
|
60857
|
-
task:
|
|
61221
|
+
task: z48.string().min(1).describe(
|
|
60858
61222
|
"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."
|
|
60859
61223
|
)
|
|
60860
61224
|
};
|
|
@@ -61075,16 +61439,16 @@ var FALLBACK_QUEEN_PROMPT_TEMPLATE = [
|
|
|
61075
61439
|
var QUEEN_PROMPT_FILENAME = "queen-prompt.md";
|
|
61076
61440
|
var QUEEN_PROMPT_MAX_LOOKUP_LEVELS = 10;
|
|
61077
61441
|
async function findQueenPromptOnDisk() {
|
|
61078
|
-
const start =
|
|
61442
|
+
const start = path33.dirname(fileURLToPath5(import.meta.url));
|
|
61079
61443
|
let cursor = start;
|
|
61080
61444
|
for (let i = 0; i < QUEEN_PROMPT_MAX_LOOKUP_LEVELS; i += 1) {
|
|
61081
|
-
const candidate =
|
|
61445
|
+
const candidate = path33.join(cursor, QUEEN_PROMPT_FILENAME);
|
|
61082
61446
|
try {
|
|
61083
61447
|
await access(candidate);
|
|
61084
61448
|
return candidate;
|
|
61085
61449
|
} catch {
|
|
61086
61450
|
}
|
|
61087
|
-
const parent =
|
|
61451
|
+
const parent = path33.dirname(cursor);
|
|
61088
61452
|
if (parent === cursor) break;
|
|
61089
61453
|
cursor = parent;
|
|
61090
61454
|
}
|
|
@@ -61130,7 +61494,7 @@ async function resolveWorkerConfigForHandoff(args) {
|
|
|
61130
61494
|
title: `quest ${record.id} handoff: ${taskTitle}`,
|
|
61131
61495
|
internal: true
|
|
61132
61496
|
};
|
|
61133
|
-
const roleName =
|
|
61497
|
+
const roleName = path33.basename(rolePath, path33.extname(rolePath));
|
|
61134
61498
|
try {
|
|
61135
61499
|
const resolved = await resolveRole({
|
|
61136
61500
|
roleName,
|
|
@@ -61309,7 +61673,7 @@ var OrchestratorRunner = class {
|
|
|
61309
61673
|
import { promisify as promisify4 } from "node:util";
|
|
61310
61674
|
import { execFile as execFile3 } from "node:child_process";
|
|
61311
61675
|
import { appendFile, mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
|
|
61312
|
-
import
|
|
61676
|
+
import path34 from "node:path";
|
|
61313
61677
|
var execFileAsync3 = promisify4(execFile3);
|
|
61314
61678
|
var MAX_VERIFY_OUTPUT_BYTES2 = 64 * 1024;
|
|
61315
61679
|
function nowIso5() {
|
|
@@ -61384,7 +61748,7 @@ function runContainsPromise(run, completionPromise) {
|
|
|
61384
61748
|
return finalText.includes(completionPromise);
|
|
61385
61749
|
}
|
|
61386
61750
|
function resolveRalphStatePaths(cwd) {
|
|
61387
|
-
const dir =
|
|
61751
|
+
const dir = path34.join(cwd, ".hiveminds", "ralph loop");
|
|
61388
61752
|
return {
|
|
61389
61753
|
dir,
|
|
61390
61754
|
logPath: ""
|
|
@@ -61394,7 +61758,7 @@ async function initializeRalphState(args) {
|
|
|
61394
61758
|
const base = resolveRalphStatePaths(args.record.cwd);
|
|
61395
61759
|
const paths = {
|
|
61396
61760
|
dir: base.dir,
|
|
61397
|
-
logPath:
|
|
61761
|
+
logPath: path34.join(base.dir, `ralphloop_${args.record.id}.md`)
|
|
61398
61762
|
};
|
|
61399
61763
|
await mkdir9(paths.dir, { recursive: true });
|
|
61400
61764
|
await writeFile8(
|
|
@@ -61629,8 +61993,8 @@ function deepMerge(current, patch) {
|
|
|
61629
61993
|
}
|
|
61630
61994
|
return next;
|
|
61631
61995
|
}
|
|
61632
|
-
function getValueAtPath(config,
|
|
61633
|
-
return
|
|
61996
|
+
function getValueAtPath(config, path40) {
|
|
61997
|
+
return path40.split(".").reduce((value, segment) => isRecord3(value) ? value[segment] : void 0, config);
|
|
61634
61998
|
}
|
|
61635
61999
|
function isEqualValue(a, b) {
|
|
61636
62000
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -61649,20 +62013,20 @@ var DaemonConfigStore = class {
|
|
|
61649
62013
|
patch(partial) {
|
|
61650
62014
|
const parsedPatch = MutableDaemonConfigPatchSchema.parse(partial);
|
|
61651
62015
|
const next = MutableDaemonConfigSchema.parse(deepMerge(this.current, parsedPatch));
|
|
61652
|
-
const changedFieldPaths = Array.from(this.fieldChangeHandlers.keys()).filter((
|
|
61653
|
-
return !isEqualValue(getValueAtPath(this.current,
|
|
62016
|
+
const changedFieldPaths = Array.from(this.fieldChangeHandlers.keys()).filter((path40) => {
|
|
62017
|
+
return !isEqualValue(getValueAtPath(this.current, path40), getValueAtPath(next, path40));
|
|
61654
62018
|
});
|
|
61655
62019
|
if (changedFieldPaths.length === 0 && isEqualValue(this.current, next)) {
|
|
61656
62020
|
return this.current;
|
|
61657
62021
|
}
|
|
61658
62022
|
this.persistConfig(next);
|
|
61659
62023
|
this.current = next;
|
|
61660
|
-
for (const
|
|
61661
|
-
const handlers = this.fieldChangeHandlers.get(
|
|
62024
|
+
for (const path40 of changedFieldPaths) {
|
|
62025
|
+
const handlers = this.fieldChangeHandlers.get(path40);
|
|
61662
62026
|
if (!handlers) {
|
|
61663
62027
|
continue;
|
|
61664
62028
|
}
|
|
61665
|
-
const value = getValueAtPath(next,
|
|
62029
|
+
const value = getValueAtPath(next, path40);
|
|
61666
62030
|
for (const handler of handlers) {
|
|
61667
62031
|
handler(value);
|
|
61668
62032
|
}
|
|
@@ -61672,18 +62036,18 @@ var DaemonConfigStore = class {
|
|
|
61672
62036
|
}
|
|
61673
62037
|
return next;
|
|
61674
62038
|
}
|
|
61675
|
-
onFieldChange(
|
|
61676
|
-
const handlers = this.fieldChangeHandlers.get(
|
|
62039
|
+
onFieldChange(path40, handler) {
|
|
62040
|
+
const handlers = this.fieldChangeHandlers.get(path40) ?? /* @__PURE__ */ new Set();
|
|
61677
62041
|
handlers.add(handler);
|
|
61678
|
-
this.fieldChangeHandlers.set(
|
|
62042
|
+
this.fieldChangeHandlers.set(path40, handlers);
|
|
61679
62043
|
return () => {
|
|
61680
|
-
const currentHandlers = this.fieldChangeHandlers.get(
|
|
62044
|
+
const currentHandlers = this.fieldChangeHandlers.get(path40);
|
|
61681
62045
|
if (!currentHandlers) {
|
|
61682
62046
|
return;
|
|
61683
62047
|
}
|
|
61684
62048
|
currentHandlers.delete(handler);
|
|
61685
62049
|
if (currentHandlers.size === 0) {
|
|
61686
|
-
this.fieldChangeHandlers.delete(
|
|
62050
|
+
this.fieldChangeHandlers.delete(path40);
|
|
61687
62051
|
}
|
|
61688
62052
|
};
|
|
61689
62053
|
}
|
|
@@ -62576,13 +62940,13 @@ function createTerminalManager() {
|
|
|
62576
62940
|
}
|
|
62577
62941
|
|
|
62578
62942
|
// ../server/src/shared/connection-offer.ts
|
|
62579
|
-
import { z as
|
|
62580
|
-
var ConnectionOfferV2Schema =
|
|
62581
|
-
v:
|
|
62582
|
-
serverId:
|
|
62583
|
-
daemonPublicKeyB64:
|
|
62584
|
-
relay:
|
|
62585
|
-
endpoint:
|
|
62943
|
+
import { z as z49 } from "zod";
|
|
62944
|
+
var ConnectionOfferV2Schema = z49.object({
|
|
62945
|
+
v: z49.literal(2),
|
|
62946
|
+
serverId: z49.string().min(1),
|
|
62947
|
+
daemonPublicKeyB64: z49.string().min(1),
|
|
62948
|
+
relay: z49.object({
|
|
62949
|
+
endpoint: z49.string().min(1)
|
|
62586
62950
|
})
|
|
62587
62951
|
});
|
|
62588
62952
|
|
|
@@ -63229,7 +63593,7 @@ function createAuthServerClient(options) {
|
|
|
63229
63593
|
|
|
63230
63594
|
// ../server/src/server/package-version.ts
|
|
63231
63595
|
import { existsSync as existsSync19, readFileSync as readFileSync10 } from "node:fs";
|
|
63232
|
-
import
|
|
63596
|
+
import path35 from "node:path";
|
|
63233
63597
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
63234
63598
|
var PackageVersionResolutionError = class extends Error {
|
|
63235
63599
|
constructor(params) {
|
|
@@ -63239,9 +63603,9 @@ var PackageVersionResolutionError = class extends Error {
|
|
|
63239
63603
|
};
|
|
63240
63604
|
function resolvePackageVersion(params) {
|
|
63241
63605
|
const moduleUrl = params.moduleUrl ?? import.meta.url;
|
|
63242
|
-
let currentDir =
|
|
63606
|
+
let currentDir = path35.dirname(fileURLToPath6(moduleUrl));
|
|
63243
63607
|
while (true) {
|
|
63244
|
-
const packageJsonPath =
|
|
63608
|
+
const packageJsonPath = path35.join(currentDir, "package.json");
|
|
63245
63609
|
if (existsSync19(packageJsonPath)) {
|
|
63246
63610
|
try {
|
|
63247
63611
|
const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf8"));
|
|
@@ -63260,7 +63624,7 @@ function resolvePackageVersion(params) {
|
|
|
63260
63624
|
}
|
|
63261
63625
|
}
|
|
63262
63626
|
}
|
|
63263
|
-
const parentDir =
|
|
63627
|
+
const parentDir = path35.dirname(currentDir);
|
|
63264
63628
|
if (parentDir === currentDir) {
|
|
63265
63629
|
break;
|
|
63266
63630
|
}
|
|
@@ -63765,11 +64129,11 @@ async function createAppostleDaemon(config, rootLogger) {
|
|
|
63765
64129
|
httpServer.on("upgrade", scriptProxyUpgradeHandler);
|
|
63766
64130
|
const agentStorage = new AgentStorage(config.agentStoragePath, logger);
|
|
63767
64131
|
const projectRegistry = new FileBackedProjectRegistry(
|
|
63768
|
-
|
|
64132
|
+
path36.join(config.appostleHome, "projects", "projects.json"),
|
|
63769
64133
|
logger
|
|
63770
64134
|
);
|
|
63771
64135
|
workspaceRegistry = new FileBackedWorkspaceRegistry(
|
|
63772
|
-
|
|
64136
|
+
path36.join(config.appostleHome, "projects", "workspaces.json"),
|
|
63773
64137
|
logger
|
|
63774
64138
|
);
|
|
63775
64139
|
const chatService = new FileBackedChatService({
|
|
@@ -63787,6 +64151,7 @@ async function createAppostleDaemon(config, rootLogger) {
|
|
|
63787
64151
|
registry: agentStorage,
|
|
63788
64152
|
appostleHome: config.appostleHome,
|
|
63789
64153
|
chromeEnabled: config.chromeEnabled ?? true,
|
|
64154
|
+
playwrightEnabled: config.playwrightEnabled ?? true,
|
|
63790
64155
|
ownerUserId: ownerUserId ?? void 0,
|
|
63791
64156
|
logger
|
|
63792
64157
|
});
|
|
@@ -64211,22 +64576,22 @@ async function closeAllAgents(logger, agentManager) {
|
|
|
64211
64576
|
}
|
|
64212
64577
|
|
|
64213
64578
|
// ../server/src/server/config.ts
|
|
64214
|
-
import
|
|
64215
|
-
import { z as
|
|
64579
|
+
import path38 from "node:path";
|
|
64580
|
+
import { z as z53 } from "zod";
|
|
64216
64581
|
|
|
64217
64582
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
64218
|
-
import { z as
|
|
64583
|
+
import { z as z52 } from "zod";
|
|
64219
64584
|
|
|
64220
64585
|
// ../server/src/server/speech/providers/local/config.ts
|
|
64221
|
-
import
|
|
64222
|
-
import { z as
|
|
64223
|
-
var DEFAULT_LOCAL_MODELS_SUBDIR =
|
|
64224
|
-
var NumberLikeSchema2 =
|
|
64225
|
-
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(
|
|
64226
|
-
var OptionalIntegerSchema = NumberLikeSchema2.pipe(
|
|
64227
|
-
var LocalSpeechResolutionSchema =
|
|
64228
|
-
includeProviderConfig:
|
|
64229
|
-
modelsDir:
|
|
64586
|
+
import path37 from "node:path";
|
|
64587
|
+
import { z as z50 } from "zod";
|
|
64588
|
+
var DEFAULT_LOCAL_MODELS_SUBDIR = path37.join("models", "local-speech");
|
|
64589
|
+
var NumberLikeSchema2 = z50.union([z50.number(), z50.string().trim().min(1)]);
|
|
64590
|
+
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z50.coerce.number().finite()).optional();
|
|
64591
|
+
var OptionalIntegerSchema = NumberLikeSchema2.pipe(z50.coerce.number().int()).optional();
|
|
64592
|
+
var LocalSpeechResolutionSchema = z50.object({
|
|
64593
|
+
includeProviderConfig: z50.boolean(),
|
|
64594
|
+
modelsDir: z50.string().trim().min(1),
|
|
64230
64595
|
dictationLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
64231
64596
|
voiceLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
64232
64597
|
voiceLocalTtsModel: LocalTtsModelIdSchema.default(DEFAULT_LOCAL_TTS_MODEL),
|
|
@@ -64247,7 +64612,7 @@ function resolveLocalSpeechConfig(params) {
|
|
|
64247
64612
|
const includeProviderConfig = shouldIncludeLocalProviderConfig(params);
|
|
64248
64613
|
const parsed = LocalSpeechResolutionSchema.parse({
|
|
64249
64614
|
includeProviderConfig,
|
|
64250
|
-
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ??
|
|
64615
|
+
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ?? path37.join(params.appostleHome, DEFAULT_LOCAL_MODELS_SUBDIR),
|
|
64251
64616
|
dictationLocalSttModel: params.env.APPOSTLE_DICTATION_LOCAL_STT_MODEL ?? persistedLocalFeatureModel(
|
|
64252
64617
|
params.providers.dictationStt.provider,
|
|
64253
64618
|
params.providers.dictationStt.enabled,
|
|
@@ -64282,17 +64647,17 @@ function resolveLocalSpeechConfig(params) {
|
|
|
64282
64647
|
}
|
|
64283
64648
|
|
|
64284
64649
|
// ../server/src/server/speech/speech-types.ts
|
|
64285
|
-
import { z as
|
|
64286
|
-
var SpeechProviderIdSchema2 =
|
|
64287
|
-
var RequestedSpeechProviderSchema =
|
|
64650
|
+
import { z as z51 } from "zod";
|
|
64651
|
+
var SpeechProviderIdSchema2 = z51.enum(["openai", "local"]);
|
|
64652
|
+
var RequestedSpeechProviderSchema = z51.object({
|
|
64288
64653
|
provider: SpeechProviderIdSchema2,
|
|
64289
|
-
explicit:
|
|
64290
|
-
enabled:
|
|
64654
|
+
explicit: z51.boolean(),
|
|
64655
|
+
enabled: z51.boolean().optional()
|
|
64291
64656
|
});
|
|
64292
64657
|
|
|
64293
64658
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
64294
|
-
var OptionalSpeechProviderSchema =
|
|
64295
|
-
var OptionalBooleanFlagSchema =
|
|
64659
|
+
var OptionalSpeechProviderSchema = z52.string().trim().toLowerCase().pipe(SpeechProviderIdSchema2).optional();
|
|
64660
|
+
var OptionalBooleanFlagSchema = z52.union([z52.boolean(), z52.string().trim().toLowerCase()]).optional().transform((value) => {
|
|
64296
64661
|
if (typeof value === "boolean") {
|
|
64297
64662
|
return value;
|
|
64298
64663
|
}
|
|
@@ -64307,7 +64672,7 @@ var OptionalBooleanFlagSchema = z51.union([z51.boolean(), z51.string().trim().to
|
|
|
64307
64672
|
}
|
|
64308
64673
|
return void 0;
|
|
64309
64674
|
});
|
|
64310
|
-
var RequestedSpeechProvidersSchema =
|
|
64675
|
+
var RequestedSpeechProvidersSchema = z52.object({
|
|
64311
64676
|
dictationStt: OptionalSpeechProviderSchema.default("local"),
|
|
64312
64677
|
voiceTurnDetection: OptionalSpeechProviderSchema.default("local"),
|
|
64313
64678
|
voiceStt: OptionalSpeechProviderSchema.default("local"),
|
|
@@ -64416,9 +64781,9 @@ function parseBooleanEnv(value) {
|
|
|
64416
64781
|
}
|
|
64417
64782
|
return void 0;
|
|
64418
64783
|
}
|
|
64419
|
-
var OptionalVoiceLlmProviderSchema =
|
|
64784
|
+
var OptionalVoiceLlmProviderSchema = z53.union([z53.string(), z53.null(), z53.undefined()]).transform(
|
|
64420
64785
|
(value) => typeof value === "string" ? value.trim().toLowerCase() : null
|
|
64421
|
-
).pipe(
|
|
64786
|
+
).pipe(z53.union([AgentProviderSchema, z53.null()]));
|
|
64422
64787
|
function parseOptionalVoiceLlmProvider(value) {
|
|
64423
64788
|
const parsed = OptionalVoiceLlmProviderSchema.safeParse(value);
|
|
64424
64789
|
return parsed.success ? parsed.data : null;
|
|
@@ -64471,6 +64836,7 @@ function loadConfig(appostleHome, options) {
|
|
|
64471
64836
|
const mcpEnabled = options?.cli?.mcpEnabled ?? persisted.daemon?.mcp?.enabled ?? true;
|
|
64472
64837
|
const mcpInjectIntoAgents = options?.cli?.mcpInjectIntoAgents ?? persisted.daemon?.mcp?.injectIntoAgents ?? false;
|
|
64473
64838
|
const chromeEnabled = persisted.daemon?.chrome?.enabled ?? true;
|
|
64839
|
+
const playwrightEnabled = persisted.daemon?.playwright?.enabled ?? true;
|
|
64474
64840
|
const daemonIcon = persisted.daemon?.identity?.icon;
|
|
64475
64841
|
const relayEnabled = options?.cli?.relayEnabled ?? parseBooleanEnv(env.APPOSTLE_RELAY_ENABLED) ?? persisted.daemon?.relay?.enabled ?? true;
|
|
64476
64842
|
const relayEndpoint = env.APPOSTLE_RELAY_ENDPOINT ?? persisted.daemon?.relay?.endpoint ?? DEFAULT_RELAY_ENDPOINT;
|
|
@@ -64501,9 +64867,10 @@ function loadConfig(appostleHome, options) {
|
|
|
64501
64867
|
mcpEnabled,
|
|
64502
64868
|
mcpInjectIntoAgents,
|
|
64503
64869
|
chromeEnabled,
|
|
64870
|
+
playwrightEnabled,
|
|
64504
64871
|
mcpDebug: env.MCP_DEBUG === "1",
|
|
64505
64872
|
daemonIcon,
|
|
64506
|
-
agentStoragePath:
|
|
64873
|
+
agentStoragePath: path38.join(appostleHome, "agents"),
|
|
64507
64874
|
staticDir: "public",
|
|
64508
64875
|
agentClients: {},
|
|
64509
64876
|
relayEnabled,
|
|
@@ -64522,7 +64889,7 @@ function loadConfig(appostleHome, options) {
|
|
|
64522
64889
|
|
|
64523
64890
|
// ../server/src/server/logger.ts
|
|
64524
64891
|
import { existsSync as existsSync21, mkdirSync as mkdirSync8, readdirSync as readdirSync3, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
64525
|
-
import
|
|
64892
|
+
import path39 from "node:path";
|
|
64526
64893
|
import pino from "pino";
|
|
64527
64894
|
import pretty from "pino-pretty";
|
|
64528
64895
|
import { createStream as createRotatingFileStream } from "rotating-file-stream";
|
|
@@ -64565,14 +64932,14 @@ function parsePositiveInteger(value) {
|
|
|
64565
64932
|
return parsed;
|
|
64566
64933
|
}
|
|
64567
64934
|
function resolveFilePath(appostleHome, configuredPath) {
|
|
64568
|
-
const fallback =
|
|
64935
|
+
const fallback = path39.join(appostleHome, DEFAULT_DAEMON_LOG_FILENAME);
|
|
64569
64936
|
if (!configuredPath) {
|
|
64570
64937
|
return fallback;
|
|
64571
64938
|
}
|
|
64572
|
-
if (
|
|
64939
|
+
if (path39.isAbsolute(configuredPath)) {
|
|
64573
64940
|
return configuredPath;
|
|
64574
64941
|
}
|
|
64575
|
-
return
|
|
64942
|
+
return path39.resolve(appostleHome, configuredPath);
|
|
64576
64943
|
}
|
|
64577
64944
|
function minLogLevel(levels) {
|
|
64578
64945
|
let minLevel = levels[0];
|
|
@@ -64609,20 +64976,20 @@ function normalizeLoggerConfigInput(config) {
|
|
|
64609
64976
|
}
|
|
64610
64977
|
function rotateOnRestart(filePath, maxFiles) {
|
|
64611
64978
|
if (!existsSync21(filePath)) return;
|
|
64612
|
-
const dir =
|
|
64613
|
-
const base =
|
|
64979
|
+
const dir = path39.dirname(filePath);
|
|
64980
|
+
const base = path39.basename(filePath);
|
|
64614
64981
|
const now = /* @__PURE__ */ new Date();
|
|
64615
64982
|
const pad = (n) => String(n).padStart(2, "0");
|
|
64616
64983
|
const ts = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
64617
64984
|
try {
|
|
64618
|
-
renameSync3(filePath,
|
|
64985
|
+
renameSync3(filePath, path39.join(dir, `${ts}-00-${base}`));
|
|
64619
64986
|
} catch {
|
|
64620
64987
|
return;
|
|
64621
64988
|
}
|
|
64622
64989
|
const rotatedFiles = readdirSync3(dir).filter((f) => f.endsWith(`-${base}`) && f !== base).sort().reverse();
|
|
64623
64990
|
for (const file of rotatedFiles.slice(maxFiles)) {
|
|
64624
64991
|
try {
|
|
64625
|
-
unlinkSync2(
|
|
64992
|
+
unlinkSync2(path39.join(dir, file));
|
|
64626
64993
|
} catch {
|
|
64627
64994
|
}
|
|
64628
64995
|
}
|
|
@@ -64671,15 +65038,15 @@ function resolveLogConfig(configInput, options) {
|
|
|
64671
65038
|
}
|
|
64672
65039
|
function createRootLogger(configInput, options) {
|
|
64673
65040
|
const config = resolveLogConfig(configInput, options);
|
|
64674
|
-
mkdirSync8(
|
|
65041
|
+
mkdirSync8(path39.dirname(config.file.path), { recursive: true });
|
|
64675
65042
|
const consoleStream = config.console.format === "pretty" ? pretty({
|
|
64676
65043
|
colorize: true,
|
|
64677
65044
|
singleLine: true,
|
|
64678
65045
|
ignore: "pid,hostname"
|
|
64679
65046
|
}) : pino.destination({ dest: 1, sync: false });
|
|
64680
65047
|
rotateOnRestart(config.file.path, config.file.rotate.maxFiles);
|
|
64681
|
-
const fileStream = createRotatingFileStream(
|
|
64682
|
-
path:
|
|
65048
|
+
const fileStream = createRotatingFileStream(path39.basename(config.file.path), {
|
|
65049
|
+
path: path39.dirname(config.file.path),
|
|
64683
65050
|
size: toRotatingFileStreamSize(config.file.rotate.maxSize),
|
|
64684
65051
|
maxFiles: config.file.rotate.maxFiles
|
|
64685
65052
|
});
|