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/appostle.js
CHANGED
|
@@ -3234,7 +3234,9 @@ var BrandEntrySchema = z10.object({
|
|
|
3234
3234
|
variables: z10.array(BrandVariableSchema).optional().default([]),
|
|
3235
3235
|
path: z10.string(),
|
|
3236
3236
|
modifiedAt: z10.string(),
|
|
3237
|
-
size: z10.number()
|
|
3237
|
+
size: z10.number(),
|
|
3238
|
+
/** Raw markdown body (frontmatter stripped). Optional, fed by the scanner. */
|
|
3239
|
+
body: z10.string().optional()
|
|
3238
3240
|
});
|
|
3239
3241
|
var BrandFrontmatterSchema = z10.object({
|
|
3240
3242
|
description: z10.string().optional(),
|
|
@@ -3411,6 +3413,22 @@ var BrandGenerateWireframeResponseSchema = z10.object({
|
|
|
3411
3413
|
error: z10.string().nullable()
|
|
3412
3414
|
})
|
|
3413
3415
|
});
|
|
3416
|
+
var BrandAnalyzePhotographyRequestSchema = z10.object({
|
|
3417
|
+
type: z10.literal("brands/analyze-photography"),
|
|
3418
|
+
requestId: z10.string(),
|
|
3419
|
+
workspaceRoot: z10.string(),
|
|
3420
|
+
brandPath: z10.string(),
|
|
3421
|
+
brief: z10.string(),
|
|
3422
|
+
referenceDescriptions: z10.array(z10.string()).optional()
|
|
3423
|
+
});
|
|
3424
|
+
var BrandAnalyzePhotographyResponseSchema = z10.object({
|
|
3425
|
+
type: z10.literal("brands/analyze-photography/response"),
|
|
3426
|
+
payload: z10.object({
|
|
3427
|
+
requestId: z10.string(),
|
|
3428
|
+
updatedCount: z10.number(),
|
|
3429
|
+
error: z10.string().nullable()
|
|
3430
|
+
})
|
|
3431
|
+
});
|
|
3414
3432
|
var RightFontEntrySchema = z10.object({
|
|
3415
3433
|
id: z10.string(),
|
|
3416
3434
|
name: z10.string(),
|
|
@@ -3510,10 +3528,14 @@ var MutableDaemonConfigSchema = z11.object({
|
|
|
3510
3528
|
icon: z11.string().optional()
|
|
3511
3529
|
}).passthrough().optional(),
|
|
3512
3530
|
chrome: z11.object({
|
|
3513
|
-
// When false, agents on this host won't
|
|
3514
|
-
//
|
|
3515
|
-
//
|
|
3516
|
-
|
|
3531
|
+
// When false, agents on this host won't have the claude-in-chrome
|
|
3532
|
+
// tools injected. Set to false on hosts where the client's browser
|
|
3533
|
+
// should not be controlled (e.g. headless/server-only hosts).
|
|
3534
|
+
enabled: z11.boolean()
|
|
3535
|
+
}).passthrough().optional(),
|
|
3536
|
+
playwright: z11.object({
|
|
3537
|
+
// When false, agents on this host won't have the Playwright MCP
|
|
3538
|
+
// server injected.
|
|
3517
3539
|
enabled: z11.boolean()
|
|
3518
3540
|
}).passthrough().optional()
|
|
3519
3541
|
}).passthrough();
|
|
@@ -3522,7 +3544,8 @@ var MutableDaemonConfigPatchSchema = z11.object({
|
|
|
3522
3544
|
identity: z11.object({
|
|
3523
3545
|
icon: z11.string().optional()
|
|
3524
3546
|
}).partial().passthrough().optional(),
|
|
3525
|
-
chrome: MutableDaemonConfigSchema.shape.chrome
|
|
3547
|
+
chrome: MutableDaemonConfigSchema.shape.chrome,
|
|
3548
|
+
playwright: MutableDaemonConfigSchema.shape.playwright
|
|
3526
3549
|
}).partial().passthrough();
|
|
3527
3550
|
var AgentStatusSchema = z11.enum(AGENT_LIFECYCLE_STATUSES);
|
|
3528
3551
|
var AgentModeSchema = z11.object({
|
|
@@ -5532,6 +5555,7 @@ var SessionInboundMessageSchema = z11.discriminatedUnion("type", [
|
|
|
5532
5555
|
BrandGenerateLayoutRequestSchema,
|
|
5533
5556
|
BrandLayoutQaNextRequestSchema,
|
|
5534
5557
|
BrandGenerateWireframeRequestSchema,
|
|
5558
|
+
BrandAnalyzePhotographyRequestSchema,
|
|
5535
5559
|
RightFontLibraryRequestSchema,
|
|
5536
5560
|
GoogleFontsCatalogRequestSchema,
|
|
5537
5561
|
GoogleFontsDownloadRequestSchema,
|
|
@@ -7168,6 +7192,7 @@ var SessionOutboundMessageSchema = z11.discriminatedUnion("type", [
|
|
|
7168
7192
|
BrandGenerateLayoutResponseSchema,
|
|
7169
7193
|
BrandLayoutQaNextResponseSchema,
|
|
7170
7194
|
BrandGenerateWireframeResponseSchema,
|
|
7195
|
+
BrandAnalyzePhotographyResponseSchema,
|
|
7171
7196
|
RightFontLibraryResponseSchema,
|
|
7172
7197
|
GoogleFontsCatalogResponseSchema,
|
|
7173
7198
|
GoogleFontsDownloadResponseSchema,
|
|
@@ -7377,7 +7402,7 @@ import { exec } from "node:child_process";
|
|
|
7377
7402
|
import { promisify as promisify3 } from "util";
|
|
7378
7403
|
import { join as join14, resolve as resolve9, sep as sep2 } from "path";
|
|
7379
7404
|
import { homedir as homedir5, hostname as osHostname } from "node:os";
|
|
7380
|
-
import { z as
|
|
7405
|
+
import { z as z40 } from "zod";
|
|
7381
7406
|
|
|
7382
7407
|
// ../server/src/server/persisted-config.ts
|
|
7383
7408
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -7674,6 +7699,9 @@ var PersistedConfigSchema = z14.object({
|
|
|
7674
7699
|
}).strict().optional(),
|
|
7675
7700
|
chrome: z14.object({
|
|
7676
7701
|
enabled: z14.boolean().optional()
|
|
7702
|
+
}).strict().optional(),
|
|
7703
|
+
playwright: z14.object({
|
|
7704
|
+
enabled: z14.boolean().optional()
|
|
7677
7705
|
}).strict().optional()
|
|
7678
7706
|
}).strict().transform(({ allowedHosts, ...daemon }) => {
|
|
7679
7707
|
const hostnames = daemon.hostnames ?? allowedHosts;
|
|
@@ -18494,6 +18522,7 @@ var ClaudeAgentClient = class {
|
|
|
18494
18522
|
runtimeSettings: this.runtimeSettings,
|
|
18495
18523
|
launchEnv: launchContext?.env,
|
|
18496
18524
|
chromeEnabled: launchContext?.chromeEnabled,
|
|
18525
|
+
playwrightEnabled: launchContext?.playwrightEnabled,
|
|
18497
18526
|
logger: this.logger,
|
|
18498
18527
|
queryFactory: this.queryFactory
|
|
18499
18528
|
});
|
|
@@ -18512,6 +18541,7 @@ var ClaudeAgentClient = class {
|
|
|
18512
18541
|
handle,
|
|
18513
18542
|
launchEnv: launchContext?.env,
|
|
18514
18543
|
chromeEnabled: launchContext?.chromeEnabled,
|
|
18544
|
+
playwrightEnabled: launchContext?.playwrightEnabled,
|
|
18515
18545
|
logger: this.logger,
|
|
18516
18546
|
queryFactory: this.queryFactory
|
|
18517
18547
|
});
|
|
@@ -18826,6 +18856,7 @@ var ClaudeAgentSession = class {
|
|
|
18826
18856
|
this.config = config;
|
|
18827
18857
|
this.launchEnv = options.launchEnv;
|
|
18828
18858
|
this.chromeEnabled = options.chromeEnabled ?? true;
|
|
18859
|
+
this.playwrightEnabled = options.playwrightEnabled ?? true;
|
|
18829
18860
|
this.defaults = options.defaults;
|
|
18830
18861
|
this.runtimeSettings = options.runtimeSettings;
|
|
18831
18862
|
this.logger = options.logger;
|
|
@@ -19589,7 +19620,8 @@ var ClaudeAgentSession = class {
|
|
|
19589
19620
|
} else {
|
|
19590
19621
|
const homeMcps = loadHomeMcpServers(this.logger);
|
|
19591
19622
|
if (homeMcps) {
|
|
19592
|
-
|
|
19623
|
+
const filtered = this.playwrightEnabled ? homeMcps : Object.fromEntries(Object.entries(homeMcps).filter(([k]) => k !== "playwright"));
|
|
19624
|
+
base.mcpServers = this.normalizeMcpServers(filtered);
|
|
19593
19625
|
}
|
|
19594
19626
|
}
|
|
19595
19627
|
if (this.config.model) {
|
|
@@ -20727,6 +20759,12 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20727
20759
|
buildStructuredToolResult(server, tool4, output, input) {
|
|
20728
20760
|
const normalizedServer = server.toLowerCase();
|
|
20729
20761
|
const normalizedTool = tool4.toLowerCase();
|
|
20762
|
+
if (normalizedTool === "mcp__playwright__browser_take_screenshot" || normalizedTool === "browser_take_screenshot") {
|
|
20763
|
+
const imageOutput = this.tryInlinePlaywrightScreenshot(output, input);
|
|
20764
|
+
if (imageOutput) {
|
|
20765
|
+
return { output: imageOutput };
|
|
20766
|
+
}
|
|
20767
|
+
}
|
|
20730
20768
|
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))) {
|
|
20731
20769
|
const command = this.extractCommandText(input ?? {}) ?? "command";
|
|
20732
20770
|
return {
|
|
@@ -20768,8 +20806,46 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20768
20806
|
};
|
|
20769
20807
|
}
|
|
20770
20808
|
}
|
|
20809
|
+
if (normalizedServer === "playwright" && normalizedTool === "browser_take_screenshot") {
|
|
20810
|
+
const imageDetail = this.tryReadPlaywrightScreenshot(output, input);
|
|
20811
|
+
if (imageDetail) return imageDetail;
|
|
20812
|
+
}
|
|
20771
20813
|
return void 0;
|
|
20772
20814
|
}
|
|
20815
|
+
/**
|
|
20816
|
+
* Playwright MCP's `browser_take_screenshot` saves the image to a local file
|
|
20817
|
+
* and returns a markdown link (`[Screenshot of viewport](./file.png)`).
|
|
20818
|
+
* Extract the path, resolve it against the agent CWD, read the bytes, and
|
|
20819
|
+
* return an image ToolCallDetail so the client renders it inline.
|
|
20820
|
+
*/
|
|
20821
|
+
tryReadPlaywrightScreenshot(output, input) {
|
|
20822
|
+
let filePath;
|
|
20823
|
+
if (input && typeof input.filename === "string") {
|
|
20824
|
+
filePath = input.filename;
|
|
20825
|
+
}
|
|
20826
|
+
if (!filePath) {
|
|
20827
|
+
const match = output.match(/\[Screenshot[^\]]*\]\(([^)]+)\)/i);
|
|
20828
|
+
if (match?.[1]) {
|
|
20829
|
+
filePath = match[1];
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
if (!filePath) return void 0;
|
|
20833
|
+
const cwd = this.config.cwd;
|
|
20834
|
+
if (!cwd) return void 0;
|
|
20835
|
+
const abs = path9.isAbsolute(filePath) ? filePath : path9.resolve(cwd, filePath);
|
|
20836
|
+
try {
|
|
20837
|
+
const buf = fs4.readFileSync(abs);
|
|
20838
|
+
const ext = path9.extname(abs).toLowerCase();
|
|
20839
|
+
const mime = ext === ".jpeg" || ext === ".jpg" ? "image/jpeg" : "image/png";
|
|
20840
|
+
return {
|
|
20841
|
+
type: "image",
|
|
20842
|
+
toolName: "mcp__playwright__browser_take_screenshot",
|
|
20843
|
+
images: [{ mimeType: mime, data: buf.toString("base64") }]
|
|
20844
|
+
};
|
|
20845
|
+
} catch {
|
|
20846
|
+
return void 0;
|
|
20847
|
+
}
|
|
20848
|
+
}
|
|
20773
20849
|
mapPartialEvent(event, options) {
|
|
20774
20850
|
if (event.type === "content_block_start") {
|
|
20775
20851
|
const block = isClaudeContentChunk2(event.content_block) ? event.content_block : null;
|
|
@@ -20942,6 +21018,44 @@ ${error.stack ?? ""}` : JSON.stringify(error);
|
|
|
20942
21018
|
}
|
|
20943
21019
|
return input;
|
|
20944
21020
|
}
|
|
21021
|
+
/**
|
|
21022
|
+
* Playwright MCP returns `### Result\n- [Screenshot of viewport](./foo.png)`
|
|
21023
|
+
* and writes the file to disk instead of inlining the image. Resolve that
|
|
21024
|
+
* path against the agent cwd and turn the file into an Anthropic-style
|
|
21025
|
+
* image content block array, so downstream `extractImageToolDetail`
|
|
21026
|
+
* promotes the tool call to a renderable `image` detail.
|
|
21027
|
+
*
|
|
21028
|
+
* Returns `null` when the path cannot be resolved/read safely; the caller
|
|
21029
|
+
* then falls back to the default text output behavior.
|
|
21030
|
+
*/
|
|
21031
|
+
tryInlinePlaywrightScreenshot(output, input) {
|
|
21032
|
+
const cwd = this.config.cwd;
|
|
21033
|
+
if (!cwd) return null;
|
|
21034
|
+
let relPath;
|
|
21035
|
+
if (input && typeof input.filename === "string" && input.filename.length > 0) {
|
|
21036
|
+
relPath = input.filename;
|
|
21037
|
+
} else {
|
|
21038
|
+
const match = output.match(/\[Screenshot[^\]]*\]\(([^)]+)\)/);
|
|
21039
|
+
if (match?.[1]) {
|
|
21040
|
+
relPath = match[1];
|
|
21041
|
+
}
|
|
21042
|
+
}
|
|
21043
|
+
if (!relPath) return null;
|
|
21044
|
+
const resolved = path9.resolve(cwd, relPath);
|
|
21045
|
+
const cwdReal = path9.resolve(cwd);
|
|
21046
|
+
if (!resolved.startsWith(cwdReal + path9.sep) && resolved !== cwdReal) {
|
|
21047
|
+
return null;
|
|
21048
|
+
}
|
|
21049
|
+
let buf;
|
|
21050
|
+
try {
|
|
21051
|
+
buf = fs4.readFileSync(resolved);
|
|
21052
|
+
} catch {
|
|
21053
|
+
return null;
|
|
21054
|
+
}
|
|
21055
|
+
const ext = path9.extname(resolved).toLowerCase();
|
|
21056
|
+
const mimeType = ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : ext === ".webp" ? "image/webp" : ext === ".gif" ? "image/gif" : "image/png";
|
|
21057
|
+
return [{ type: "image", mimeType, data: buf.toString("base64") }];
|
|
21058
|
+
}
|
|
20945
21059
|
areToolInputsEqual(left, right) {
|
|
20946
21060
|
if (!left) {
|
|
20947
21061
|
return false;
|
|
@@ -31425,6 +31539,330 @@ function removeClaudeProfile(username, logger) {
|
|
|
31425
31539
|
import { z as z34 } from "zod";
|
|
31426
31540
|
import { getSessionMessages } from "@anthropic-ai/claude-agent-sdk";
|
|
31427
31541
|
|
|
31542
|
+
// ../server/src/server/skills/scanner.ts
|
|
31543
|
+
import fs8 from "node:fs/promises";
|
|
31544
|
+
import os6 from "node:os";
|
|
31545
|
+
import path14 from "node:path";
|
|
31546
|
+
var NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
31547
|
+
function homeDir() {
|
|
31548
|
+
return process.env.HOME || os6.homedir();
|
|
31549
|
+
}
|
|
31550
|
+
function codexHomeDir() {
|
|
31551
|
+
return process.env.CODEX_HOME || path14.join(homeDir(), ".codex");
|
|
31552
|
+
}
|
|
31553
|
+
function resolveScopeDir(provider, scope, workspaceRoot) {
|
|
31554
|
+
if (scope === "codex-prompts") {
|
|
31555
|
+
if (provider !== "codex") {
|
|
31556
|
+
throw new Error(`Scope "codex-prompts" is only valid for provider "codex"`);
|
|
31557
|
+
}
|
|
31558
|
+
return path14.join(codexHomeDir(), "prompts");
|
|
31559
|
+
}
|
|
31560
|
+
if (scope === "project") {
|
|
31561
|
+
if (!workspaceRoot) {
|
|
31562
|
+
throw new Error(`workspaceRoot is required for scope "project"`);
|
|
31563
|
+
}
|
|
31564
|
+
const dotDir = provider === "claude" ? ".claude" : ".codex";
|
|
31565
|
+
return path14.join(workspaceRoot, dotDir, "skills");
|
|
31566
|
+
}
|
|
31567
|
+
if (provider === "claude") {
|
|
31568
|
+
return path14.join(homeDir(), ".claude", "skills");
|
|
31569
|
+
}
|
|
31570
|
+
return path14.join(codexHomeDir(), "skills");
|
|
31571
|
+
}
|
|
31572
|
+
function allowedRoots(workspaceRoot) {
|
|
31573
|
+
const roots = [
|
|
31574
|
+
path14.join(homeDir(), ".claude", "skills"),
|
|
31575
|
+
path14.join(codexHomeDir(), "skills"),
|
|
31576
|
+
path14.join(codexHomeDir(), "prompts")
|
|
31577
|
+
];
|
|
31578
|
+
if (workspaceRoot) {
|
|
31579
|
+
roots.push(path14.join(workspaceRoot, ".claude", "skills"));
|
|
31580
|
+
roots.push(path14.join(workspaceRoot, ".codex", "skills"));
|
|
31581
|
+
}
|
|
31582
|
+
return roots.map((r) => path14.resolve(r));
|
|
31583
|
+
}
|
|
31584
|
+
function isInsideAllowedRoot(absPath, workspaceRoot) {
|
|
31585
|
+
const resolved = path14.resolve(absPath);
|
|
31586
|
+
for (const root of allowedRoots(workspaceRoot)) {
|
|
31587
|
+
const rel = path14.relative(root, resolved);
|
|
31588
|
+
if (rel === "" || !rel.startsWith("..") && !path14.isAbsolute(rel)) {
|
|
31589
|
+
return true;
|
|
31590
|
+
}
|
|
31591
|
+
}
|
|
31592
|
+
return false;
|
|
31593
|
+
}
|
|
31594
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
31595
|
+
function parseSkillFile(text) {
|
|
31596
|
+
const match = FRONTMATTER_RE.exec(text);
|
|
31597
|
+
if (!match) {
|
|
31598
|
+
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
31599
|
+
}
|
|
31600
|
+
const yamlBlock = match[1] ?? "";
|
|
31601
|
+
const body = match[2] ?? "";
|
|
31602
|
+
return {
|
|
31603
|
+
rawFrontmatterLines: yamlBlock.split(/\r?\n/),
|
|
31604
|
+
body,
|
|
31605
|
+
hadFrontmatter: true
|
|
31606
|
+
};
|
|
31607
|
+
}
|
|
31608
|
+
function readDescription(text) {
|
|
31609
|
+
const parsed = parseSkillFile(text);
|
|
31610
|
+
if (!parsed.hadFrontmatter) return "";
|
|
31611
|
+
for (const line of parsed.rawFrontmatterLines) {
|
|
31612
|
+
const m = /^description\s*:\s*(.*)$/.exec(line);
|
|
31613
|
+
if (m) {
|
|
31614
|
+
const value = (m[1] ?? "").trim();
|
|
31615
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
31616
|
+
return value.slice(1, -1);
|
|
31617
|
+
}
|
|
31618
|
+
return value;
|
|
31619
|
+
}
|
|
31620
|
+
}
|
|
31621
|
+
return "";
|
|
31622
|
+
}
|
|
31623
|
+
function findOwnedSpans(lines) {
|
|
31624
|
+
const spans = [];
|
|
31625
|
+
let i = 0;
|
|
31626
|
+
while (i < lines.length) {
|
|
31627
|
+
const line = lines[i] ?? "";
|
|
31628
|
+
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
31629
|
+
if (!keyMatch) {
|
|
31630
|
+
i++;
|
|
31631
|
+
continue;
|
|
31632
|
+
}
|
|
31633
|
+
const key = keyMatch[1];
|
|
31634
|
+
const value = keyMatch[2];
|
|
31635
|
+
if (key !== "description" && key !== "argument-hint" && key !== "allowed-tools") {
|
|
31636
|
+
i++;
|
|
31637
|
+
continue;
|
|
31638
|
+
}
|
|
31639
|
+
let end = i + 1;
|
|
31640
|
+
if (value === "") {
|
|
31641
|
+
while (end < lines.length) {
|
|
31642
|
+
const next = lines[end] ?? "";
|
|
31643
|
+
if (/^\s+-\s+/.test(next) || /^\s*$/.test(next)) {
|
|
31644
|
+
end++;
|
|
31645
|
+
} else {
|
|
31646
|
+
break;
|
|
31647
|
+
}
|
|
31648
|
+
}
|
|
31649
|
+
}
|
|
31650
|
+
spans.push({ key, startLine: i, endLine: end });
|
|
31651
|
+
i = end;
|
|
31652
|
+
}
|
|
31653
|
+
return spans;
|
|
31654
|
+
}
|
|
31655
|
+
function emitFrontmatterValue(key, value) {
|
|
31656
|
+
if (key === "allowed-tools") {
|
|
31657
|
+
if (!Array.isArray(value) || value.length === 0) return [];
|
|
31658
|
+
return [`allowed-tools:`, ...value.map((v) => ` - ${v}`)];
|
|
31659
|
+
}
|
|
31660
|
+
const text = typeof value === "string" ? value : "";
|
|
31661
|
+
if (text.length === 0) return [];
|
|
31662
|
+
const needsQuote = /[:#\n]/.test(text) || text.startsWith(" ") || text.endsWith(" ");
|
|
31663
|
+
const formatted = needsQuote ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
31664
|
+
return [`${key}: ${formatted}`];
|
|
31665
|
+
}
|
|
31666
|
+
function rewriteFrontmatter(originalLines, next) {
|
|
31667
|
+
const spans = findOwnedSpans(originalLines);
|
|
31668
|
+
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
31669
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
31670
|
+
if (next.description !== void 0) {
|
|
31671
|
+
replacements.set("description", emitFrontmatterValue("description", next.description));
|
|
31672
|
+
}
|
|
31673
|
+
if (next.argumentHint !== void 0) {
|
|
31674
|
+
replacements.set("argument-hint", emitFrontmatterValue("argument-hint", next.argumentHint));
|
|
31675
|
+
}
|
|
31676
|
+
if (next.allowedTools !== void 0) {
|
|
31677
|
+
replacements.set("allowed-tools", emitFrontmatterValue("allowed-tools", next.allowedTools));
|
|
31678
|
+
}
|
|
31679
|
+
const out = [];
|
|
31680
|
+
let i = 0;
|
|
31681
|
+
while (i < originalLines.length) {
|
|
31682
|
+
const span = spans.find((s) => s.startLine === i);
|
|
31683
|
+
if (span) {
|
|
31684
|
+
if (replacements.has(span.key)) {
|
|
31685
|
+
out.push(...replacements.get(span.key) ?? []);
|
|
31686
|
+
} else {
|
|
31687
|
+
for (let j = span.startLine; j < span.endLine; j++) {
|
|
31688
|
+
out.push(originalLines[j] ?? "");
|
|
31689
|
+
}
|
|
31690
|
+
}
|
|
31691
|
+
i = span.endLine;
|
|
31692
|
+
continue;
|
|
31693
|
+
}
|
|
31694
|
+
out.push(originalLines[i] ?? "");
|
|
31695
|
+
i++;
|
|
31696
|
+
}
|
|
31697
|
+
for (const key of ["description", "argument-hint", "allowed-tools"]) {
|
|
31698
|
+
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
31699
|
+
out.push(...replacements.get(key) ?? []);
|
|
31700
|
+
}
|
|
31701
|
+
}
|
|
31702
|
+
return out;
|
|
31703
|
+
}
|
|
31704
|
+
async function listSkills(args) {
|
|
31705
|
+
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
31706
|
+
let entries;
|
|
31707
|
+
try {
|
|
31708
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
31709
|
+
} catch {
|
|
31710
|
+
return [];
|
|
31711
|
+
}
|
|
31712
|
+
const results = [];
|
|
31713
|
+
if (args.scope === "codex-prompts") {
|
|
31714
|
+
for (const entry of entries) {
|
|
31715
|
+
if (!entry.isFile()) continue;
|
|
31716
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
31717
|
+
const name = entry.name.slice(0, -".md".length);
|
|
31718
|
+
if (!name) continue;
|
|
31719
|
+
const fullPath = path14.join(dir, entry.name);
|
|
31720
|
+
const stat5 = await safeStat(fullPath);
|
|
31721
|
+
if (!stat5) continue;
|
|
31722
|
+
const description = await readDescriptionSafely(fullPath);
|
|
31723
|
+
results.push({
|
|
31724
|
+
id: `${args.provider}:${args.scope}:${name}`,
|
|
31725
|
+
provider: args.provider,
|
|
31726
|
+
scope: args.scope,
|
|
31727
|
+
name,
|
|
31728
|
+
path: fullPath,
|
|
31729
|
+
description,
|
|
31730
|
+
modifiedAt: stat5.mtime.toISOString(),
|
|
31731
|
+
size: stat5.size
|
|
31732
|
+
});
|
|
31733
|
+
}
|
|
31734
|
+
} else {
|
|
31735
|
+
for (const entry of entries) {
|
|
31736
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
31737
|
+
const skillDir = path14.join(dir, entry.name);
|
|
31738
|
+
const skillPath = path14.join(skillDir, "SKILL.md");
|
|
31739
|
+
const stat5 = await safeStat(skillPath);
|
|
31740
|
+
if (!stat5) continue;
|
|
31741
|
+
const description = await readDescriptionSafely(skillPath);
|
|
31742
|
+
results.push({
|
|
31743
|
+
id: `${args.provider}:${args.scope}:${entry.name}`,
|
|
31744
|
+
provider: args.provider,
|
|
31745
|
+
scope: args.scope,
|
|
31746
|
+
name: entry.name,
|
|
31747
|
+
path: skillPath,
|
|
31748
|
+
description,
|
|
31749
|
+
modifiedAt: stat5.mtime.toISOString(),
|
|
31750
|
+
size: stat5.size
|
|
31751
|
+
});
|
|
31752
|
+
}
|
|
31753
|
+
}
|
|
31754
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
31755
|
+
return results;
|
|
31756
|
+
}
|
|
31757
|
+
async function safeStat(filePath) {
|
|
31758
|
+
try {
|
|
31759
|
+
const s = await fs8.stat(filePath);
|
|
31760
|
+
return { mtime: s.mtime, size: s.size };
|
|
31761
|
+
} catch {
|
|
31762
|
+
return null;
|
|
31763
|
+
}
|
|
31764
|
+
}
|
|
31765
|
+
async function readDescriptionSafely(filePath) {
|
|
31766
|
+
try {
|
|
31767
|
+
const text = await fs8.readFile(filePath, "utf8");
|
|
31768
|
+
return readDescription(text);
|
|
31769
|
+
} catch {
|
|
31770
|
+
return "";
|
|
31771
|
+
}
|
|
31772
|
+
}
|
|
31773
|
+
async function createSkill(args) {
|
|
31774
|
+
if (!NAME_REGEX.test(args.name)) {
|
|
31775
|
+
throw new Error(
|
|
31776
|
+
`Invalid skill name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
31777
|
+
);
|
|
31778
|
+
}
|
|
31779
|
+
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
31780
|
+
throw new Error(`Skill name must not contain path separators or "..".`);
|
|
31781
|
+
}
|
|
31782
|
+
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
31783
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
31784
|
+
if (args.scope === "codex-prompts") {
|
|
31785
|
+
const filePath2 = path14.join(dir, `${args.name}.md`);
|
|
31786
|
+
try {
|
|
31787
|
+
await fs8.access(filePath2);
|
|
31788
|
+
throw new Error(`A prompt named "${args.name}" already exists at ${filePath2}`);
|
|
31789
|
+
} catch (err) {
|
|
31790
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
31791
|
+
throw err;
|
|
31792
|
+
}
|
|
31793
|
+
if (err instanceof Error && err.message.includes("already exists")) {
|
|
31794
|
+
throw err;
|
|
31795
|
+
}
|
|
31796
|
+
}
|
|
31797
|
+
const initial2 = buildStarterPrompt(args.name);
|
|
31798
|
+
await fs8.writeFile(filePath2, initial2, "utf8");
|
|
31799
|
+
return { path: filePath2 };
|
|
31800
|
+
}
|
|
31801
|
+
const skillDir = path14.join(dir, args.name);
|
|
31802
|
+
let dirExists = false;
|
|
31803
|
+
try {
|
|
31804
|
+
const stat5 = await fs8.stat(skillDir);
|
|
31805
|
+
dirExists = stat5.isDirectory();
|
|
31806
|
+
} catch {
|
|
31807
|
+
dirExists = false;
|
|
31808
|
+
}
|
|
31809
|
+
if (dirExists) {
|
|
31810
|
+
throw new Error(`A skill named "${args.name}" already exists at ${skillDir}`);
|
|
31811
|
+
}
|
|
31812
|
+
await fs8.mkdir(skillDir, { recursive: true });
|
|
31813
|
+
const filePath = path14.join(skillDir, "SKILL.md");
|
|
31814
|
+
const initial = buildStarterSkill(args.name);
|
|
31815
|
+
await fs8.writeFile(filePath, initial, "utf8");
|
|
31816
|
+
return { path: filePath };
|
|
31817
|
+
}
|
|
31818
|
+
function buildStarterSkill(name) {
|
|
31819
|
+
return `---
|
|
31820
|
+
name: ${name}
|
|
31821
|
+
description: ""
|
|
31822
|
+
---
|
|
31823
|
+
|
|
31824
|
+
# ${name}
|
|
31825
|
+
|
|
31826
|
+
Describe when this skill should be used and what it does. The body of this
|
|
31827
|
+
file is loaded into the agent's context when the skill is invoked.
|
|
31828
|
+
`;
|
|
31829
|
+
}
|
|
31830
|
+
function buildStarterPrompt(name) {
|
|
31831
|
+
return `---
|
|
31832
|
+
description: ""
|
|
31833
|
+
argument-hint: ""
|
|
31834
|
+
---
|
|
31835
|
+
|
|
31836
|
+
# ${name}
|
|
31837
|
+
|
|
31838
|
+
Body of the prompt. Use \`$1\`, \`$2\`, ... or \`$ARGUMENTS\` for parameter expansion.
|
|
31839
|
+
`;
|
|
31840
|
+
}
|
|
31841
|
+
async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
31842
|
+
if (!path14.isAbsolute(args.path)) {
|
|
31843
|
+
throw new Error(`writeSkillFrontmatter expects an absolute path; got "${args.path}"`);
|
|
31844
|
+
}
|
|
31845
|
+
if (!isInsideAllowedRoot(args.path, workspaceRoot)) {
|
|
31846
|
+
throw new Error(`Path "${args.path}" is not inside an allowlisted skill root`);
|
|
31847
|
+
}
|
|
31848
|
+
let original;
|
|
31849
|
+
try {
|
|
31850
|
+
original = await fs8.readFile(args.path, "utf8");
|
|
31851
|
+
} catch (err) {
|
|
31852
|
+
throw new Error(
|
|
31853
|
+
`Failed to read skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
31854
|
+
);
|
|
31855
|
+
}
|
|
31856
|
+
const parsed = parseSkillFile(original);
|
|
31857
|
+
const newLines = rewriteFrontmatter(parsed.rawFrontmatterLines, args.frontmatter);
|
|
31858
|
+
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
31859
|
+
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
31860
|
+
${parsed.body}` : `${nextFrontmatter}
|
|
31861
|
+
|
|
31862
|
+
${original}`;
|
|
31863
|
+
await fs8.writeFile(args.path, nextContent, "utf8");
|
|
31864
|
+
}
|
|
31865
|
+
|
|
31428
31866
|
// ../server/src/server/agent/handoff-mcp.ts
|
|
31429
31867
|
import { createSdkMcpServer, tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
31430
31868
|
import { z as z33 } from "zod";
|
|
@@ -32397,8 +32835,8 @@ function isVoicePermissionAllowed(request) {
|
|
|
32397
32835
|
}
|
|
32398
32836
|
|
|
32399
32837
|
// ../server/src/server/file-explorer/service.ts
|
|
32400
|
-
import { promises as
|
|
32401
|
-
import
|
|
32838
|
+
import { promises as fs9 } from "fs";
|
|
32839
|
+
import path15 from "path";
|
|
32402
32840
|
|
|
32403
32841
|
// ../server/src/server/path-utils.ts
|
|
32404
32842
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -32445,14 +32883,14 @@ async function listDirectoryEntries({
|
|
|
32445
32883
|
relativePath = "."
|
|
32446
32884
|
}) {
|
|
32447
32885
|
const directoryPath = await resolveScopedPath({ root, relativePath });
|
|
32448
|
-
const stats = await
|
|
32886
|
+
const stats = await fs9.stat(directoryPath);
|
|
32449
32887
|
if (!stats.isDirectory()) {
|
|
32450
32888
|
throw new Error("Requested path is not a directory");
|
|
32451
32889
|
}
|
|
32452
|
-
const dirents = await
|
|
32890
|
+
const dirents = await fs9.readdir(directoryPath, { withFileTypes: true });
|
|
32453
32891
|
const entriesWithNulls = await Promise.all(
|
|
32454
32892
|
dirents.map(async (dirent) => {
|
|
32455
|
-
const targetPath =
|
|
32893
|
+
const targetPath = path15.join(directoryPath, dirent.name);
|
|
32456
32894
|
const kind = dirent.isDirectory() ? "directory" : "file";
|
|
32457
32895
|
try {
|
|
32458
32896
|
return await buildEntryPayload({
|
|
@@ -32487,18 +32925,18 @@ async function readExplorerFile({
|
|
|
32487
32925
|
relativePath
|
|
32488
32926
|
}) {
|
|
32489
32927
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
32490
|
-
const stats = await
|
|
32928
|
+
const stats = await fs9.stat(filePath);
|
|
32491
32929
|
if (!stats.isFile()) {
|
|
32492
32930
|
throw new Error("Requested path is not a file");
|
|
32493
32931
|
}
|
|
32494
|
-
const ext =
|
|
32932
|
+
const ext = path15.extname(filePath).toLowerCase();
|
|
32495
32933
|
const basePayload = {
|
|
32496
32934
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
32497
32935
|
size: stats.size,
|
|
32498
32936
|
modifiedAt: stats.mtime.toISOString()
|
|
32499
32937
|
};
|
|
32500
32938
|
if (ext in IMAGE_MIME_TYPES) {
|
|
32501
|
-
const buffer2 = await
|
|
32939
|
+
const buffer2 = await fs9.readFile(filePath);
|
|
32502
32940
|
return {
|
|
32503
32941
|
...basePayload,
|
|
32504
32942
|
kind: "image",
|
|
@@ -32507,7 +32945,7 @@ async function readExplorerFile({
|
|
|
32507
32945
|
mimeType: IMAGE_MIME_TYPES[ext]
|
|
32508
32946
|
};
|
|
32509
32947
|
}
|
|
32510
|
-
const buffer = await
|
|
32948
|
+
const buffer = await fs9.readFile(filePath);
|
|
32511
32949
|
if (isLikelyBinary(buffer)) {
|
|
32512
32950
|
return {
|
|
32513
32951
|
...basePayload,
|
|
@@ -32529,34 +32967,34 @@ async function writeTextFile({
|
|
|
32529
32967
|
relativePath,
|
|
32530
32968
|
content
|
|
32531
32969
|
}) {
|
|
32532
|
-
const ext =
|
|
32970
|
+
const ext = path15.extname(relativePath).toLowerCase();
|
|
32533
32971
|
if (ext in IMAGE_MIME_TYPES) {
|
|
32534
32972
|
throw new Error(`Refusing to write '${relativePath}': binary/image file`);
|
|
32535
32973
|
}
|
|
32536
32974
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
32537
32975
|
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
32538
|
-
await
|
|
32539
|
-
await
|
|
32976
|
+
await fs9.writeFile(tempPath, content, "utf8");
|
|
32977
|
+
await fs9.rename(tempPath, filePath);
|
|
32540
32978
|
}
|
|
32541
32979
|
async function deleteFile({ root, relativePath }) {
|
|
32542
|
-
const ext =
|
|
32980
|
+
const ext = path15.extname(relativePath).toLowerCase();
|
|
32543
32981
|
if (ext !== ".md") {
|
|
32544
32982
|
throw new Error(`Refusing to delete '${relativePath}': only .md files allowed`);
|
|
32545
32983
|
}
|
|
32546
32984
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
32547
|
-
const stats = await
|
|
32985
|
+
const stats = await fs9.stat(filePath);
|
|
32548
32986
|
if (!stats.isFile()) {
|
|
32549
32987
|
throw new Error("Requested path is not a file");
|
|
32550
32988
|
}
|
|
32551
|
-
await
|
|
32989
|
+
await fs9.unlink(filePath);
|
|
32552
32990
|
}
|
|
32553
32991
|
async function deleteEntry({ root, relativePath }) {
|
|
32554
32992
|
const entryPath = await resolveScopedPath({ root, relativePath });
|
|
32555
|
-
await
|
|
32993
|
+
await fs9.rm(entryPath, { recursive: true, force: false });
|
|
32556
32994
|
}
|
|
32557
32995
|
async function createDirectory({ root, relativePath }) {
|
|
32558
32996
|
const dirPath = await resolveScopedPath({ root, relativePath });
|
|
32559
|
-
await
|
|
32997
|
+
await fs9.mkdir(dirPath, { recursive: true });
|
|
32560
32998
|
}
|
|
32561
32999
|
async function moveEntry({
|
|
32562
33000
|
root,
|
|
@@ -32565,22 +33003,22 @@ async function moveEntry({
|
|
|
32565
33003
|
}) {
|
|
32566
33004
|
const src = await resolveScopedPath({ root, relativePath: sourcePath });
|
|
32567
33005
|
const dest = await resolveScopedPath({ root, relativePath: destinationPath });
|
|
32568
|
-
await
|
|
33006
|
+
await fs9.rename(src, dest);
|
|
32569
33007
|
}
|
|
32570
33008
|
async function getDownloadableFileInfo({ root, relativePath }) {
|
|
32571
33009
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
32572
|
-
const stats = await
|
|
33010
|
+
const stats = await fs9.stat(filePath);
|
|
32573
33011
|
if (!stats.isFile()) {
|
|
32574
33012
|
throw new Error("Requested path is not a file");
|
|
32575
33013
|
}
|
|
32576
|
-
const ext =
|
|
33014
|
+
const ext = path15.extname(filePath).toLowerCase();
|
|
32577
33015
|
let mimeType = "application/octet-stream";
|
|
32578
33016
|
if (ext in IMAGE_MIME_TYPES) {
|
|
32579
33017
|
mimeType = IMAGE_MIME_TYPES[ext] ?? mimeType;
|
|
32580
33018
|
} else if (ext in FONT_MIME_TYPES) {
|
|
32581
33019
|
mimeType = FONT_MIME_TYPES[ext] ?? mimeType;
|
|
32582
33020
|
} else {
|
|
32583
|
-
const handle = await
|
|
33021
|
+
const handle = await fs9.open(filePath, "r");
|
|
32584
33022
|
const sample = Buffer.alloc(8192);
|
|
32585
33023
|
try {
|
|
32586
33024
|
const { bytesRead } = await handle.read(sample, 0, sample.length, 0);
|
|
@@ -32595,23 +33033,23 @@ async function getDownloadableFileInfo({ root, relativePath }) {
|
|
|
32595
33033
|
return {
|
|
32596
33034
|
path: normalizeRelativePath({ root, targetPath: filePath }),
|
|
32597
33035
|
absolutePath: filePath,
|
|
32598
|
-
fileName:
|
|
33036
|
+
fileName: path15.basename(filePath),
|
|
32599
33037
|
mimeType,
|
|
32600
33038
|
size: stats.size
|
|
32601
33039
|
};
|
|
32602
33040
|
}
|
|
32603
33041
|
async function resolveScopedPath({ root, relativePath = "." }) {
|
|
32604
|
-
const normalizedRoot =
|
|
33042
|
+
const normalizedRoot = path15.resolve(root);
|
|
32605
33043
|
const requestedPath = resolvePathFromBase(normalizedRoot, relativePath);
|
|
32606
|
-
const relative =
|
|
32607
|
-
if (relative !== "" && (relative.startsWith("..") ||
|
|
33044
|
+
const relative = path15.relative(normalizedRoot, requestedPath);
|
|
33045
|
+
if (relative !== "" && (relative.startsWith("..") || path15.isAbsolute(relative))) {
|
|
32608
33046
|
throw new Error("Access outside of workspace is not allowed");
|
|
32609
33047
|
}
|
|
32610
|
-
const realRoot = await
|
|
33048
|
+
const realRoot = await fs9.realpath(normalizedRoot);
|
|
32611
33049
|
try {
|
|
32612
|
-
const realPath = await
|
|
32613
|
-
const realRelative =
|
|
32614
|
-
if (realRelative !== "" && (realRelative.startsWith("..") ||
|
|
33050
|
+
const realPath = await fs9.realpath(requestedPath);
|
|
33051
|
+
const realRelative = path15.relative(realRoot, realPath);
|
|
33052
|
+
if (realRelative !== "" && (realRelative.startsWith("..") || path15.isAbsolute(realRelative))) {
|
|
32615
33053
|
throw new Error("Access outside of workspace is not allowed");
|
|
32616
33054
|
}
|
|
32617
33055
|
return requestedPath;
|
|
@@ -32628,7 +33066,7 @@ async function buildEntryPayload({
|
|
|
32628
33066
|
name,
|
|
32629
33067
|
kind
|
|
32630
33068
|
}) {
|
|
32631
|
-
const stats = await
|
|
33069
|
+
const stats = await fs9.stat(targetPath);
|
|
32632
33070
|
return {
|
|
32633
33071
|
name,
|
|
32634
33072
|
path: normalizeRelativePath({ root, targetPath }),
|
|
@@ -32642,10 +33080,10 @@ function isMissingEntryError(error) {
|
|
|
32642
33080
|
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
32643
33081
|
}
|
|
32644
33082
|
function normalizeRelativePath({ root, targetPath }) {
|
|
32645
|
-
const normalizedRoot =
|
|
32646
|
-
const normalizedTarget =
|
|
32647
|
-
const relative =
|
|
32648
|
-
return relative === "" ? "." : relative.split(
|
|
33083
|
+
const normalizedRoot = path15.resolve(root);
|
|
33084
|
+
const normalizedTarget = path15.resolve(targetPath);
|
|
33085
|
+
const relative = path15.relative(normalizedRoot, normalizedTarget);
|
|
33086
|
+
return relative === "" ? "." : relative.split(path15.sep).join("/");
|
|
32649
33087
|
}
|
|
32650
33088
|
function textMimeTypeForExtension(ext) {
|
|
32651
33089
|
return TEXT_MIME_TYPES[ext] ?? DEFAULT_TEXT_MIME_TYPE;
|
|
@@ -32998,342 +33436,18 @@ async function getProjectIcon(projectDir) {
|
|
|
32998
33436
|
}
|
|
32999
33437
|
|
|
33000
33438
|
// ../server/src/utils/path.ts
|
|
33001
|
-
import
|
|
33439
|
+
import os7 from "os";
|
|
33002
33440
|
function expandTilde(path29) {
|
|
33003
33441
|
if (path29.startsWith("~/")) {
|
|
33004
|
-
const homeDir3 = process.env.HOME ||
|
|
33442
|
+
const homeDir3 = process.env.HOME || os7.homedir();
|
|
33005
33443
|
return path29.replace("~", homeDir3);
|
|
33006
33444
|
}
|
|
33007
33445
|
if (path29 === "~") {
|
|
33008
|
-
return process.env.HOME ||
|
|
33446
|
+
return process.env.HOME || os7.homedir();
|
|
33009
33447
|
}
|
|
33010
33448
|
return path29;
|
|
33011
33449
|
}
|
|
33012
33450
|
|
|
33013
|
-
// ../server/src/server/skills/scanner.ts
|
|
33014
|
-
import fs9 from "node:fs/promises";
|
|
33015
|
-
import os7 from "node:os";
|
|
33016
|
-
import path15 from "node:path";
|
|
33017
|
-
var NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
33018
|
-
function homeDir() {
|
|
33019
|
-
return process.env.HOME || os7.homedir();
|
|
33020
|
-
}
|
|
33021
|
-
function codexHomeDir() {
|
|
33022
|
-
return process.env.CODEX_HOME || path15.join(homeDir(), ".codex");
|
|
33023
|
-
}
|
|
33024
|
-
function resolveScopeDir(provider, scope, workspaceRoot) {
|
|
33025
|
-
if (scope === "codex-prompts") {
|
|
33026
|
-
if (provider !== "codex") {
|
|
33027
|
-
throw new Error(`Scope "codex-prompts" is only valid for provider "codex"`);
|
|
33028
|
-
}
|
|
33029
|
-
return path15.join(codexHomeDir(), "prompts");
|
|
33030
|
-
}
|
|
33031
|
-
if (scope === "project") {
|
|
33032
|
-
if (!workspaceRoot) {
|
|
33033
|
-
throw new Error(`workspaceRoot is required for scope "project"`);
|
|
33034
|
-
}
|
|
33035
|
-
const dotDir = provider === "claude" ? ".claude" : ".codex";
|
|
33036
|
-
return path15.join(workspaceRoot, dotDir, "skills");
|
|
33037
|
-
}
|
|
33038
|
-
if (provider === "claude") {
|
|
33039
|
-
return path15.join(homeDir(), ".claude", "skills");
|
|
33040
|
-
}
|
|
33041
|
-
return path15.join(codexHomeDir(), "skills");
|
|
33042
|
-
}
|
|
33043
|
-
function allowedRoots(workspaceRoot) {
|
|
33044
|
-
const roots = [
|
|
33045
|
-
path15.join(homeDir(), ".claude", "skills"),
|
|
33046
|
-
path15.join(codexHomeDir(), "skills"),
|
|
33047
|
-
path15.join(codexHomeDir(), "prompts")
|
|
33048
|
-
];
|
|
33049
|
-
if (workspaceRoot) {
|
|
33050
|
-
roots.push(path15.join(workspaceRoot, ".claude", "skills"));
|
|
33051
|
-
roots.push(path15.join(workspaceRoot, ".codex", "skills"));
|
|
33052
|
-
}
|
|
33053
|
-
return roots.map((r) => path15.resolve(r));
|
|
33054
|
-
}
|
|
33055
|
-
function isInsideAllowedRoot(absPath, workspaceRoot) {
|
|
33056
|
-
const resolved = path15.resolve(absPath);
|
|
33057
|
-
for (const root of allowedRoots(workspaceRoot)) {
|
|
33058
|
-
const rel = path15.relative(root, resolved);
|
|
33059
|
-
if (rel === "" || !rel.startsWith("..") && !path15.isAbsolute(rel)) {
|
|
33060
|
-
return true;
|
|
33061
|
-
}
|
|
33062
|
-
}
|
|
33063
|
-
return false;
|
|
33064
|
-
}
|
|
33065
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
33066
|
-
function parseSkillFile(text) {
|
|
33067
|
-
const match = FRONTMATTER_RE.exec(text);
|
|
33068
|
-
if (!match) {
|
|
33069
|
-
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
33070
|
-
}
|
|
33071
|
-
const yamlBlock = match[1] ?? "";
|
|
33072
|
-
const body = match[2] ?? "";
|
|
33073
|
-
return {
|
|
33074
|
-
rawFrontmatterLines: yamlBlock.split(/\r?\n/),
|
|
33075
|
-
body,
|
|
33076
|
-
hadFrontmatter: true
|
|
33077
|
-
};
|
|
33078
|
-
}
|
|
33079
|
-
function readDescription(text) {
|
|
33080
|
-
const parsed = parseSkillFile(text);
|
|
33081
|
-
if (!parsed.hadFrontmatter) return "";
|
|
33082
|
-
for (const line of parsed.rawFrontmatterLines) {
|
|
33083
|
-
const m = /^description\s*:\s*(.*)$/.exec(line);
|
|
33084
|
-
if (m) {
|
|
33085
|
-
const value = (m[1] ?? "").trim();
|
|
33086
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
33087
|
-
return value.slice(1, -1);
|
|
33088
|
-
}
|
|
33089
|
-
return value;
|
|
33090
|
-
}
|
|
33091
|
-
}
|
|
33092
|
-
return "";
|
|
33093
|
-
}
|
|
33094
|
-
function findOwnedSpans(lines) {
|
|
33095
|
-
const spans = [];
|
|
33096
|
-
let i = 0;
|
|
33097
|
-
while (i < lines.length) {
|
|
33098
|
-
const line = lines[i] ?? "";
|
|
33099
|
-
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
33100
|
-
if (!keyMatch) {
|
|
33101
|
-
i++;
|
|
33102
|
-
continue;
|
|
33103
|
-
}
|
|
33104
|
-
const key = keyMatch[1];
|
|
33105
|
-
const value = keyMatch[2];
|
|
33106
|
-
if (key !== "description" && key !== "argument-hint" && key !== "allowed-tools") {
|
|
33107
|
-
i++;
|
|
33108
|
-
continue;
|
|
33109
|
-
}
|
|
33110
|
-
let end = i + 1;
|
|
33111
|
-
if (value === "") {
|
|
33112
|
-
while (end < lines.length) {
|
|
33113
|
-
const next = lines[end] ?? "";
|
|
33114
|
-
if (/^\s+-\s+/.test(next) || /^\s*$/.test(next)) {
|
|
33115
|
-
end++;
|
|
33116
|
-
} else {
|
|
33117
|
-
break;
|
|
33118
|
-
}
|
|
33119
|
-
}
|
|
33120
|
-
}
|
|
33121
|
-
spans.push({ key, startLine: i, endLine: end });
|
|
33122
|
-
i = end;
|
|
33123
|
-
}
|
|
33124
|
-
return spans;
|
|
33125
|
-
}
|
|
33126
|
-
function emitFrontmatterValue(key, value) {
|
|
33127
|
-
if (key === "allowed-tools") {
|
|
33128
|
-
if (!Array.isArray(value) || value.length === 0) return [];
|
|
33129
|
-
return [`allowed-tools:`, ...value.map((v) => ` - ${v}`)];
|
|
33130
|
-
}
|
|
33131
|
-
const text = typeof value === "string" ? value : "";
|
|
33132
|
-
if (text.length === 0) return [];
|
|
33133
|
-
const needsQuote = /[:#\n]/.test(text) || text.startsWith(" ") || text.endsWith(" ");
|
|
33134
|
-
const formatted = needsQuote ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
33135
|
-
return [`${key}: ${formatted}`];
|
|
33136
|
-
}
|
|
33137
|
-
function rewriteFrontmatter(originalLines, next) {
|
|
33138
|
-
const spans = findOwnedSpans(originalLines);
|
|
33139
|
-
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
33140
|
-
const replacements = /* @__PURE__ */ new Map();
|
|
33141
|
-
if (next.description !== void 0) {
|
|
33142
|
-
replacements.set("description", emitFrontmatterValue("description", next.description));
|
|
33143
|
-
}
|
|
33144
|
-
if (next.argumentHint !== void 0) {
|
|
33145
|
-
replacements.set("argument-hint", emitFrontmatterValue("argument-hint", next.argumentHint));
|
|
33146
|
-
}
|
|
33147
|
-
if (next.allowedTools !== void 0) {
|
|
33148
|
-
replacements.set("allowed-tools", emitFrontmatterValue("allowed-tools", next.allowedTools));
|
|
33149
|
-
}
|
|
33150
|
-
const out = [];
|
|
33151
|
-
let i = 0;
|
|
33152
|
-
while (i < originalLines.length) {
|
|
33153
|
-
const span = spans.find((s) => s.startLine === i);
|
|
33154
|
-
if (span) {
|
|
33155
|
-
if (replacements.has(span.key)) {
|
|
33156
|
-
out.push(...replacements.get(span.key) ?? []);
|
|
33157
|
-
} else {
|
|
33158
|
-
for (let j = span.startLine; j < span.endLine; j++) {
|
|
33159
|
-
out.push(originalLines[j] ?? "");
|
|
33160
|
-
}
|
|
33161
|
-
}
|
|
33162
|
-
i = span.endLine;
|
|
33163
|
-
continue;
|
|
33164
|
-
}
|
|
33165
|
-
out.push(originalLines[i] ?? "");
|
|
33166
|
-
i++;
|
|
33167
|
-
}
|
|
33168
|
-
for (const key of ["description", "argument-hint", "allowed-tools"]) {
|
|
33169
|
-
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
33170
|
-
out.push(...replacements.get(key) ?? []);
|
|
33171
|
-
}
|
|
33172
|
-
}
|
|
33173
|
-
return out;
|
|
33174
|
-
}
|
|
33175
|
-
async function listSkills(args) {
|
|
33176
|
-
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
33177
|
-
let entries;
|
|
33178
|
-
try {
|
|
33179
|
-
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
33180
|
-
} catch {
|
|
33181
|
-
return [];
|
|
33182
|
-
}
|
|
33183
|
-
const results = [];
|
|
33184
|
-
if (args.scope === "codex-prompts") {
|
|
33185
|
-
for (const entry of entries) {
|
|
33186
|
-
if (!entry.isFile()) continue;
|
|
33187
|
-
if (!entry.name.endsWith(".md")) continue;
|
|
33188
|
-
const name = entry.name.slice(0, -".md".length);
|
|
33189
|
-
if (!name) continue;
|
|
33190
|
-
const fullPath = path15.join(dir, entry.name);
|
|
33191
|
-
const stat5 = await safeStat(fullPath);
|
|
33192
|
-
if (!stat5) continue;
|
|
33193
|
-
const description = await readDescriptionSafely(fullPath);
|
|
33194
|
-
results.push({
|
|
33195
|
-
id: `${args.provider}:${args.scope}:${name}`,
|
|
33196
|
-
provider: args.provider,
|
|
33197
|
-
scope: args.scope,
|
|
33198
|
-
name,
|
|
33199
|
-
path: fullPath,
|
|
33200
|
-
description,
|
|
33201
|
-
modifiedAt: stat5.mtime.toISOString(),
|
|
33202
|
-
size: stat5.size
|
|
33203
|
-
});
|
|
33204
|
-
}
|
|
33205
|
-
} else {
|
|
33206
|
-
for (const entry of entries) {
|
|
33207
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
33208
|
-
const skillDir = path15.join(dir, entry.name);
|
|
33209
|
-
const skillPath = path15.join(skillDir, "SKILL.md");
|
|
33210
|
-
const stat5 = await safeStat(skillPath);
|
|
33211
|
-
if (!stat5) continue;
|
|
33212
|
-
const description = await readDescriptionSafely(skillPath);
|
|
33213
|
-
results.push({
|
|
33214
|
-
id: `${args.provider}:${args.scope}:${entry.name}`,
|
|
33215
|
-
provider: args.provider,
|
|
33216
|
-
scope: args.scope,
|
|
33217
|
-
name: entry.name,
|
|
33218
|
-
path: skillPath,
|
|
33219
|
-
description,
|
|
33220
|
-
modifiedAt: stat5.mtime.toISOString(),
|
|
33221
|
-
size: stat5.size
|
|
33222
|
-
});
|
|
33223
|
-
}
|
|
33224
|
-
}
|
|
33225
|
-
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
33226
|
-
return results;
|
|
33227
|
-
}
|
|
33228
|
-
async function safeStat(filePath) {
|
|
33229
|
-
try {
|
|
33230
|
-
const s = await fs9.stat(filePath);
|
|
33231
|
-
return { mtime: s.mtime, size: s.size };
|
|
33232
|
-
} catch {
|
|
33233
|
-
return null;
|
|
33234
|
-
}
|
|
33235
|
-
}
|
|
33236
|
-
async function readDescriptionSafely(filePath) {
|
|
33237
|
-
try {
|
|
33238
|
-
const text = await fs9.readFile(filePath, "utf8");
|
|
33239
|
-
return readDescription(text);
|
|
33240
|
-
} catch {
|
|
33241
|
-
return "";
|
|
33242
|
-
}
|
|
33243
|
-
}
|
|
33244
|
-
async function createSkill(args) {
|
|
33245
|
-
if (!NAME_REGEX.test(args.name)) {
|
|
33246
|
-
throw new Error(
|
|
33247
|
-
`Invalid skill name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
33248
|
-
);
|
|
33249
|
-
}
|
|
33250
|
-
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
33251
|
-
throw new Error(`Skill name must not contain path separators or "..".`);
|
|
33252
|
-
}
|
|
33253
|
-
const dir = resolveScopeDir(args.provider, args.scope, args.workspaceRoot);
|
|
33254
|
-
await fs9.mkdir(dir, { recursive: true });
|
|
33255
|
-
if (args.scope === "codex-prompts") {
|
|
33256
|
-
const filePath2 = path15.join(dir, `${args.name}.md`);
|
|
33257
|
-
try {
|
|
33258
|
-
await fs9.access(filePath2);
|
|
33259
|
-
throw new Error(`A prompt named "${args.name}" already exists at ${filePath2}`);
|
|
33260
|
-
} catch (err) {
|
|
33261
|
-
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
33262
|
-
throw err;
|
|
33263
|
-
}
|
|
33264
|
-
if (err instanceof Error && err.message.includes("already exists")) {
|
|
33265
|
-
throw err;
|
|
33266
|
-
}
|
|
33267
|
-
}
|
|
33268
|
-
const initial2 = buildStarterPrompt(args.name);
|
|
33269
|
-
await fs9.writeFile(filePath2, initial2, "utf8");
|
|
33270
|
-
return { path: filePath2 };
|
|
33271
|
-
}
|
|
33272
|
-
const skillDir = path15.join(dir, args.name);
|
|
33273
|
-
let dirExists = false;
|
|
33274
|
-
try {
|
|
33275
|
-
const stat5 = await fs9.stat(skillDir);
|
|
33276
|
-
dirExists = stat5.isDirectory();
|
|
33277
|
-
} catch {
|
|
33278
|
-
dirExists = false;
|
|
33279
|
-
}
|
|
33280
|
-
if (dirExists) {
|
|
33281
|
-
throw new Error(`A skill named "${args.name}" already exists at ${skillDir}`);
|
|
33282
|
-
}
|
|
33283
|
-
await fs9.mkdir(skillDir, { recursive: true });
|
|
33284
|
-
const filePath = path15.join(skillDir, "SKILL.md");
|
|
33285
|
-
const initial = buildStarterSkill(args.name);
|
|
33286
|
-
await fs9.writeFile(filePath, initial, "utf8");
|
|
33287
|
-
return { path: filePath };
|
|
33288
|
-
}
|
|
33289
|
-
function buildStarterSkill(name) {
|
|
33290
|
-
return `---
|
|
33291
|
-
name: ${name}
|
|
33292
|
-
description: ""
|
|
33293
|
-
---
|
|
33294
|
-
|
|
33295
|
-
# ${name}
|
|
33296
|
-
|
|
33297
|
-
Describe when this skill should be used and what it does. The body of this
|
|
33298
|
-
file is loaded into the agent's context when the skill is invoked.
|
|
33299
|
-
`;
|
|
33300
|
-
}
|
|
33301
|
-
function buildStarterPrompt(name) {
|
|
33302
|
-
return `---
|
|
33303
|
-
description: ""
|
|
33304
|
-
argument-hint: ""
|
|
33305
|
-
---
|
|
33306
|
-
|
|
33307
|
-
# ${name}
|
|
33308
|
-
|
|
33309
|
-
Body of the prompt. Use \`$1\`, \`$2\`, ... or \`$ARGUMENTS\` for parameter expansion.
|
|
33310
|
-
`;
|
|
33311
|
-
}
|
|
33312
|
-
async function writeSkillFrontmatter(args, workspaceRoot) {
|
|
33313
|
-
if (!path15.isAbsolute(args.path)) {
|
|
33314
|
-
throw new Error(`writeSkillFrontmatter expects an absolute path; got "${args.path}"`);
|
|
33315
|
-
}
|
|
33316
|
-
if (!isInsideAllowedRoot(args.path, workspaceRoot)) {
|
|
33317
|
-
throw new Error(`Path "${args.path}" is not inside an allowlisted skill root`);
|
|
33318
|
-
}
|
|
33319
|
-
let original;
|
|
33320
|
-
try {
|
|
33321
|
-
original = await fs9.readFile(args.path, "utf8");
|
|
33322
|
-
} catch (err) {
|
|
33323
|
-
throw new Error(
|
|
33324
|
-
`Failed to read skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
33325
|
-
);
|
|
33326
|
-
}
|
|
33327
|
-
const parsed = parseSkillFile(original);
|
|
33328
|
-
const newLines = rewriteFrontmatter(parsed.rawFrontmatterLines, args.frontmatter);
|
|
33329
|
-
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
33330
|
-
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
33331
|
-
${parsed.body}` : `${nextFrontmatter}
|
|
33332
|
-
|
|
33333
|
-
${original}`;
|
|
33334
|
-
await fs9.writeFile(args.path, nextContent, "utf8");
|
|
33335
|
-
}
|
|
33336
|
-
|
|
33337
33451
|
// ../server/src/utils/directory-suggestions.ts
|
|
33338
33452
|
import { readdir as readdir2, realpath, stat as stat3 } from "node:fs/promises";
|
|
33339
33453
|
import path16 from "node:path";
|
|
@@ -34972,10 +35086,12 @@ async function readBrandsFromDir(scope, dir) {
|
|
|
34972
35086
|
continue;
|
|
34973
35087
|
}
|
|
34974
35088
|
let parsed;
|
|
35089
|
+
let body = "";
|
|
34975
35090
|
try {
|
|
34976
35091
|
const text = await fs11.readFile(fullPath, "utf8");
|
|
34977
|
-
const { rawFrontmatterLines, hadFrontmatter } = parseBrandFile(text);
|
|
35092
|
+
const { rawFrontmatterLines, body: parsedBody, hadFrontmatter } = parseBrandFile(text);
|
|
34978
35093
|
parsed = hadFrontmatter ? parseFrontmatter2(rawFrontmatterLines) : { description: "", variables: [] };
|
|
35094
|
+
body = parsedBody;
|
|
34979
35095
|
} catch {
|
|
34980
35096
|
parsed = { description: "", variables: [] };
|
|
34981
35097
|
}
|
|
@@ -34987,7 +35103,8 @@ async function readBrandsFromDir(scope, dir) {
|
|
|
34987
35103
|
variables: parsed.variables,
|
|
34988
35104
|
path: fullPath,
|
|
34989
35105
|
modifiedAt: stat5.mtime.toISOString(),
|
|
34990
|
-
size: stat5.size
|
|
35106
|
+
size: stat5.size,
|
|
35107
|
+
body
|
|
34991
35108
|
});
|
|
34992
35109
|
}
|
|
34993
35110
|
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -35426,11 +35543,132 @@ async function generateAndApplyBrandTokens(options) {
|
|
|
35426
35543
|
return { generatedCount: acceptedUpdates.size };
|
|
35427
35544
|
}
|
|
35428
35545
|
|
|
35546
|
+
// ../server/src/server/brand/photography-analyzer.ts
|
|
35547
|
+
import { z as z38 } from "zod";
|
|
35548
|
+
var PhotographyResponseSchema = z38.object({
|
|
35549
|
+
dna: z38.string().min(1),
|
|
35550
|
+
dials: z38.record(z38.string(), z38.string())
|
|
35551
|
+
});
|
|
35552
|
+
function buildPrompt3(args) {
|
|
35553
|
+
const dialLines = args.selectVars.map((v) => {
|
|
35554
|
+
const options = (v.options ?? []).join(", ");
|
|
35555
|
+
return `- ${v.key} (${v.label || v.key}): current="${v.value || "(unset)"}" options=[${options}]`;
|
|
35556
|
+
}).join("\n");
|
|
35557
|
+
const refSection = args.referenceDescriptions.length > 0 ? [
|
|
35558
|
+
"",
|
|
35559
|
+
"Reference image filenames/descriptions provided by the user:",
|
|
35560
|
+
...args.referenceDescriptions.map((r) => `- ${r}`)
|
|
35561
|
+
].join("\n") : "";
|
|
35562
|
+
return [
|
|
35563
|
+
"You are a photography director analyzing a brand's photographic identity.",
|
|
35564
|
+
"The user has provided a brief describing the visual direction they want.",
|
|
35565
|
+
"Your job is to:",
|
|
35566
|
+
"1. Write a rich, specific Style DNA (2-3 sentences describing the photographic",
|
|
35567
|
+
" identity in concrete visual terms \u2014 lighting quality, tonal mood, depth",
|
|
35568
|
+
" characteristics, color rendering. No marketing fluff.)",
|
|
35569
|
+
"2. Pre-dial every camera control to match that style direction.",
|
|
35570
|
+
" ONLY use values that appear in the options list for each dial.",
|
|
35571
|
+
"",
|
|
35572
|
+
"User brief:",
|
|
35573
|
+
args.brief,
|
|
35574
|
+
refSection,
|
|
35575
|
+
"",
|
|
35576
|
+
"Camera dials (key, current value, valid options):",
|
|
35577
|
+
dialLines,
|
|
35578
|
+
"",
|
|
35579
|
+
"Return JSON only with the shape:",
|
|
35580
|
+
'{ "dna": "Rich style DNA description...", "dials": { "key": "value", ... } }',
|
|
35581
|
+
"",
|
|
35582
|
+
"Rules:",
|
|
35583
|
+
"- The dna field must be a non-empty string, 2-3 sentences.",
|
|
35584
|
+
"- Every value in dials must be one of the listed options for that key.",
|
|
35585
|
+
"- Include ALL dial keys in dials, even if you keep the current value.",
|
|
35586
|
+
"- Do not invent new keys. Do not output anything outside the JSON."
|
|
35587
|
+
].filter((line) => line !== "").join("\n");
|
|
35588
|
+
}
|
|
35589
|
+
async function analyzeAndApplyPhotographyStyle(options) {
|
|
35590
|
+
const { agentManager, workspaceRoot, brandPath, brief, referenceDescriptions, logger } = options;
|
|
35591
|
+
const brands = await listBrands({ workspaceRoot });
|
|
35592
|
+
const photoBrand = brands.find((b) => b.path === brandPath) ?? null;
|
|
35593
|
+
if (!photoBrand) {
|
|
35594
|
+
throw new Error(`Brand file not found at ${brandPath}`);
|
|
35595
|
+
}
|
|
35596
|
+
const allVars = photoBrand.variables;
|
|
35597
|
+
const selectVars = allVars.filter(
|
|
35598
|
+
(v) => v.type === "select" && v.options && v.options.length > 0
|
|
35599
|
+
);
|
|
35600
|
+
if (selectVars.length === 0) {
|
|
35601
|
+
throw new Error("No camera dials found in photography brand file");
|
|
35602
|
+
}
|
|
35603
|
+
const validOptions = /* @__PURE__ */ new Map();
|
|
35604
|
+
for (const v of selectVars) {
|
|
35605
|
+
validOptions.set(v.key, new Set(v.options ?? []));
|
|
35606
|
+
}
|
|
35607
|
+
const agentPrompt = buildPrompt3({
|
|
35608
|
+
brief,
|
|
35609
|
+
referenceDescriptions: referenceDescriptions ?? [],
|
|
35610
|
+
selectVars
|
|
35611
|
+
});
|
|
35612
|
+
let response;
|
|
35613
|
+
try {
|
|
35614
|
+
response = await generateStructuredAgentResponseWithFallback({
|
|
35615
|
+
manager: agentManager,
|
|
35616
|
+
cwd: workspaceRoot,
|
|
35617
|
+
prompt: agentPrompt,
|
|
35618
|
+
schema: PhotographyResponseSchema,
|
|
35619
|
+
schemaName: "PhotographyAnalysis",
|
|
35620
|
+
maxRetries: 2,
|
|
35621
|
+
providers: DEFAULT_STRUCTURED_GENERATION_PROVIDERS,
|
|
35622
|
+
agentConfigOverrides: {
|
|
35623
|
+
title: "Photography style analyzer",
|
|
35624
|
+
internal: true
|
|
35625
|
+
}
|
|
35626
|
+
});
|
|
35627
|
+
} catch (error) {
|
|
35628
|
+
if (error instanceof StructuredAgentResponseError || error instanceof StructuredAgentFallbackError) {
|
|
35629
|
+
logger.warn({ err: error, brandPath }, "Structured photography analysis failed");
|
|
35630
|
+
throw new Error("Photography analysis failed \u2014 the agent did not return valid JSON");
|
|
35631
|
+
}
|
|
35632
|
+
throw error;
|
|
35633
|
+
}
|
|
35634
|
+
const acceptedUpdates = /* @__PURE__ */ new Map();
|
|
35635
|
+
for (const [key, value] of Object.entries(response.dials)) {
|
|
35636
|
+
if (typeof value !== "string") continue;
|
|
35637
|
+
const trimmed = value.trim();
|
|
35638
|
+
const opts = validOptions.get(key);
|
|
35639
|
+
if (!opts) continue;
|
|
35640
|
+
if (!opts.has(trimmed)) continue;
|
|
35641
|
+
acceptedUpdates.set(key, trimmed);
|
|
35642
|
+
}
|
|
35643
|
+
acceptedUpdates.set("photo.dna", response.dna);
|
|
35644
|
+
acceptedUpdates.set("photo.brief", brief);
|
|
35645
|
+
const nextVariables = allVars.map((v) => {
|
|
35646
|
+
const update = acceptedUpdates.get(v.key);
|
|
35647
|
+
if (update === void 0) return v;
|
|
35648
|
+
return { ...v, value: update };
|
|
35649
|
+
});
|
|
35650
|
+
await writeBrandFrontmatter(
|
|
35651
|
+
{
|
|
35652
|
+
path: brandPath,
|
|
35653
|
+
frontmatter: {
|
|
35654
|
+
description: photoBrand.description,
|
|
35655
|
+
variables: nextVariables
|
|
35656
|
+
}
|
|
35657
|
+
},
|
|
35658
|
+
workspaceRoot
|
|
35659
|
+
);
|
|
35660
|
+
logger.info(
|
|
35661
|
+
{ brandPath, updatedCount: acceptedUpdates.size },
|
|
35662
|
+
"photography-analyzer: applied style analysis"
|
|
35663
|
+
);
|
|
35664
|
+
return { updatedCount: acceptedUpdates.size };
|
|
35665
|
+
}
|
|
35666
|
+
|
|
35429
35667
|
// ../server/src/server/brand/layout-generator.ts
|
|
35430
35668
|
import { promises as fs12 } from "node:fs";
|
|
35431
35669
|
import path20 from "node:path";
|
|
35432
35670
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
35433
|
-
import { z as
|
|
35671
|
+
import { z as z39 } from "zod";
|
|
35434
35672
|
var QA_FILENAME = "layout-qa.md";
|
|
35435
35673
|
var PROMPT_FILENAME = "layout-prompt.md";
|
|
35436
35674
|
var MAX_LOOKUP_LEVELS = 10;
|
|
@@ -35465,34 +35703,34 @@ async function writeRoleFile(workspaceRoot, content) {
|
|
|
35465
35703
|
await fs12.mkdir(path20.dirname(filePath), { recursive: true });
|
|
35466
35704
|
await fs12.writeFile(filePath, content, "utf8");
|
|
35467
35705
|
}
|
|
35468
|
-
var QaNextResponseSchema =
|
|
35469
|
-
done:
|
|
35470
|
-
question:
|
|
35471
|
-
options:
|
|
35472
|
-
roleDocument:
|
|
35473
|
-
});
|
|
35474
|
-
var RefinementResponseSchema =
|
|
35475
|
-
roleDocument:
|
|
35476
|
-
});
|
|
35477
|
-
var WireframeBlockSchema =
|
|
35478
|
-
type:
|
|
35479
|
-
widthFraction:
|
|
35480
|
-
heightVh:
|
|
35481
|
-
});
|
|
35482
|
-
var WireframeSectionSchema =
|
|
35483
|
-
type:
|
|
35484
|
-
widthMode:
|
|
35485
|
-
heightVh:
|
|
35486
|
-
arrangement:
|
|
35487
|
-
blocks:
|
|
35488
|
-
bg:
|
|
35489
|
-
gapAfterVh:
|
|
35490
|
-
});
|
|
35491
|
-
var WireframeDataSchema =
|
|
35492
|
-
viewport:
|
|
35493
|
-
maxWidth:
|
|
35494
|
-
pageBg:
|
|
35495
|
-
sections:
|
|
35706
|
+
var QaNextResponseSchema = z39.object({
|
|
35707
|
+
done: z39.boolean(),
|
|
35708
|
+
question: z39.string().optional(),
|
|
35709
|
+
options: z39.array(z39.string()).optional(),
|
|
35710
|
+
roleDocument: z39.string().optional()
|
|
35711
|
+
});
|
|
35712
|
+
var RefinementResponseSchema = z39.object({
|
|
35713
|
+
roleDocument: z39.string()
|
|
35714
|
+
});
|
|
35715
|
+
var WireframeBlockSchema = z39.object({
|
|
35716
|
+
type: z39.enum(["headline", "subheadline", "text", "image", "cta", "spacer"]),
|
|
35717
|
+
widthFraction: z39.number(),
|
|
35718
|
+
heightVh: z39.number()
|
|
35719
|
+
});
|
|
35720
|
+
var WireframeSectionSchema = z39.object({
|
|
35721
|
+
type: z39.enum(["hero", "features", "content", "cta", "footer", "divider"]),
|
|
35722
|
+
widthMode: z39.enum(["full-bleed", "contained"]),
|
|
35723
|
+
heightVh: z39.number(),
|
|
35724
|
+
arrangement: z39.enum(["single-column", "split", "asymmetric-grid", "bento", "stacked"]),
|
|
35725
|
+
blocks: z39.array(WireframeBlockSchema),
|
|
35726
|
+
bg: z39.enum(["neutral", "alt", "accent", "image"]),
|
|
35727
|
+
gapAfterVh: z39.number()
|
|
35728
|
+
});
|
|
35729
|
+
var WireframeDataSchema = z39.object({
|
|
35730
|
+
viewport: z39.object({ width: z39.number(), height: z39.number() }),
|
|
35731
|
+
maxWidth: z39.number(),
|
|
35732
|
+
pageBg: z39.string(),
|
|
35733
|
+
sections: z39.array(WireframeSectionSchema)
|
|
35496
35734
|
});
|
|
35497
35735
|
function buildFullBrandContext(allBrands) {
|
|
35498
35736
|
const lines = [];
|
|
@@ -37167,7 +37405,7 @@ var MIN_STREAMING_SEGMENT_DURATION_MS = 1e3;
|
|
|
37167
37405
|
var MIN_STREAMING_SEGMENT_BYTES = Math.round(
|
|
37168
37406
|
PCM_BYTES_PER_MS * MIN_STREAMING_SEGMENT_DURATION_MS
|
|
37169
37407
|
);
|
|
37170
|
-
var AgentIdSchema2 =
|
|
37408
|
+
var AgentIdSchema2 = z40.string().uuid();
|
|
37171
37409
|
var VOICE_INTERRUPT_CONFIRMATION_MS = 500;
|
|
37172
37410
|
var VoiceFeatureUnavailableError = class extends Error {
|
|
37173
37411
|
constructor(context) {
|
|
@@ -38548,6 +38786,9 @@ var Session = class _Session {
|
|
|
38548
38786
|
case "brands/generate-wireframe":
|
|
38549
38787
|
await this.handleBrandGenerateWireframeRequest(msg);
|
|
38550
38788
|
break;
|
|
38789
|
+
case "brands/analyze-photography":
|
|
38790
|
+
await this.handleBrandAnalyzePhotographyRequest(msg);
|
|
38791
|
+
break;
|
|
38551
38792
|
case "rightfont/library":
|
|
38552
38793
|
await this.handleRightFontLibraryRequest(msg);
|
|
38553
38794
|
break;
|
|
@@ -40207,8 +40448,8 @@ var Session = class _Session {
|
|
|
40207
40448
|
}
|
|
40208
40449
|
async generateCommitMessage(cwd) {
|
|
40209
40450
|
const files = await listUncommittedFiles(cwd);
|
|
40210
|
-
const schema =
|
|
40211
|
-
message:
|
|
40451
|
+
const schema = z40.object({
|
|
40452
|
+
message: z40.string().min(1).max(100).describe(
|
|
40212
40453
|
"Short feature-level summary, lowercase, imperative mood, no trailing period, no filename references."
|
|
40213
40454
|
)
|
|
40214
40455
|
});
|
|
@@ -40253,9 +40494,9 @@ var Session = class _Session {
|
|
|
40253
40494
|
},
|
|
40254
40495
|
{ appostleHome: this.appostleHome }
|
|
40255
40496
|
);
|
|
40256
|
-
const schema =
|
|
40257
|
-
title:
|
|
40258
|
-
body:
|
|
40497
|
+
const schema = z40.object({
|
|
40498
|
+
title: z40.string().min(1).max(72),
|
|
40499
|
+
body: z40.string().min(1)
|
|
40259
40500
|
});
|
|
40260
40501
|
const fileList = diff.structured && diff.structured.length > 0 ? [
|
|
40261
40502
|
"Files changed:",
|
|
@@ -45747,6 +45988,38 @@ ${details}`.trim());
|
|
|
45747
45988
|
});
|
|
45748
45989
|
}
|
|
45749
45990
|
}
|
|
45991
|
+
async handleBrandAnalyzePhotographyRequest(request) {
|
|
45992
|
+
const { requestId, workspaceRoot, brandPath, brief, referenceDescriptions } = request;
|
|
45993
|
+
try {
|
|
45994
|
+
const result = await analyzeAndApplyPhotographyStyle({
|
|
45995
|
+
agentManager: this.agentManager,
|
|
45996
|
+
workspaceRoot: expandTilde(workspaceRoot),
|
|
45997
|
+
brandPath: expandTilde(brandPath),
|
|
45998
|
+
brief,
|
|
45999
|
+
referenceDescriptions,
|
|
46000
|
+
logger: this.sessionLogger
|
|
46001
|
+
});
|
|
46002
|
+
this.emit({
|
|
46003
|
+
type: "brands/analyze-photography/response",
|
|
46004
|
+
payload: {
|
|
46005
|
+
requestId,
|
|
46006
|
+
updatedCount: result.updatedCount,
|
|
46007
|
+
error: null
|
|
46008
|
+
}
|
|
46009
|
+
});
|
|
46010
|
+
} catch (error) {
|
|
46011
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46012
|
+
this.sessionLogger.error({ err: error, brandPath }, "Failed to analyze photography style");
|
|
46013
|
+
this.emit({
|
|
46014
|
+
type: "brands/analyze-photography/response",
|
|
46015
|
+
payload: {
|
|
46016
|
+
requestId,
|
|
46017
|
+
updatedCount: 0,
|
|
46018
|
+
error: message
|
|
46019
|
+
}
|
|
46020
|
+
});
|
|
46021
|
+
}
|
|
46022
|
+
}
|
|
45750
46023
|
async handleRightFontLibraryRequest(request) {
|
|
45751
46024
|
const { requestId, libraryPath } = request;
|
|
45752
46025
|
try {
|
|
@@ -46308,7 +46581,7 @@ import webpush from "web-push";
|
|
|
46308
46581
|
import webpush2 from "web-push";
|
|
46309
46582
|
|
|
46310
46583
|
// ../server/src/server/speech/providers/local/sherpa/model-catalog.ts
|
|
46311
|
-
import { z as
|
|
46584
|
+
import { z as z41 } from "zod";
|
|
46312
46585
|
var SHERPA_ONNX_MODEL_CATALOG = {
|
|
46313
46586
|
"zipformer-bilingual-zh-en-2023-02-20": {
|
|
46314
46587
|
kind: "stt-online",
|
|
@@ -46401,7 +46674,7 @@ function buildAliasMap(modelIds) {
|
|
|
46401
46674
|
}
|
|
46402
46675
|
function createAliasedModelIdSchema(params) {
|
|
46403
46676
|
const validIds = new Set(params.modelIds);
|
|
46404
|
-
return
|
|
46677
|
+
return z41.string().trim().toLowerCase().refine(
|
|
46405
46678
|
(value) => validIds.has(value) || Object.prototype.hasOwnProperty.call(params.aliases, value),
|
|
46406
46679
|
{
|
|
46407
46680
|
message: "Invalid model id"
|
|
@@ -46432,20 +46705,20 @@ import { v4 as uuidv410 } from "uuid";
|
|
|
46432
46705
|
import { v4 as uuidv411 } from "uuid";
|
|
46433
46706
|
|
|
46434
46707
|
// ../server/src/server/speech/providers/openai/config.ts
|
|
46435
|
-
import { z as
|
|
46708
|
+
import { z as z42 } from "zod";
|
|
46436
46709
|
var DEFAULT_OPENAI_REALTIME_TRANSCRIPTION_MODEL = "gpt-4o-transcribe";
|
|
46437
46710
|
var DEFAULT_OPENAI_TTS_MODEL = "tts-1";
|
|
46438
|
-
var OpenAiTtsVoiceSchema =
|
|
46439
|
-
var OpenAiTtsModelSchema =
|
|
46440
|
-
var NumberLikeSchema =
|
|
46441
|
-
var OptionalFiniteNumberSchema = NumberLikeSchema.pipe(
|
|
46442
|
-
var OptionalTrimmedStringSchema =
|
|
46443
|
-
var OpenAiSpeechResolutionSchema =
|
|
46711
|
+
var OpenAiTtsVoiceSchema = z42.enum(["alloy", "echo", "fable", "onyx", "nova", "shimmer"]);
|
|
46712
|
+
var OpenAiTtsModelSchema = z42.enum(["tts-1", "tts-1-hd"]);
|
|
46713
|
+
var NumberLikeSchema = z42.union([z42.number(), z42.string().trim().min(1)]);
|
|
46714
|
+
var OptionalFiniteNumberSchema = NumberLikeSchema.pipe(z42.coerce.number().finite()).optional();
|
|
46715
|
+
var OptionalTrimmedStringSchema = z42.string().trim().optional().transform((value) => value && value.length > 0 ? value : void 0);
|
|
46716
|
+
var OpenAiSpeechResolutionSchema = z42.object({
|
|
46444
46717
|
apiKey: OptionalTrimmedStringSchema,
|
|
46445
46718
|
sttConfidenceThreshold: OptionalFiniteNumberSchema,
|
|
46446
46719
|
sttModel: OptionalTrimmedStringSchema,
|
|
46447
|
-
ttsVoice:
|
|
46448
|
-
ttsModel:
|
|
46720
|
+
ttsVoice: z42.string().trim().toLowerCase().pipe(OpenAiTtsVoiceSchema).default("alloy"),
|
|
46721
|
+
ttsModel: z42.string().trim().toLowerCase().pipe(OpenAiTtsModelSchema).default(DEFAULT_OPENAI_TTS_MODEL),
|
|
46449
46722
|
realtimeTranscriptionModel: OptionalTrimmedStringSchema.default(
|
|
46450
46723
|
DEFAULT_OPENAI_REALTIME_TRANSCRIPTION_MODEL
|
|
46451
46724
|
)
|
|
@@ -46490,177 +46763,177 @@ import { v4 } from "uuid";
|
|
|
46490
46763
|
import OpenAI2 from "openai";
|
|
46491
46764
|
|
|
46492
46765
|
// ../server/src/server/agent/agent-storage.ts
|
|
46493
|
-
import { z as
|
|
46494
|
-
var SERIALIZABLE_CONFIG_SCHEMA =
|
|
46495
|
-
title:
|
|
46496
|
-
modeId:
|
|
46497
|
-
model:
|
|
46498
|
-
thinkingOptionId:
|
|
46499
|
-
featureValues:
|
|
46500
|
-
extra:
|
|
46501
|
-
systemPrompt:
|
|
46502
|
-
mcpServers:
|
|
46766
|
+
import { z as z43 } from "zod";
|
|
46767
|
+
var SERIALIZABLE_CONFIG_SCHEMA = z43.object({
|
|
46768
|
+
title: z43.string().nullable().optional(),
|
|
46769
|
+
modeId: z43.string().nullable().optional(),
|
|
46770
|
+
model: z43.string().nullable().optional(),
|
|
46771
|
+
thinkingOptionId: z43.string().nullable().optional(),
|
|
46772
|
+
featureValues: z43.record(z43.unknown()).nullable().optional(),
|
|
46773
|
+
extra: z43.record(z43.any()).nullable().optional(),
|
|
46774
|
+
systemPrompt: z43.string().nullable().optional(),
|
|
46775
|
+
mcpServers: z43.record(z43.any()).nullable().optional()
|
|
46503
46776
|
}).nullable().optional();
|
|
46504
|
-
var PERSISTENCE_HANDLE_SCHEMA =
|
|
46505
|
-
provider:
|
|
46506
|
-
sessionId:
|
|
46507
|
-
nativeHandle:
|
|
46508
|
-
metadata:
|
|
46777
|
+
var PERSISTENCE_HANDLE_SCHEMA = z43.object({
|
|
46778
|
+
provider: z43.string(),
|
|
46779
|
+
sessionId: z43.string(),
|
|
46780
|
+
nativeHandle: z43.any().optional(),
|
|
46781
|
+
metadata: z43.record(z43.any()).optional()
|
|
46509
46782
|
}).nullable().optional();
|
|
46510
|
-
var STORED_AGENT_SCHEMA =
|
|
46511
|
-
id:
|
|
46512
|
-
provider:
|
|
46513
|
-
cwd:
|
|
46514
|
-
createdAt:
|
|
46515
|
-
updatedAt:
|
|
46516
|
-
lastActivityAt:
|
|
46517
|
-
lastUserMessageAt:
|
|
46518
|
-
title:
|
|
46519
|
-
labels:
|
|
46783
|
+
var STORED_AGENT_SCHEMA = z43.object({
|
|
46784
|
+
id: z43.string(),
|
|
46785
|
+
provider: z43.string(),
|
|
46786
|
+
cwd: z43.string(),
|
|
46787
|
+
createdAt: z43.string(),
|
|
46788
|
+
updatedAt: z43.string(),
|
|
46789
|
+
lastActivityAt: z43.string().optional(),
|
|
46790
|
+
lastUserMessageAt: z43.string().nullable().optional(),
|
|
46791
|
+
title: z43.string().nullable().optional(),
|
|
46792
|
+
labels: z43.record(z43.string()).default({}),
|
|
46520
46793
|
lastStatus: AgentStatusSchema.default("closed"),
|
|
46521
|
-
lastModeId:
|
|
46794
|
+
lastModeId: z43.string().nullable().optional(),
|
|
46522
46795
|
config: SERIALIZABLE_CONFIG_SCHEMA,
|
|
46523
|
-
runtimeInfo:
|
|
46524
|
-
provider:
|
|
46525
|
-
sessionId:
|
|
46526
|
-
model:
|
|
46527
|
-
thinkingOptionId:
|
|
46528
|
-
modeId:
|
|
46529
|
-
extra:
|
|
46796
|
+
runtimeInfo: z43.object({
|
|
46797
|
+
provider: z43.string(),
|
|
46798
|
+
sessionId: z43.string().nullable(),
|
|
46799
|
+
model: z43.string().nullable().optional(),
|
|
46800
|
+
thinkingOptionId: z43.string().nullable().optional(),
|
|
46801
|
+
modeId: z43.string().nullable().optional(),
|
|
46802
|
+
extra: z43.record(z43.unknown()).optional()
|
|
46530
46803
|
}).optional(),
|
|
46531
|
-
features:
|
|
46804
|
+
features: z43.array(AgentFeatureSchema).optional(),
|
|
46532
46805
|
persistence: PERSISTENCE_HANDLE_SCHEMA,
|
|
46533
|
-
lastError:
|
|
46534
|
-
requiresAttention:
|
|
46535
|
-
attentionReason:
|
|
46536
|
-
attentionTimestamp:
|
|
46537
|
-
internal:
|
|
46538
|
-
archivedAt:
|
|
46806
|
+
lastError: z43.string().nullable().optional(),
|
|
46807
|
+
requiresAttention: z43.boolean().optional(),
|
|
46808
|
+
attentionReason: z43.enum(["finished", "error", "permission"]).nullable().optional(),
|
|
46809
|
+
attentionTimestamp: z43.string().nullable().optional(),
|
|
46810
|
+
internal: z43.boolean().optional(),
|
|
46811
|
+
archivedAt: z43.string().nullable().optional(),
|
|
46539
46812
|
// Fork lineage (optional for backward compat with pre-fork records).
|
|
46540
|
-
parentAgentId:
|
|
46541
|
-
forkedFromMessageUuid:
|
|
46813
|
+
parentAgentId: z43.string().optional(),
|
|
46814
|
+
forkedFromMessageUuid: z43.string().optional(),
|
|
46542
46815
|
// Multi-tenant session isolation: the auth-server user-id that created
|
|
46543
46816
|
// (and therefore owns) this agent + the additive ACL of other users
|
|
46544
46817
|
// granted access. Both optional/null-default for backward compatibility
|
|
46545
46818
|
// with pre-Phase-2c records — those load with `null` owner and stay
|
|
46546
46819
|
// visible to every connecting user (matches today's single-tenant
|
|
46547
46820
|
// behavior). Set on new agents at create time via Session.ownerUserId.
|
|
46548
|
-
ownerUserId:
|
|
46549
|
-
sharedWithUserIds:
|
|
46821
|
+
ownerUserId: z43.string().nullable().optional(),
|
|
46822
|
+
sharedWithUserIds: z43.array(z43.string()).default([]),
|
|
46550
46823
|
// Owner's display username — needed on resume to route to the correct
|
|
46551
46824
|
// `CLAUDE_CONFIG_DIR` via `ensureClaudeProfile(username)` for agents
|
|
46552
46825
|
// created by a shared (non-owner) user. Without this, a resumed shared-
|
|
46553
46826
|
// user agent would silently fall back to the daemon owner's Claude
|
|
46554
46827
|
// profile/subscription. Optional — pre-Phase-3 records rehydrate without
|
|
46555
46828
|
// per-user profile routing.
|
|
46556
|
-
ownerUsername:
|
|
46829
|
+
ownerUsername: z43.string().optional()
|
|
46557
46830
|
});
|
|
46558
46831
|
|
|
46559
46832
|
// ../server/src/server/agent/mcp-server.ts
|
|
46560
46833
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
46561
|
-
import { z as
|
|
46562
|
-
var TerminalSummarySchema =
|
|
46563
|
-
id:
|
|
46564
|
-
name:
|
|
46565
|
-
cwd:
|
|
46834
|
+
import { z as z44 } from "zod";
|
|
46835
|
+
var TerminalSummarySchema = z44.object({
|
|
46836
|
+
id: z44.string(),
|
|
46837
|
+
name: z44.string(),
|
|
46838
|
+
cwd: z44.string()
|
|
46566
46839
|
});
|
|
46567
|
-
var WorktreeSummarySchema =
|
|
46568
|
-
path:
|
|
46569
|
-
createdAt:
|
|
46570
|
-
branchName:
|
|
46571
|
-
head:
|
|
46840
|
+
var WorktreeSummarySchema = z44.object({
|
|
46841
|
+
path: z44.string(),
|
|
46842
|
+
createdAt: z44.string(),
|
|
46843
|
+
branchName: z44.string().optional(),
|
|
46844
|
+
head: z44.string().optional()
|
|
46572
46845
|
});
|
|
46573
46846
|
|
|
46574
46847
|
// ../server/src/server/loop-service.ts
|
|
46575
|
-
import { z as
|
|
46848
|
+
import { z as z45 } from "zod";
|
|
46576
46849
|
var MAX_VERIFY_OUTPUT_BYTES = 64 * 1024;
|
|
46577
|
-
var LoopVerifyPromptSchema =
|
|
46578
|
-
passed:
|
|
46579
|
-
reason:
|
|
46580
|
-
});
|
|
46581
|
-
var LoopLogEntrySchema2 =
|
|
46582
|
-
seq:
|
|
46583
|
-
timestamp:
|
|
46584
|
-
iteration:
|
|
46585
|
-
source:
|
|
46586
|
-
level:
|
|
46587
|
-
text:
|
|
46588
|
-
});
|
|
46589
|
-
var LoopVerifyCheckResultSchema2 =
|
|
46590
|
-
command:
|
|
46591
|
-
exitCode:
|
|
46592
|
-
passed:
|
|
46593
|
-
stdout:
|
|
46594
|
-
stderr:
|
|
46595
|
-
startedAt:
|
|
46596
|
-
completedAt:
|
|
46597
|
-
});
|
|
46598
|
-
var LoopVerifyPromptResultSchema2 =
|
|
46599
|
-
passed:
|
|
46600
|
-
reason:
|
|
46601
|
-
verifierAgentId:
|
|
46602
|
-
startedAt:
|
|
46603
|
-
completedAt:
|
|
46604
|
-
});
|
|
46605
|
-
var LoopIterationRecordSchema2 =
|
|
46606
|
-
index:
|
|
46607
|
-
workerAgentId:
|
|
46608
|
-
workerStartedAt:
|
|
46609
|
-
workerCompletedAt:
|
|
46610
|
-
verifierAgentId:
|
|
46611
|
-
status:
|
|
46612
|
-
workerOutcome:
|
|
46613
|
-
failureReason:
|
|
46614
|
-
verifyChecks:
|
|
46850
|
+
var LoopVerifyPromptSchema = z45.object({
|
|
46851
|
+
passed: z45.boolean(),
|
|
46852
|
+
reason: z45.string().min(1)
|
|
46853
|
+
});
|
|
46854
|
+
var LoopLogEntrySchema2 = z45.object({
|
|
46855
|
+
seq: z45.number().int().positive(),
|
|
46856
|
+
timestamp: z45.string(),
|
|
46857
|
+
iteration: z45.number().int().positive().nullable(),
|
|
46858
|
+
source: z45.enum(["loop", "worker", "verifier", "verify-check"]),
|
|
46859
|
+
level: z45.enum(["info", "error"]),
|
|
46860
|
+
text: z45.string()
|
|
46861
|
+
});
|
|
46862
|
+
var LoopVerifyCheckResultSchema2 = z45.object({
|
|
46863
|
+
command: z45.string(),
|
|
46864
|
+
exitCode: z45.number().int(),
|
|
46865
|
+
passed: z45.boolean(),
|
|
46866
|
+
stdout: z45.string(),
|
|
46867
|
+
stderr: z45.string(),
|
|
46868
|
+
startedAt: z45.string(),
|
|
46869
|
+
completedAt: z45.string()
|
|
46870
|
+
});
|
|
46871
|
+
var LoopVerifyPromptResultSchema2 = z45.object({
|
|
46872
|
+
passed: z45.boolean(),
|
|
46873
|
+
reason: z45.string(),
|
|
46874
|
+
verifierAgentId: z45.string().nullable(),
|
|
46875
|
+
startedAt: z45.string(),
|
|
46876
|
+
completedAt: z45.string()
|
|
46877
|
+
});
|
|
46878
|
+
var LoopIterationRecordSchema2 = z45.object({
|
|
46879
|
+
index: z45.number().int().positive(),
|
|
46880
|
+
workerAgentId: z45.string().nullable(),
|
|
46881
|
+
workerStartedAt: z45.string(),
|
|
46882
|
+
workerCompletedAt: z45.string().nullable(),
|
|
46883
|
+
verifierAgentId: z45.string().nullable(),
|
|
46884
|
+
status: z45.enum(["running", "succeeded", "failed", "stopped"]),
|
|
46885
|
+
workerOutcome: z45.enum(["completed", "failed", "canceled"]).nullable(),
|
|
46886
|
+
failureReason: z45.string().nullable(),
|
|
46887
|
+
verifyChecks: z45.array(LoopVerifyCheckResultSchema2),
|
|
46615
46888
|
verifyPrompt: LoopVerifyPromptResultSchema2.nullable()
|
|
46616
46889
|
});
|
|
46617
|
-
var LoopRecordSchema2 =
|
|
46618
|
-
id:
|
|
46619
|
-
name:
|
|
46620
|
-
prompt:
|
|
46621
|
-
cwd:
|
|
46622
|
-
provider:
|
|
46623
|
-
model:
|
|
46624
|
-
workerProvider:
|
|
46625
|
-
workerModel:
|
|
46626
|
-
verifierProvider:
|
|
46627
|
-
verifierModel:
|
|
46628
|
-
verifyPrompt:
|
|
46629
|
-
verifyChecks:
|
|
46630
|
-
archive:
|
|
46631
|
-
sleepMs:
|
|
46632
|
-
maxIterations:
|
|
46633
|
-
maxTimeMs:
|
|
46634
|
-
status:
|
|
46635
|
-
createdAt:
|
|
46636
|
-
updatedAt:
|
|
46637
|
-
startedAt:
|
|
46638
|
-
completedAt:
|
|
46639
|
-
stopRequestedAt:
|
|
46640
|
-
iterations:
|
|
46641
|
-
logs:
|
|
46642
|
-
nextLogSeq:
|
|
46643
|
-
activeIteration:
|
|
46644
|
-
activeWorkerAgentId:
|
|
46645
|
-
activeVerifierAgentId:
|
|
46646
|
-
});
|
|
46647
|
-
var StoredLoopsSchema =
|
|
46890
|
+
var LoopRecordSchema2 = z45.object({
|
|
46891
|
+
id: z45.string(),
|
|
46892
|
+
name: z45.string().nullable(),
|
|
46893
|
+
prompt: z45.string(),
|
|
46894
|
+
cwd: z45.string(),
|
|
46895
|
+
provider: z45.string(),
|
|
46896
|
+
model: z45.string().nullable(),
|
|
46897
|
+
workerProvider: z45.string().nullable(),
|
|
46898
|
+
workerModel: z45.string().nullable(),
|
|
46899
|
+
verifierProvider: z45.string().nullable(),
|
|
46900
|
+
verifierModel: z45.string().nullable(),
|
|
46901
|
+
verifyPrompt: z45.string().nullable(),
|
|
46902
|
+
verifyChecks: z45.array(z45.string()),
|
|
46903
|
+
archive: z45.boolean(),
|
|
46904
|
+
sleepMs: z45.number().int().nonnegative(),
|
|
46905
|
+
maxIterations: z45.number().int().positive().nullable(),
|
|
46906
|
+
maxTimeMs: z45.number().int().positive().nullable(),
|
|
46907
|
+
status: z45.enum(["running", "succeeded", "failed", "stopped"]),
|
|
46908
|
+
createdAt: z45.string(),
|
|
46909
|
+
updatedAt: z45.string(),
|
|
46910
|
+
startedAt: z45.string(),
|
|
46911
|
+
completedAt: z45.string().nullable(),
|
|
46912
|
+
stopRequestedAt: z45.string().nullable(),
|
|
46913
|
+
iterations: z45.array(LoopIterationRecordSchema2),
|
|
46914
|
+
logs: z45.array(LoopLogEntrySchema2),
|
|
46915
|
+
nextLogSeq: z45.number().int().positive(),
|
|
46916
|
+
activeIteration: z45.number().int().positive().nullable(),
|
|
46917
|
+
activeWorkerAgentId: z45.string().nullable(),
|
|
46918
|
+
activeVerifierAgentId: z45.string().nullable()
|
|
46919
|
+
});
|
|
46920
|
+
var StoredLoopsSchema = z45.array(LoopRecordSchema2);
|
|
46648
46921
|
|
|
46649
46922
|
// ../server/src/server/quest/store.ts
|
|
46650
|
-
import { z as
|
|
46651
|
-
var StoredQuestsSchema =
|
|
46923
|
+
import { z as z46 } from "zod";
|
|
46924
|
+
var StoredQuestsSchema = z46.array(QuestRecordSchema);
|
|
46652
46925
|
|
|
46653
46926
|
// ../server/src/server/quest/quest-metadata-generator.ts
|
|
46654
|
-
import { z as
|
|
46927
|
+
import { z as z47 } from "zod";
|
|
46655
46928
|
|
|
46656
46929
|
// ../server/src/server/quest/orchestrator-mcp.ts
|
|
46657
46930
|
import { createSdkMcpServer as createSdkMcpServer2, tool as tool3 } from "@anthropic-ai/claude-agent-sdk";
|
|
46658
|
-
import { z as
|
|
46931
|
+
import { z as z48 } from "zod";
|
|
46659
46932
|
var HANDOFF_INPUT_SHAPE = {
|
|
46660
|
-
rolePath:
|
|
46933
|
+
rolePath: z48.string().min(1).describe(
|
|
46661
46934
|
"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."
|
|
46662
46935
|
),
|
|
46663
|
-
task:
|
|
46936
|
+
task: z48.string().min(1).describe(
|
|
46664
46937
|
"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."
|
|
46665
46938
|
)
|
|
46666
46939
|
};
|
|
@@ -46715,13 +46988,13 @@ var execFileAsync3 = promisify4(execFile3);
|
|
|
46715
46988
|
var MAX_VERIFY_OUTPUT_BYTES2 = 64 * 1024;
|
|
46716
46989
|
|
|
46717
46990
|
// ../server/src/shared/connection-offer.ts
|
|
46718
|
-
import { z as
|
|
46719
|
-
var ConnectionOfferV2Schema =
|
|
46720
|
-
v:
|
|
46721
|
-
serverId:
|
|
46722
|
-
daemonPublicKeyB64:
|
|
46723
|
-
relay:
|
|
46724
|
-
endpoint:
|
|
46991
|
+
import { z as z49 } from "zod";
|
|
46992
|
+
var ConnectionOfferV2Schema = z49.object({
|
|
46993
|
+
v: z49.literal(2),
|
|
46994
|
+
serverId: z49.string().min(1),
|
|
46995
|
+
daemonPublicKeyB64: z49.string().min(1),
|
|
46996
|
+
relay: z49.object({
|
|
46997
|
+
endpoint: z49.string().min(1)
|
|
46725
46998
|
})
|
|
46726
46999
|
});
|
|
46727
47000
|
|
|
@@ -46755,21 +47028,21 @@ function isRelayClientWebSocketUrl(url) {
|
|
|
46755
47028
|
|
|
46756
47029
|
// ../server/src/server/config.ts
|
|
46757
47030
|
import path22 from "node:path";
|
|
46758
|
-
import { z as
|
|
47031
|
+
import { z as z53 } from "zod";
|
|
46759
47032
|
|
|
46760
47033
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
46761
|
-
import { z as
|
|
47034
|
+
import { z as z52 } from "zod";
|
|
46762
47035
|
|
|
46763
47036
|
// ../server/src/server/speech/providers/local/config.ts
|
|
46764
47037
|
import path21 from "node:path";
|
|
46765
|
-
import { z as
|
|
47038
|
+
import { z as z50 } from "zod";
|
|
46766
47039
|
var DEFAULT_LOCAL_MODELS_SUBDIR = path21.join("models", "local-speech");
|
|
46767
|
-
var NumberLikeSchema2 =
|
|
46768
|
-
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(
|
|
46769
|
-
var OptionalIntegerSchema = NumberLikeSchema2.pipe(
|
|
46770
|
-
var LocalSpeechResolutionSchema =
|
|
46771
|
-
includeProviderConfig:
|
|
46772
|
-
modelsDir:
|
|
47040
|
+
var NumberLikeSchema2 = z50.union([z50.number(), z50.string().trim().min(1)]);
|
|
47041
|
+
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z50.coerce.number().finite()).optional();
|
|
47042
|
+
var OptionalIntegerSchema = NumberLikeSchema2.pipe(z50.coerce.number().int()).optional();
|
|
47043
|
+
var LocalSpeechResolutionSchema = z50.object({
|
|
47044
|
+
includeProviderConfig: z50.boolean(),
|
|
47045
|
+
modelsDir: z50.string().trim().min(1),
|
|
46773
47046
|
dictationLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
46774
47047
|
voiceLocalSttModel: LocalSttModelIdSchema.default(DEFAULT_LOCAL_STT_MODEL),
|
|
46775
47048
|
voiceLocalTtsModel: LocalTtsModelIdSchema.default(DEFAULT_LOCAL_TTS_MODEL),
|
|
@@ -46825,17 +47098,17 @@ function resolveLocalSpeechConfig(params) {
|
|
|
46825
47098
|
}
|
|
46826
47099
|
|
|
46827
47100
|
// ../server/src/server/speech/speech-types.ts
|
|
46828
|
-
import { z as
|
|
46829
|
-
var SpeechProviderIdSchema2 =
|
|
46830
|
-
var RequestedSpeechProviderSchema =
|
|
47101
|
+
import { z as z51 } from "zod";
|
|
47102
|
+
var SpeechProviderIdSchema2 = z51.enum(["openai", "local"]);
|
|
47103
|
+
var RequestedSpeechProviderSchema = z51.object({
|
|
46831
47104
|
provider: SpeechProviderIdSchema2,
|
|
46832
|
-
explicit:
|
|
46833
|
-
enabled:
|
|
47105
|
+
explicit: z51.boolean(),
|
|
47106
|
+
enabled: z51.boolean().optional()
|
|
46834
47107
|
});
|
|
46835
47108
|
|
|
46836
47109
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
46837
|
-
var OptionalSpeechProviderSchema =
|
|
46838
|
-
var OptionalBooleanFlagSchema =
|
|
47110
|
+
var OptionalSpeechProviderSchema = z52.string().trim().toLowerCase().pipe(SpeechProviderIdSchema2).optional();
|
|
47111
|
+
var OptionalBooleanFlagSchema = z52.union([z52.boolean(), z52.string().trim().toLowerCase()]).optional().transform((value) => {
|
|
46839
47112
|
if (typeof value === "boolean") {
|
|
46840
47113
|
return value;
|
|
46841
47114
|
}
|
|
@@ -46850,7 +47123,7 @@ var OptionalBooleanFlagSchema = z51.union([z51.boolean(), z51.string().trim().to
|
|
|
46850
47123
|
}
|
|
46851
47124
|
return void 0;
|
|
46852
47125
|
});
|
|
46853
|
-
var RequestedSpeechProvidersSchema =
|
|
47126
|
+
var RequestedSpeechProvidersSchema = z52.object({
|
|
46854
47127
|
dictationStt: OptionalSpeechProviderSchema.default("local"),
|
|
46855
47128
|
voiceTurnDetection: OptionalSpeechProviderSchema.default("local"),
|
|
46856
47129
|
voiceStt: OptionalSpeechProviderSchema.default("local"),
|
|
@@ -46959,9 +47232,9 @@ function parseBooleanEnv(value) {
|
|
|
46959
47232
|
}
|
|
46960
47233
|
return void 0;
|
|
46961
47234
|
}
|
|
46962
|
-
var OptionalVoiceLlmProviderSchema =
|
|
47235
|
+
var OptionalVoiceLlmProviderSchema = z53.union([z53.string(), z53.null(), z53.undefined()]).transform(
|
|
46963
47236
|
(value) => typeof value === "string" ? value.trim().toLowerCase() : null
|
|
46964
|
-
).pipe(
|
|
47237
|
+
).pipe(z53.union([AgentProviderSchema, z53.null()]));
|
|
46965
47238
|
function parseOptionalVoiceLlmProvider(value) {
|
|
46966
47239
|
const parsed = OptionalVoiceLlmProviderSchema.safeParse(value);
|
|
46967
47240
|
return parsed.success ? parsed.data : null;
|
|
@@ -47014,6 +47287,7 @@ function loadConfig(appostleHome, options) {
|
|
|
47014
47287
|
const mcpEnabled = options?.cli?.mcpEnabled ?? persisted.daemon?.mcp?.enabled ?? true;
|
|
47015
47288
|
const mcpInjectIntoAgents = options?.cli?.mcpInjectIntoAgents ?? persisted.daemon?.mcp?.injectIntoAgents ?? false;
|
|
47016
47289
|
const chromeEnabled = persisted.daemon?.chrome?.enabled ?? true;
|
|
47290
|
+
const playwrightEnabled = persisted.daemon?.playwright?.enabled ?? true;
|
|
47017
47291
|
const daemonIcon = persisted.daemon?.identity?.icon;
|
|
47018
47292
|
const relayEnabled = options?.cli?.relayEnabled ?? parseBooleanEnv(env.APPOSTLE_RELAY_ENABLED) ?? persisted.daemon?.relay?.enabled ?? true;
|
|
47019
47293
|
const relayEndpoint = env.APPOSTLE_RELAY_ENDPOINT ?? persisted.daemon?.relay?.endpoint ?? DEFAULT_RELAY_ENDPOINT;
|
|
@@ -47044,6 +47318,7 @@ function loadConfig(appostleHome, options) {
|
|
|
47044
47318
|
mcpEnabled,
|
|
47045
47319
|
mcpInjectIntoAgents,
|
|
47046
47320
|
chromeEnabled,
|
|
47321
|
+
playwrightEnabled,
|
|
47047
47322
|
mcpDebug: env.MCP_DEBUG === "1",
|
|
47048
47323
|
daemonIcon,
|
|
47049
47324
|
agentStoragePath: path22.join(appostleHome, "agents"),
|
|
@@ -47335,6 +47610,9 @@ function createEncryptedTransport(base, daemonPublicKeyB64, logger, staticKeyPai
|
|
|
47335
47610
|
throw new Error("Encrypted channel not ready");
|
|
47336
47611
|
}
|
|
47337
47612
|
void channel.send(normalizeTransportPayload(data)).catch((error) => {
|
|
47613
|
+
if (closed) {
|
|
47614
|
+
console.error("[relay-e2ee] send failed after transport close:", error);
|
|
47615
|
+
}
|
|
47338
47616
|
emitError(error);
|
|
47339
47617
|
});
|
|
47340
47618
|
},
|
|
@@ -50937,6 +51215,20 @@ var DaemonClient = class {
|
|
|
50937
51215
|
timeout: 12e4
|
|
50938
51216
|
});
|
|
50939
51217
|
}
|
|
51218
|
+
async brandsAnalyzePhotography(options) {
|
|
51219
|
+
return this.sendCorrelatedSessionRequest({
|
|
51220
|
+
requestId: options.requestId,
|
|
51221
|
+
message: {
|
|
51222
|
+
type: "brands/analyze-photography",
|
|
51223
|
+
workspaceRoot: options.workspaceRoot,
|
|
51224
|
+
brandPath: options.brandPath,
|
|
51225
|
+
brief: options.brief,
|
|
51226
|
+
...options.referenceDescriptions ? { referenceDescriptions: options.referenceDescriptions } : {}
|
|
51227
|
+
},
|
|
51228
|
+
responseType: "brands/analyze-photography/response",
|
|
51229
|
+
timeout: 12e4
|
|
51230
|
+
});
|
|
51231
|
+
}
|
|
50940
51232
|
onTerminalStreamEvent(handler) {
|
|
50941
51233
|
this.terminalStreamListeners.add(handler);
|
|
50942
51234
|
return () => {
|