appostle-installer 0.0.4 → 0.0.6
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 +1913 -325
- package/dist/appostle.js.map +4 -4
- package/dist/worker.js +2668 -476
- package/dist/worker.js.map +4 -4
- package/package.json +1 -2
package/dist/worker.js
CHANGED
|
@@ -8,7 +8,7 @@ import { createReadStream, unlinkSync, existsSync as existsSync18 } from "fs";
|
|
|
8
8
|
import { stat as stat9 } from "fs/promises";
|
|
9
9
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
10
10
|
import { hostname as getHostname2 } from "node:os";
|
|
11
|
-
import
|
|
11
|
+
import path29 from "node:path";
|
|
12
12
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
13
13
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
14
14
|
|
|
@@ -461,18 +461,18 @@ function resolveNodePtyPackageRoot() {
|
|
|
461
461
|
return null;
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
|
-
function ensureExecutableBit(
|
|
465
|
-
if (!existsSync2(
|
|
464
|
+
function ensureExecutableBit(path33) {
|
|
465
|
+
if (!existsSync2(path33)) {
|
|
466
466
|
return;
|
|
467
467
|
}
|
|
468
|
-
const stat10 = statSync(
|
|
468
|
+
const stat10 = statSync(path33);
|
|
469
469
|
if (!stat10.isFile()) {
|
|
470
470
|
return;
|
|
471
471
|
}
|
|
472
472
|
if ((stat10.mode & 73) === 73) {
|
|
473
473
|
return;
|
|
474
474
|
}
|
|
475
|
-
chmodSync(
|
|
475
|
+
chmodSync(path33, stat10.mode | 73);
|
|
476
476
|
}
|
|
477
477
|
function ensureNodePtySpawnHelperExecutableForCurrentPlatform(options = {}) {
|
|
478
478
|
const platform = options.platform ?? process.platform;
|
|
@@ -1090,11 +1090,11 @@ function parseGitRevParsePath(stdout) {
|
|
|
1090
1090
|
if (lines.length !== 1) {
|
|
1091
1091
|
return null;
|
|
1092
1092
|
}
|
|
1093
|
-
const
|
|
1094
|
-
if (!
|
|
1093
|
+
const path33 = lines[0]?.trim() ?? "";
|
|
1094
|
+
if (!path33 || path33.startsWith("--")) {
|
|
1095
1095
|
return null;
|
|
1096
1096
|
}
|
|
1097
|
-
return
|
|
1097
|
+
return path33;
|
|
1098
1098
|
}
|
|
1099
1099
|
function resolveGitRevParsePath(cwd, stdout) {
|
|
1100
1100
|
const parsed = parseGitRevParsePath(stdout);
|
|
@@ -1867,9 +1867,9 @@ async function deleteAppostleWorktree({
|
|
|
1867
1867
|
}
|
|
1868
1868
|
}
|
|
1869
1869
|
}
|
|
1870
|
-
async function pathExists(
|
|
1870
|
+
async function pathExists(path33) {
|
|
1871
1871
|
try {
|
|
1872
|
-
await stat(
|
|
1872
|
+
await stat(path33);
|
|
1873
1873
|
return true;
|
|
1874
1874
|
} catch (error) {
|
|
1875
1875
|
if (error.code === "ENOENT") {
|
|
@@ -1878,8 +1878,8 @@ async function pathExists(path30) {
|
|
|
1878
1878
|
throw error;
|
|
1879
1879
|
}
|
|
1880
1880
|
}
|
|
1881
|
-
async function removeDirectoryWithRetries(
|
|
1882
|
-
if (!await pathExists(
|
|
1881
|
+
async function removeDirectoryWithRetries(path33) {
|
|
1882
|
+
if (!await pathExists(path33)) {
|
|
1883
1883
|
return;
|
|
1884
1884
|
}
|
|
1885
1885
|
const delaysMs = [0, 100, 300, 700, 1500];
|
|
@@ -1889,17 +1889,17 @@ async function removeDirectoryWithRetries(path30) {
|
|
|
1889
1889
|
await new Promise((resolve14) => setTimeout(resolve14, delay));
|
|
1890
1890
|
}
|
|
1891
1891
|
try {
|
|
1892
|
-
await rm(
|
|
1893
|
-
if (!await pathExists(
|
|
1892
|
+
await rm(path33, { recursive: true, force: true });
|
|
1893
|
+
if (!await pathExists(path33)) {
|
|
1894
1894
|
return;
|
|
1895
1895
|
}
|
|
1896
|
-
lastError = new Error(`Directory still present after rm: ${
|
|
1896
|
+
lastError = new Error(`Directory still present after rm: ${path33}`);
|
|
1897
1897
|
} catch (error) {
|
|
1898
1898
|
lastError = error;
|
|
1899
1899
|
}
|
|
1900
1900
|
}
|
|
1901
|
-
if (await pathExists(
|
|
1902
|
-
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${
|
|
1901
|
+
if (await pathExists(path33)) {
|
|
1902
|
+
throw lastError instanceof Error ? lastError : new Error(`Failed to remove worktree directory: ${path33}`);
|
|
1903
1903
|
}
|
|
1904
1904
|
}
|
|
1905
1905
|
var createWorktree = async ({
|
|
@@ -3225,14 +3225,14 @@ var LoopStopResponseSchema = z7.object({
|
|
|
3225
3225
|
})
|
|
3226
3226
|
});
|
|
3227
3227
|
|
|
3228
|
-
// ../server/src/server/
|
|
3228
|
+
// ../server/src/server/quest/rpc-schemas.ts
|
|
3229
3229
|
import { z as z9 } from "zod";
|
|
3230
3230
|
|
|
3231
|
-
// ../server/src/server/
|
|
3231
|
+
// ../server/src/server/quest/types.ts
|
|
3232
3232
|
import { z as z8 } from "zod";
|
|
3233
|
-
var
|
|
3233
|
+
var QuestKindSchema = z8.enum(["loop", "ralph", "run", "orchestrate"]);
|
|
3234
3234
|
var FieldModeSchema = z8.enum(["locked", "inherit", "free"]);
|
|
3235
|
-
var
|
|
3235
|
+
var QuestRulesSchema = z8.object({
|
|
3236
3236
|
perCardRole: FieldModeSchema,
|
|
3237
3237
|
perCardModel: FieldModeSchema,
|
|
3238
3238
|
perCardPrompt: FieldModeSchema,
|
|
@@ -3256,79 +3256,101 @@ var CardWiringSchema = z8.object({
|
|
|
3256
3256
|
inputs: CardWiringInputsSchema,
|
|
3257
3257
|
outputs: CardWiringOutputsSchema
|
|
3258
3258
|
});
|
|
3259
|
-
var
|
|
3259
|
+
var QuestCardStatusSchema = z8.enum([
|
|
3260
3260
|
"queued",
|
|
3261
3261
|
"running",
|
|
3262
3262
|
"succeeded",
|
|
3263
3263
|
"failed",
|
|
3264
3264
|
"stopped"
|
|
3265
3265
|
]);
|
|
3266
|
-
var
|
|
3266
|
+
var QuestCardVerifySchema = z8.object({
|
|
3267
3267
|
verifierAgentId: z8.string().nullable(),
|
|
3268
3268
|
passed: z8.boolean().nullable(),
|
|
3269
3269
|
reason: z8.string().nullable(),
|
|
3270
3270
|
startedAt: z8.string().nullable(),
|
|
3271
3271
|
completedAt: z8.string().nullable()
|
|
3272
3272
|
});
|
|
3273
|
-
var
|
|
3273
|
+
var QuestCardSchema = z8.object({
|
|
3274
3274
|
/**
|
|
3275
3275
|
* 1..N for iteration cards. The summarizer card uses 0 as a sentinel —
|
|
3276
|
-
* it lives on `
|
|
3276
|
+
* it lives on `QuestRecord.summarizerCard`, not in the cards array,
|
|
3277
3277
|
* and the UI renders it as "Summary" when it sees index === 0.
|
|
3278
3278
|
*/
|
|
3279
3279
|
index: z8.number().int().nonnegative(),
|
|
3280
|
-
/** Role name resolved against the agents/.scanner. Null = use
|
|
3280
|
+
/** Role name resolved against the agents/.scanner. Null = use quest default. */
|
|
3281
3281
|
role: z8.string().nullable(),
|
|
3282
|
-
/** Model override. Null = use
|
|
3282
|
+
/** Model override. Null = use quest.defaultModel. */
|
|
3283
3283
|
model: z8.string().nullable(),
|
|
3284
|
-
/** Prompt override. Null = use
|
|
3284
|
+
/** Prompt override. Null = use quest.prompt. */
|
|
3285
3285
|
prompt: z8.string().nullable(),
|
|
3286
3286
|
wiring: CardWiringSchema,
|
|
3287
|
-
status:
|
|
3287
|
+
status: QuestCardStatusSchema,
|
|
3288
3288
|
/** Populated when the card runs. Null until the runner spawns the agent. */
|
|
3289
3289
|
agentId: z8.string().nullable(),
|
|
3290
3290
|
startedAt: z8.string().nullable(),
|
|
3291
3291
|
completedAt: z8.string().nullable(),
|
|
3292
3292
|
/** Ralph-only — null for loop cards. */
|
|
3293
|
-
verify:
|
|
3293
|
+
verify: QuestCardVerifySchema.nullable()
|
|
3294
3294
|
});
|
|
3295
|
-
var
|
|
3295
|
+
var QuestTerminationCountSchema = z8.object({
|
|
3296
3296
|
type: z8.literal("count"),
|
|
3297
3297
|
/** 1..10 in v1. */
|
|
3298
3298
|
n: z8.number().int().min(1).max(10)
|
|
3299
3299
|
});
|
|
3300
|
-
var
|
|
3300
|
+
var QuestTerminationVerifySchema = z8.object({
|
|
3301
3301
|
type: z8.literal("verify"),
|
|
3302
3302
|
/** Verifier agent system prompt. Null means rely on shell checks alone. */
|
|
3303
3303
|
verifyPrompt: z8.string().nullable(),
|
|
3304
3304
|
/** Shell commands that must exit 0 for the iteration to pass. */
|
|
3305
3305
|
verifyChecks: z8.array(z8.string()),
|
|
3306
|
+
/**
|
|
3307
|
+
* Exact completion token that MUST appear in worker output before the quest
|
|
3308
|
+
* may succeed. Mirrors Ralph's completion-promise guardrail.
|
|
3309
|
+
*/
|
|
3310
|
+
completionPromise: z8.string().trim().min(1).optional().default("<promise>COMPLETE</promise>"),
|
|
3306
3311
|
/** Hard cap on iterations regardless of verifier outcome. */
|
|
3307
3312
|
maxIterations: z8.number().int().min(1)
|
|
3308
3313
|
});
|
|
3309
|
-
var
|
|
3310
|
-
|
|
3311
|
-
|
|
3314
|
+
var QuestTerminationOrchestrateSchema = z8.object({
|
|
3315
|
+
type: z8.literal("orchestrate")
|
|
3316
|
+
});
|
|
3317
|
+
var QuestTerminationSchema = z8.discriminatedUnion("type", [
|
|
3318
|
+
QuestTerminationCountSchema,
|
|
3319
|
+
QuestTerminationVerifySchema,
|
|
3320
|
+
QuestTerminationOrchestrateSchema
|
|
3312
3321
|
]);
|
|
3313
|
-
var
|
|
3314
|
-
var
|
|
3322
|
+
var QuestStatusSchema = z8.enum(["running", "succeeded", "failed", "stopped"]);
|
|
3323
|
+
var QuestRecordSchema = z8.object({
|
|
3315
3324
|
id: z8.string(),
|
|
3316
|
-
kind:
|
|
3317
|
-
rules:
|
|
3325
|
+
kind: QuestKindSchema,
|
|
3326
|
+
rules: QuestRulesSchema,
|
|
3318
3327
|
cwd: z8.string(),
|
|
3319
3328
|
/** User's task prompt — fed to every card unless overridden per-card. */
|
|
3320
3329
|
prompt: z8.string(),
|
|
3321
3330
|
defaultProvider: AgentProviderSchema,
|
|
3322
3331
|
defaultModel: z8.string().nullable(),
|
|
3323
|
-
termination:
|
|
3332
|
+
termination: QuestTerminationSchema,
|
|
3324
3333
|
/**
|
|
3325
3334
|
* For "loop" kind: pre-rendered N cards from the start.
|
|
3326
3335
|
* For "ralph" kind: appended as iterations execute.
|
|
3327
3336
|
*/
|
|
3328
|
-
cards: z8.array(
|
|
3337
|
+
cards: z8.array(QuestCardSchema),
|
|
3329
3338
|
/** Populated when summarizer is enabled. Runs after all cards complete. */
|
|
3330
|
-
summarizerCard:
|
|
3331
|
-
|
|
3339
|
+
summarizerCard: QuestCardSchema.nullable(),
|
|
3340
|
+
/**
|
|
3341
|
+
* Queen-bee coordinator card for "orchestrate" kind. The queen reads the
|
|
3342
|
+
* hivemind doc, decides dispatch waves, and writes JSON decisions back. It
|
|
3343
|
+
* is promotable (same affordance as regular cards). Null for non-orchestrate
|
|
3344
|
+
* kinds. Optional + default so old persisted records remain valid.
|
|
3345
|
+
*/
|
|
3346
|
+
queenCard: QuestCardSchema.nullable().optional().default(null),
|
|
3347
|
+
/**
|
|
3348
|
+
* Path to the orchestration's hivemind markdown doc. Null for non-
|
|
3349
|
+
* orchestrate kinds. Optional + default so old persisted records remain
|
|
3350
|
+
* valid.
|
|
3351
|
+
*/
|
|
3352
|
+
hivemindPath: z8.string().nullable().optional().default(null),
|
|
3353
|
+
status: QuestStatusSchema,
|
|
3332
3354
|
createdAt: z8.string(),
|
|
3333
3355
|
updatedAt: z8.string(),
|
|
3334
3356
|
startedAt: z8.string().nullable(),
|
|
@@ -3336,107 +3358,293 @@ var WorkflowRecordSchema = z8.object({
|
|
|
3336
3358
|
stopRequestedAt: z8.string().nullable()
|
|
3337
3359
|
});
|
|
3338
3360
|
|
|
3339
|
-
// ../server/src/server/
|
|
3340
|
-
var
|
|
3361
|
+
// ../server/src/server/quest/rpc-schemas.ts
|
|
3362
|
+
var QuestCardConfigInputSchema = z9.object({
|
|
3341
3363
|
role: z9.string().nullable().optional(),
|
|
3342
3364
|
model: z9.string().nullable().optional(),
|
|
3343
3365
|
prompt: z9.string().nullable().optional()
|
|
3344
3366
|
});
|
|
3345
|
-
var
|
|
3346
|
-
type: z9.literal("
|
|
3367
|
+
var QuestRunRequestSchema = z9.object({
|
|
3368
|
+
type: z9.literal("quest/run"),
|
|
3347
3369
|
requestId: z9.string(),
|
|
3348
|
-
kind:
|
|
3370
|
+
kind: QuestKindSchema,
|
|
3349
3371
|
cwd: z9.string(),
|
|
3350
3372
|
prompt: z9.string().trim().min(1),
|
|
3351
3373
|
defaultProvider: AgentProviderSchema.optional(),
|
|
3352
3374
|
defaultModel: z9.string().trim().min(1).optional(),
|
|
3353
|
-
termination:
|
|
3375
|
+
termination: QuestTerminationSchema,
|
|
3354
3376
|
/** Per-card overrides (length must match termination.n for "count"). Optional. */
|
|
3355
|
-
cards: z9.array(
|
|
3377
|
+
cards: z9.array(QuestCardConfigInputSchema).optional(),
|
|
3356
3378
|
summarizerEnabled: z9.boolean().optional()
|
|
3357
3379
|
});
|
|
3358
|
-
var
|
|
3359
|
-
type: z9.literal("
|
|
3380
|
+
var QuestListRequestSchema = z9.object({
|
|
3381
|
+
type: z9.literal("quest/list"),
|
|
3360
3382
|
requestId: z9.string()
|
|
3361
3383
|
});
|
|
3362
|
-
var
|
|
3363
|
-
type: z9.literal("
|
|
3384
|
+
var QuestInspectRequestSchema = z9.object({
|
|
3385
|
+
type: z9.literal("quest/inspect"),
|
|
3364
3386
|
requestId: z9.string(),
|
|
3365
3387
|
id: z9.string().trim().min(1)
|
|
3366
3388
|
});
|
|
3367
|
-
var
|
|
3368
|
-
type: z9.literal("
|
|
3389
|
+
var QuestStopRequestSchema = z9.object({
|
|
3390
|
+
type: z9.literal("quest/stop"),
|
|
3369
3391
|
requestId: z9.string(),
|
|
3370
3392
|
id: z9.string().trim().min(1)
|
|
3371
3393
|
});
|
|
3372
|
-
var
|
|
3394
|
+
var QuestListItemSchema = z9.object({
|
|
3373
3395
|
id: z9.string(),
|
|
3374
|
-
kind:
|
|
3375
|
-
status:
|
|
3396
|
+
kind: QuestKindSchema,
|
|
3397
|
+
status: QuestStatusSchema,
|
|
3376
3398
|
cwd: z9.string(),
|
|
3377
3399
|
prompt: z9.string(),
|
|
3378
3400
|
createdAt: z9.string(),
|
|
3379
3401
|
updatedAt: z9.string()
|
|
3380
3402
|
});
|
|
3381
|
-
var
|
|
3382
|
-
type: z9.literal("
|
|
3403
|
+
var QuestRunResponseSchema = z9.object({
|
|
3404
|
+
type: z9.literal("quest/run/response"),
|
|
3405
|
+
payload: z9.object({
|
|
3406
|
+
requestId: z9.string(),
|
|
3407
|
+
quest: QuestRecordSchema.nullable(),
|
|
3408
|
+
error: z9.string().nullable()
|
|
3409
|
+
})
|
|
3410
|
+
});
|
|
3411
|
+
var QuestListResponseSchema = z9.object({
|
|
3412
|
+
type: z9.literal("quest/list/response"),
|
|
3413
|
+
payload: z9.object({
|
|
3414
|
+
requestId: z9.string(),
|
|
3415
|
+
quests: z9.array(QuestListItemSchema),
|
|
3416
|
+
error: z9.string().nullable()
|
|
3417
|
+
})
|
|
3418
|
+
});
|
|
3419
|
+
var QuestInspectResponseSchema = z9.object({
|
|
3420
|
+
type: z9.literal("quest/inspect/response"),
|
|
3421
|
+
payload: z9.object({
|
|
3422
|
+
requestId: z9.string(),
|
|
3423
|
+
quest: QuestRecordSchema.nullable(),
|
|
3424
|
+
error: z9.string().nullable()
|
|
3425
|
+
})
|
|
3426
|
+
});
|
|
3427
|
+
var QuestStopResponseSchema = z9.object({
|
|
3428
|
+
type: z9.literal("quest/stop/response"),
|
|
3429
|
+
payload: z9.object({
|
|
3430
|
+
requestId: z9.string(),
|
|
3431
|
+
quest: QuestRecordSchema.nullable(),
|
|
3432
|
+
error: z9.string().nullable()
|
|
3433
|
+
})
|
|
3434
|
+
});
|
|
3435
|
+
var QuestStreamMessageSchema = z9.object({
|
|
3436
|
+
type: z9.literal("quest/stream"),
|
|
3437
|
+
questId: z9.string(),
|
|
3438
|
+
quest: QuestRecordSchema
|
|
3439
|
+
});
|
|
3440
|
+
var RoleScopeSchema = z9.enum(["global", "project"]);
|
|
3441
|
+
var RoleEntrySchema = z9.object({
|
|
3442
|
+
id: z9.string(),
|
|
3443
|
+
scope: RoleScopeSchema,
|
|
3444
|
+
/** Category subfolder inside .roles/, or null for flat files. */
|
|
3445
|
+
category: z9.string().nullable().optional(),
|
|
3446
|
+
name: z9.string(),
|
|
3447
|
+
description: z9.string(),
|
|
3448
|
+
/** Trigger words from the `trigger-words` frontmatter key. */
|
|
3449
|
+
triggerWords: z9.array(z9.string()).optional().default([]),
|
|
3450
|
+
provider: z9.string(),
|
|
3451
|
+
model: z9.string(),
|
|
3452
|
+
mode: z9.string(),
|
|
3453
|
+
path: z9.string(),
|
|
3454
|
+
modifiedAt: z9.string(),
|
|
3455
|
+
size: z9.number()
|
|
3456
|
+
});
|
|
3457
|
+
var RoleFrontmatterSchema = z9.object({
|
|
3458
|
+
description: z9.string().optional(),
|
|
3459
|
+
triggerWords: z9.array(z9.string()).optional(),
|
|
3460
|
+
allowedTools: z9.array(z9.string()).optional(),
|
|
3461
|
+
provider: z9.string().optional(),
|
|
3462
|
+
model: z9.string().optional(),
|
|
3463
|
+
mode: z9.string().optional()
|
|
3464
|
+
});
|
|
3465
|
+
var RolesListRequestSchema = z9.object({
|
|
3466
|
+
type: z9.literal("roles/list"),
|
|
3467
|
+
requestId: z9.string(),
|
|
3468
|
+
workspaceRoot: z9.string().optional()
|
|
3469
|
+
});
|
|
3470
|
+
var RolesListResponseSchema = z9.object({
|
|
3471
|
+
type: z9.literal("roles/list/response"),
|
|
3383
3472
|
payload: z9.object({
|
|
3384
3473
|
requestId: z9.string(),
|
|
3385
|
-
|
|
3474
|
+
roles: z9.array(RoleEntrySchema),
|
|
3386
3475
|
error: z9.string().nullable()
|
|
3387
3476
|
})
|
|
3388
3477
|
});
|
|
3389
|
-
var
|
|
3390
|
-
type: z9.literal("
|
|
3478
|
+
var RolesListScopeRequestSchema = z9.object({
|
|
3479
|
+
type: z9.literal("roles/list-scope"),
|
|
3480
|
+
requestId: z9.string(),
|
|
3481
|
+
scope: RoleScopeSchema,
|
|
3482
|
+
workspaceRoot: z9.string().optional()
|
|
3483
|
+
});
|
|
3484
|
+
var RolesListScopeResponseSchema = z9.object({
|
|
3485
|
+
type: z9.literal("roles/list-scope/response"),
|
|
3391
3486
|
payload: z9.object({
|
|
3392
3487
|
requestId: z9.string(),
|
|
3393
|
-
|
|
3488
|
+
roles: z9.array(RoleEntrySchema),
|
|
3394
3489
|
error: z9.string().nullable()
|
|
3395
3490
|
})
|
|
3396
3491
|
});
|
|
3397
|
-
var
|
|
3398
|
-
type: z9.literal("
|
|
3492
|
+
var RoleCreateRequestSchema = z9.object({
|
|
3493
|
+
type: z9.literal("roles/create"),
|
|
3494
|
+
requestId: z9.string(),
|
|
3495
|
+
scope: RoleScopeSchema,
|
|
3496
|
+
name: z9.string(),
|
|
3497
|
+
category: z9.string().optional(),
|
|
3498
|
+
workspaceRoot: z9.string().optional()
|
|
3499
|
+
});
|
|
3500
|
+
var RoleCreateResponseSchema = z9.object({
|
|
3501
|
+
type: z9.literal("roles/create/response"),
|
|
3399
3502
|
payload: z9.object({
|
|
3400
3503
|
requestId: z9.string(),
|
|
3401
|
-
|
|
3504
|
+
/** Absolute path of the created .md file. Empty string when error is set. */
|
|
3505
|
+
path: z9.string(),
|
|
3402
3506
|
error: z9.string().nullable()
|
|
3403
3507
|
})
|
|
3404
3508
|
});
|
|
3405
|
-
var
|
|
3406
|
-
type: z9.literal("
|
|
3509
|
+
var RoleWriteFrontmatterRequestSchema = z9.object({
|
|
3510
|
+
type: z9.literal("roles/write-frontmatter"),
|
|
3511
|
+
requestId: z9.string(),
|
|
3512
|
+
/** Absolute path inside an allowlisted role root. */
|
|
3513
|
+
path: z9.string(),
|
|
3514
|
+
workspaceRoot: z9.string().optional(),
|
|
3515
|
+
frontmatter: RoleFrontmatterSchema
|
|
3516
|
+
});
|
|
3517
|
+
var RoleWriteFrontmatterResponseSchema = z9.object({
|
|
3518
|
+
type: z9.literal("roles/write-frontmatter/response"),
|
|
3407
3519
|
payload: z9.object({
|
|
3408
3520
|
requestId: z9.string(),
|
|
3409
|
-
|
|
3521
|
+
path: z9.string(),
|
|
3410
3522
|
error: z9.string().nullable()
|
|
3411
3523
|
})
|
|
3412
3524
|
});
|
|
3413
|
-
var
|
|
3414
|
-
type: z9.literal("
|
|
3415
|
-
|
|
3416
|
-
|
|
3525
|
+
var RoleMoveRequestSchema = z9.object({
|
|
3526
|
+
type: z9.literal("roles/move"),
|
|
3527
|
+
requestId: z9.string(),
|
|
3528
|
+
path: z9.string(),
|
|
3529
|
+
newName: z9.string().optional(),
|
|
3530
|
+
newCategory: z9.string().optional(),
|
|
3531
|
+
workspaceRoot: z9.string().optional()
|
|
3417
3532
|
});
|
|
3418
|
-
var
|
|
3533
|
+
var RoleMoveResponseSchema = z9.object({
|
|
3534
|
+
type: z9.literal("roles/move/response"),
|
|
3535
|
+
payload: z9.object({
|
|
3536
|
+
requestId: z9.string(),
|
|
3537
|
+
path: z9.string(),
|
|
3538
|
+
error: z9.string().nullable()
|
|
3539
|
+
})
|
|
3540
|
+
});
|
|
3541
|
+
var BrandScopeSchema = z9.literal("project");
|
|
3542
|
+
var BrandVariableTypeSchema = z9.enum([
|
|
3543
|
+
"color",
|
|
3544
|
+
"font",
|
|
3545
|
+
"asset",
|
|
3546
|
+
"text",
|
|
3547
|
+
"number",
|
|
3548
|
+
"select"
|
|
3549
|
+
]);
|
|
3550
|
+
var BrandSectionSchema = z9.enum(["visual", "voice"]);
|
|
3551
|
+
var BrandVariableSchema = z9.object({
|
|
3552
|
+
key: z9.string(),
|
|
3553
|
+
type: BrandVariableTypeSchema,
|
|
3554
|
+
label: z9.string(),
|
|
3555
|
+
value: z9.string(),
|
|
3556
|
+
section: BrandSectionSchema.optional(),
|
|
3557
|
+
options: z9.array(z9.string()).optional(),
|
|
3558
|
+
fileRef: z9.string().optional()
|
|
3559
|
+
});
|
|
3560
|
+
var BrandEntrySchema = z9.object({
|
|
3419
3561
|
id: z9.string(),
|
|
3420
|
-
scope:
|
|
3562
|
+
scope: BrandScopeSchema,
|
|
3421
3563
|
name: z9.string(),
|
|
3422
3564
|
description: z9.string(),
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
mode: z9.string().nullable(),
|
|
3565
|
+
tags: z9.array(z9.string()).optional().default([]),
|
|
3566
|
+
variables: z9.array(BrandVariableSchema).optional().default([]),
|
|
3426
3567
|
path: z9.string(),
|
|
3427
3568
|
modifiedAt: z9.string(),
|
|
3428
3569
|
size: z9.number()
|
|
3429
3570
|
});
|
|
3430
|
-
var
|
|
3431
|
-
|
|
3571
|
+
var BrandFrontmatterSchema = z9.object({
|
|
3572
|
+
description: z9.string().optional(),
|
|
3573
|
+
tags: z9.array(z9.string()).optional(),
|
|
3574
|
+
variables: z9.array(BrandVariableSchema).optional()
|
|
3575
|
+
});
|
|
3576
|
+
var BrandsListScopeRequestSchema = z9.object({
|
|
3577
|
+
type: z9.literal("brands/list-scope"),
|
|
3578
|
+
requestId: z9.string(),
|
|
3579
|
+
workspaceRoot: z9.string().optional()
|
|
3580
|
+
});
|
|
3581
|
+
var BrandsListScopeResponseSchema = z9.object({
|
|
3582
|
+
type: z9.literal("brands/list-scope/response"),
|
|
3583
|
+
payload: z9.object({
|
|
3584
|
+
requestId: z9.string(),
|
|
3585
|
+
brands: z9.array(BrandEntrySchema),
|
|
3586
|
+
error: z9.string().nullable()
|
|
3587
|
+
})
|
|
3588
|
+
});
|
|
3589
|
+
var BrandCreateRequestSchema = z9.object({
|
|
3590
|
+
type: z9.literal("brands/create"),
|
|
3432
3591
|
requestId: z9.string(),
|
|
3592
|
+
name: z9.string(),
|
|
3433
3593
|
workspaceRoot: z9.string().optional()
|
|
3434
3594
|
});
|
|
3435
|
-
var
|
|
3436
|
-
type: z9.literal("
|
|
3595
|
+
var BrandCreateResponseSchema = z9.object({
|
|
3596
|
+
type: z9.literal("brands/create/response"),
|
|
3597
|
+
payload: z9.object({
|
|
3598
|
+
requestId: z9.string(),
|
|
3599
|
+
path: z9.string(),
|
|
3600
|
+
error: z9.string().nullable()
|
|
3601
|
+
})
|
|
3602
|
+
});
|
|
3603
|
+
var BrandAssetCopyRequestSchema = z9.object({
|
|
3604
|
+
type: z9.literal("brands/asset-copy"),
|
|
3605
|
+
requestId: z9.string(),
|
|
3606
|
+
workspaceRoot: z9.string(),
|
|
3607
|
+
sourcePath: z9.string(),
|
|
3608
|
+
targetName: z9.string()
|
|
3609
|
+
});
|
|
3610
|
+
var BrandAssetUploadRequestSchema = z9.object({
|
|
3611
|
+
type: z9.literal("brands/asset-upload"),
|
|
3612
|
+
requestId: z9.string(),
|
|
3613
|
+
workspaceRoot: z9.string(),
|
|
3614
|
+
targetName: z9.string(),
|
|
3615
|
+
sourceName: z9.string().optional(),
|
|
3616
|
+
dataBase64: z9.string()
|
|
3617
|
+
});
|
|
3618
|
+
var BrandAssetUploadResponseSchema = z9.object({
|
|
3619
|
+
type: z9.literal("brands/asset-upload/response"),
|
|
3620
|
+
payload: z9.object({
|
|
3621
|
+
requestId: z9.string(),
|
|
3622
|
+
relativePath: z9.string(),
|
|
3623
|
+
absolutePath: z9.string(),
|
|
3624
|
+
error: z9.string().nullable()
|
|
3625
|
+
})
|
|
3626
|
+
});
|
|
3627
|
+
var BrandAssetCopyResponseSchema = z9.object({
|
|
3628
|
+
type: z9.literal("brands/asset-copy/response"),
|
|
3629
|
+
payload: z9.object({
|
|
3630
|
+
requestId: z9.string(),
|
|
3631
|
+
relativePath: z9.string(),
|
|
3632
|
+
absolutePath: z9.string(),
|
|
3633
|
+
error: z9.string().nullable()
|
|
3634
|
+
})
|
|
3635
|
+
});
|
|
3636
|
+
var BrandWriteFrontmatterRequestSchema = z9.object({
|
|
3637
|
+
type: z9.literal("brands/write-frontmatter"),
|
|
3638
|
+
requestId: z9.string(),
|
|
3639
|
+
path: z9.string(),
|
|
3640
|
+
workspaceRoot: z9.string().optional(),
|
|
3641
|
+
frontmatter: BrandFrontmatterSchema
|
|
3642
|
+
});
|
|
3643
|
+
var BrandWriteFrontmatterResponseSchema = z9.object({
|
|
3644
|
+
type: z9.literal("brands/write-frontmatter/response"),
|
|
3437
3645
|
payload: z9.object({
|
|
3438
3646
|
requestId: z9.string(),
|
|
3439
|
-
|
|
3647
|
+
path: z9.string(),
|
|
3440
3648
|
error: z9.string().nullable()
|
|
3441
3649
|
})
|
|
3442
3650
|
});
|
|
@@ -3990,6 +4198,7 @@ var UpdateAgentRequestMessageSchema = z10.object({
|
|
|
3990
4198
|
agentId: z10.string(),
|
|
3991
4199
|
name: z10.string().optional(),
|
|
3992
4200
|
labels: z10.record(z10.string()).optional(),
|
|
4201
|
+
internal: z10.boolean().optional(),
|
|
3993
4202
|
requestId: z10.string()
|
|
3994
4203
|
});
|
|
3995
4204
|
var SetVoiceModeMessageSchema = z10.object({
|
|
@@ -4779,6 +4988,25 @@ var FileDeleteRequestSchema = z10.object({
|
|
|
4779
4988
|
path: z10.string(),
|
|
4780
4989
|
requestId: z10.string()
|
|
4781
4990
|
});
|
|
4991
|
+
var FileExplorerDeleteRequestSchema = z10.object({
|
|
4992
|
+
type: z10.literal("file_explorer_delete_request"),
|
|
4993
|
+
cwd: z10.string(),
|
|
4994
|
+
path: z10.string(),
|
|
4995
|
+
requestId: z10.string()
|
|
4996
|
+
});
|
|
4997
|
+
var FileMkdirRequestSchema = z10.object({
|
|
4998
|
+
type: z10.literal("file_mkdir_request"),
|
|
4999
|
+
cwd: z10.string(),
|
|
5000
|
+
path: z10.string(),
|
|
5001
|
+
requestId: z10.string()
|
|
5002
|
+
});
|
|
5003
|
+
var FileMoveRequestSchema = z10.object({
|
|
5004
|
+
type: z10.literal("file_move_request"),
|
|
5005
|
+
cwd: z10.string(),
|
|
5006
|
+
sourcePath: z10.string(),
|
|
5007
|
+
destinationPath: z10.string(),
|
|
5008
|
+
requestId: z10.string()
|
|
5009
|
+
});
|
|
4782
5010
|
var ClearAgentAttentionMessageSchema = z10.object({
|
|
4783
5011
|
type: z10.literal("clear_agent_attention"),
|
|
4784
5012
|
agentId: z10.union([z10.string(), z10.array(z10.string())]),
|
|
@@ -5051,6 +5279,9 @@ var SessionInboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
5051
5279
|
FileExplorerRequestSchema,
|
|
5052
5280
|
FileWriteRequestSchema,
|
|
5053
5281
|
FileDeleteRequestSchema,
|
|
5282
|
+
FileExplorerDeleteRequestSchema,
|
|
5283
|
+
FileMkdirRequestSchema,
|
|
5284
|
+
FileMoveRequestSchema,
|
|
5054
5285
|
ProjectIconRequestSchema,
|
|
5055
5286
|
FileDownloadTokenRequestSchema,
|
|
5056
5287
|
ClearAgentAttentionMessageSchema,
|
|
@@ -5060,6 +5291,15 @@ var SessionInboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
5060
5291
|
ListSkillsRequestSchema,
|
|
5061
5292
|
CreateSkillRequestSchema,
|
|
5062
5293
|
WriteSkillFrontmatterRequestSchema,
|
|
5294
|
+
RolesListScopeRequestSchema,
|
|
5295
|
+
RoleCreateRequestSchema,
|
|
5296
|
+
RoleWriteFrontmatterRequestSchema,
|
|
5297
|
+
RoleMoveRequestSchema,
|
|
5298
|
+
BrandsListScopeRequestSchema,
|
|
5299
|
+
BrandCreateRequestSchema,
|
|
5300
|
+
BrandWriteFrontmatterRequestSchema,
|
|
5301
|
+
BrandAssetCopyRequestSchema,
|
|
5302
|
+
BrandAssetUploadRequestSchema,
|
|
5063
5303
|
RegisterPushTokenMessageSchema,
|
|
5064
5304
|
RegisterWebPushSubscriptionMessageSchema,
|
|
5065
5305
|
UnregisterWebPushSubscriptionMessageSchema,
|
|
@@ -5094,11 +5334,11 @@ var SessionInboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
5094
5334
|
LoopInspectRequestSchema,
|
|
5095
5335
|
LoopLogsRequestSchema,
|
|
5096
5336
|
LoopStopRequestSchema,
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5337
|
+
QuestRunRequestSchema,
|
|
5338
|
+
QuestListRequestSchema,
|
|
5339
|
+
QuestInspectRequestSchema,
|
|
5340
|
+
QuestStopRequestSchema,
|
|
5341
|
+
RolesListRequestSchema
|
|
5102
5342
|
]);
|
|
5103
5343
|
var ActivityLogPayloadSchema = z10.object({
|
|
5104
5344
|
id: z10.string(),
|
|
@@ -6224,6 +6464,34 @@ var FileDeleteResponseSchema = z10.object({
|
|
|
6224
6464
|
requestId: z10.string()
|
|
6225
6465
|
})
|
|
6226
6466
|
});
|
|
6467
|
+
var FileExplorerDeleteResponseSchema = z10.object({
|
|
6468
|
+
type: z10.literal("file_explorer_delete_response"),
|
|
6469
|
+
payload: z10.object({
|
|
6470
|
+
cwd: z10.string(),
|
|
6471
|
+
path: z10.string(),
|
|
6472
|
+
error: z10.string().nullable(),
|
|
6473
|
+
requestId: z10.string()
|
|
6474
|
+
})
|
|
6475
|
+
});
|
|
6476
|
+
var FileMkdirResponseSchema = z10.object({
|
|
6477
|
+
type: z10.literal("file_mkdir_response"),
|
|
6478
|
+
payload: z10.object({
|
|
6479
|
+
cwd: z10.string(),
|
|
6480
|
+
path: z10.string(),
|
|
6481
|
+
error: z10.string().nullable(),
|
|
6482
|
+
requestId: z10.string()
|
|
6483
|
+
})
|
|
6484
|
+
});
|
|
6485
|
+
var FileMoveResponseSchema = z10.object({
|
|
6486
|
+
type: z10.literal("file_move_response"),
|
|
6487
|
+
payload: z10.object({
|
|
6488
|
+
cwd: z10.string(),
|
|
6489
|
+
sourcePath: z10.string(),
|
|
6490
|
+
destinationPath: z10.string(),
|
|
6491
|
+
error: z10.string().nullable(),
|
|
6492
|
+
requestId: z10.string()
|
|
6493
|
+
})
|
|
6494
|
+
});
|
|
6227
6495
|
var ProjectIconSchema = z10.object({
|
|
6228
6496
|
data: z10.string(),
|
|
6229
6497
|
mimeType: z10.string()
|
|
@@ -6556,6 +6824,9 @@ var SessionOutboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
6556
6824
|
FileExplorerResponseSchema,
|
|
6557
6825
|
FileWriteResponseSchema,
|
|
6558
6826
|
FileDeleteResponseSchema,
|
|
6827
|
+
FileExplorerDeleteResponseSchema,
|
|
6828
|
+
FileMkdirResponseSchema,
|
|
6829
|
+
FileMoveResponseSchema,
|
|
6559
6830
|
ProjectIconResponseSchema,
|
|
6560
6831
|
FileDownloadTokenResponseSchema,
|
|
6561
6832
|
ListProviderModelsResponseMessageSchema,
|
|
@@ -6570,6 +6841,10 @@ var SessionOutboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
6570
6841
|
ListSkillsResponseSchema,
|
|
6571
6842
|
CreateSkillResponseSchema,
|
|
6572
6843
|
WriteSkillFrontmatterResponseSchema,
|
|
6844
|
+
RolesListScopeResponseSchema,
|
|
6845
|
+
RoleCreateResponseSchema,
|
|
6846
|
+
RoleWriteFrontmatterResponseSchema,
|
|
6847
|
+
RoleMoveResponseSchema,
|
|
6573
6848
|
ListTerminalsResponseSchema,
|
|
6574
6849
|
TerminalsChangedSchema,
|
|
6575
6850
|
CreateTerminalResponseSchema,
|
|
@@ -6596,11 +6871,16 @@ var SessionOutboundMessageSchema = z10.discriminatedUnion("type", [
|
|
|
6596
6871
|
LoopInspectResponseSchema,
|
|
6597
6872
|
LoopLogsResponseSchema,
|
|
6598
6873
|
LoopStopResponseSchema,
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6874
|
+
QuestRunResponseSchema,
|
|
6875
|
+
QuestListResponseSchema,
|
|
6876
|
+
QuestInspectResponseSchema,
|
|
6877
|
+
QuestStopResponseSchema,
|
|
6878
|
+
RolesListResponseSchema,
|
|
6879
|
+
BrandsListScopeResponseSchema,
|
|
6880
|
+
BrandCreateResponseSchema,
|
|
6881
|
+
BrandWriteFrontmatterResponseSchema,
|
|
6882
|
+
BrandAssetCopyResponseSchema,
|
|
6883
|
+
BrandAssetUploadResponseSchema,
|
|
6604
6884
|
GetVapidPublicKeyResponseSchema,
|
|
6605
6885
|
LinkAccountResponseMessageSchema
|
|
6606
6886
|
]);
|
|
@@ -8655,6 +8935,12 @@ async function resolveEditorLaunch(input) {
|
|
|
8655
8935
|
if (!executable) {
|
|
8656
8936
|
throw new Error(`Editor target unavailable: ${target.label}`);
|
|
8657
8937
|
}
|
|
8938
|
+
if (input.editorId === "finder") {
|
|
8939
|
+
return {
|
|
8940
|
+
command: executable,
|
|
8941
|
+
args: ["-R", input.path]
|
|
8942
|
+
};
|
|
8943
|
+
}
|
|
8658
8944
|
return {
|
|
8659
8945
|
command: executable,
|
|
8660
8946
|
args: [input.path]
|
|
@@ -9086,14 +9372,14 @@ var DictationStreamManager = class {
|
|
|
9086
9372
|
PCM_CHANNELS,
|
|
9087
9373
|
PCM_BITS_PER_SAMPLE
|
|
9088
9374
|
);
|
|
9089
|
-
const
|
|
9375
|
+
const path33 = await maybePersistDictationDebugAudio(
|
|
9090
9376
|
wavBuffer,
|
|
9091
9377
|
{ sessionId: state.sessionId, dictationId: state.dictationId, format: "audio/wav" },
|
|
9092
9378
|
this.logger,
|
|
9093
9379
|
state.debugChunkWriter?.folder
|
|
9094
9380
|
);
|
|
9095
|
-
state.debugRecordingPath =
|
|
9096
|
-
return
|
|
9381
|
+
state.debugRecordingPath = path33;
|
|
9382
|
+
return path33;
|
|
9097
9383
|
}
|
|
9098
9384
|
failDictationStream(dictationId, error, retryable) {
|
|
9099
9385
|
this.emit({
|
|
@@ -10559,14 +10845,14 @@ function parseDiff(diffText) {
|
|
|
10559
10845
|
const firstLine = lines[0];
|
|
10560
10846
|
const isNew = section.includes("new file mode") || section.includes("--- /dev/null");
|
|
10561
10847
|
const isDeleted = section.includes("deleted file mode") || section.includes("+++ /dev/null");
|
|
10562
|
-
let
|
|
10848
|
+
let path33 = "unknown";
|
|
10563
10849
|
const pathMatch = firstLine.match(/a\/(.*?) b\//);
|
|
10564
10850
|
if (pathMatch) {
|
|
10565
|
-
|
|
10851
|
+
path33 = pathMatch[1];
|
|
10566
10852
|
} else {
|
|
10567
10853
|
const newFileMatch = firstLine.match(/b\/(.+)$/);
|
|
10568
10854
|
if (newFileMatch) {
|
|
10569
|
-
|
|
10855
|
+
path33 = newFileMatch[1];
|
|
10570
10856
|
}
|
|
10571
10857
|
}
|
|
10572
10858
|
const hunks = [];
|
|
@@ -10610,7 +10896,7 @@ function parseDiff(diffText) {
|
|
|
10610
10896
|
if (currentHunk) {
|
|
10611
10897
|
hunks.push(currentHunk);
|
|
10612
10898
|
}
|
|
10613
|
-
files.push({ path:
|
|
10899
|
+
files.push({ path: path33, isNew, isDeleted, additions, deletions, hunks });
|
|
10614
10900
|
}
|
|
10615
10901
|
return files;
|
|
10616
10902
|
}
|
|
@@ -10666,9 +10952,9 @@ function buildTokenLookup(lineMap, highlighted) {
|
|
|
10666
10952
|
}
|
|
10667
10953
|
return lookup;
|
|
10668
10954
|
}
|
|
10669
|
-
function buildFullFileTokenLookup(fileContent,
|
|
10955
|
+
function buildFullFileTokenLookup(fileContent, path33) {
|
|
10670
10956
|
const lookup = /* @__PURE__ */ new Map();
|
|
10671
|
-
const highlighted = highlightCode(fileContent,
|
|
10957
|
+
const highlighted = highlightCode(fileContent, path33);
|
|
10672
10958
|
for (let i = 0; i < highlighted.length; i++) {
|
|
10673
10959
|
lookup.set(i + 1, highlighted[i]);
|
|
10674
10960
|
}
|
|
@@ -12373,11 +12659,11 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
12373
12659
|
}
|
|
12374
12660
|
continue;
|
|
12375
12661
|
}
|
|
12376
|
-
const
|
|
12377
|
-
if (!
|
|
12662
|
+
const path33 = tabParts[1];
|
|
12663
|
+
if (!path33) continue;
|
|
12378
12664
|
const code = rawStatus[0];
|
|
12379
12665
|
changes.push({
|
|
12380
|
-
path:
|
|
12666
|
+
path: path33,
|
|
12381
12667
|
status: rawStatus,
|
|
12382
12668
|
isNew: code === "A",
|
|
12383
12669
|
isDeleted: code === "D"
|
|
@@ -12412,9 +12698,9 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
12412
12698
|
}
|
|
12413
12699
|
return Array.from(byPath.values());
|
|
12414
12700
|
}
|
|
12415
|
-
async function readGitFileContentAtRef(cwd, ref,
|
|
12701
|
+
async function readGitFileContentAtRef(cwd, ref, path33) {
|
|
12416
12702
|
try {
|
|
12417
|
-
const { stdout } = await runGitCommand(["show", `${ref}:${
|
|
12703
|
+
const { stdout } = await runGitCommand(["show", `${ref}:${path33}`], {
|
|
12418
12704
|
cwd,
|
|
12419
12705
|
env: READ_ONLY_GIT_ENV2
|
|
12420
12706
|
});
|
|
@@ -12475,21 +12761,21 @@ async function getTrackedNumstatByPath(cwd, ref, ignoreWhitespace = false) {
|
|
|
12475
12761
|
const additionsField = parts[0] ?? "";
|
|
12476
12762
|
const deletionsField = parts[1] ?? "";
|
|
12477
12763
|
const rawPath = parts.slice(2).join(" ");
|
|
12478
|
-
const
|
|
12479
|
-
if (!
|
|
12764
|
+
const path33 = normalizeNumstatPath(rawPath);
|
|
12765
|
+
if (!path33) {
|
|
12480
12766
|
continue;
|
|
12481
12767
|
}
|
|
12482
12768
|
if (additionsField === "-" || deletionsField === "-") {
|
|
12483
|
-
stats.set(
|
|
12769
|
+
stats.set(path33, { additions: 0, deletions: 0, isBinary: true });
|
|
12484
12770
|
continue;
|
|
12485
12771
|
}
|
|
12486
12772
|
const additions = Number.parseInt(additionsField, 10);
|
|
12487
12773
|
const deletions = Number.parseInt(deletionsField, 10);
|
|
12488
12774
|
if (Number.isNaN(additions) || Number.isNaN(deletions)) {
|
|
12489
|
-
stats.set(
|
|
12775
|
+
stats.set(path33, null);
|
|
12490
12776
|
continue;
|
|
12491
12777
|
}
|
|
12492
|
-
stats.set(
|
|
12778
|
+
stats.set(path33, { additions, deletions, isBinary: false });
|
|
12493
12779
|
}
|
|
12494
12780
|
return stats;
|
|
12495
12781
|
}
|
|
@@ -13181,10 +13467,10 @@ async function listUncommittedFiles(cwd) {
|
|
|
13181
13467
|
if (dest) results.push({ path: dest, changeType: code });
|
|
13182
13468
|
continue;
|
|
13183
13469
|
}
|
|
13184
|
-
const
|
|
13185
|
-
if (!
|
|
13470
|
+
const path33 = parts[1];
|
|
13471
|
+
if (!path33) continue;
|
|
13186
13472
|
if (code === "A" || code === "M" || code === "D") {
|
|
13187
|
-
results.push({ path:
|
|
13473
|
+
results.push({ path: path33, changeType: code });
|
|
13188
13474
|
}
|
|
13189
13475
|
}
|
|
13190
13476
|
} catch {
|
|
@@ -16528,6 +16814,14 @@ var CLAUDE_MODELS = [
|
|
|
16528
16814
|
isDefault: true,
|
|
16529
16815
|
thinkingOptions: [...CLAUDE_THINKING_OPTIONS]
|
|
16530
16816
|
},
|
|
16817
|
+
{
|
|
16818
|
+
provider: "claude",
|
|
16819
|
+
id: "claude-sonnet-4-6[1m]",
|
|
16820
|
+
label: "Sonnet 4.6 1M",
|
|
16821
|
+
description: "Sonnet 4.6 with 1M context window",
|
|
16822
|
+
thinkingOptions: [...CLAUDE_THINKING_OPTIONS],
|
|
16823
|
+
betas: ["context-1m-2025-08-07"]
|
|
16824
|
+
},
|
|
16531
16825
|
{
|
|
16532
16826
|
provider: "claude",
|
|
16533
16827
|
id: "claude-sonnet-4-6",
|
|
@@ -16545,6 +16839,9 @@ var CLAUDE_MODELS = [
|
|
|
16545
16839
|
function getClaudeModels() {
|
|
16546
16840
|
return CLAUDE_MODELS.map((model) => ({ ...model }));
|
|
16547
16841
|
}
|
|
16842
|
+
function getClaudeModelBetas(modelId) {
|
|
16843
|
+
return CLAUDE_MODELS.find((m) => m.id === modelId)?.betas;
|
|
16844
|
+
}
|
|
16548
16845
|
function normalizeClaudeRuntimeModelId(value) {
|
|
16549
16846
|
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
16550
16847
|
if (!trimmed) {
|
|
@@ -19127,11 +19424,18 @@ var ClaudeAgentSession = class {
|
|
|
19127
19424
|
}
|
|
19128
19425
|
if (this.config.model) {
|
|
19129
19426
|
base.model = this.config.model;
|
|
19427
|
+
const modelBetas = getClaudeModelBetas(this.config.model);
|
|
19428
|
+
if (modelBetas?.length) {
|
|
19429
|
+
base.betas = [...base.betas ?? [], ...modelBetas];
|
|
19430
|
+
}
|
|
19130
19431
|
}
|
|
19131
19432
|
this.lastOptionsModel = base.model ?? null;
|
|
19132
19433
|
if (this.claudeSessionId) {
|
|
19133
19434
|
base.resume = this.claudeSessionId;
|
|
19134
19435
|
}
|
|
19436
|
+
if (this.config.allowedTools?.length) {
|
|
19437
|
+
base.allowedTools = [...base.allowedTools ?? [], ...this.config.allowedTools];
|
|
19438
|
+
}
|
|
19135
19439
|
if (this.runtimeSettings?.disallowedTools?.length) {
|
|
19136
19440
|
base.disallowedTools = [
|
|
19137
19441
|
...base.disallowedTools ?? [],
|
|
@@ -21362,14 +21666,14 @@ function codexApplyPatchToUnifiedDiff(text) {
|
|
|
21362
21666
|
for (const line of lines) {
|
|
21363
21667
|
const directive = parseCodexApplyPatchDirective(line);
|
|
21364
21668
|
if (directive) {
|
|
21365
|
-
const
|
|
21366
|
-
if (
|
|
21669
|
+
const path33 = normalizeDiffHeaderPath(directive.path);
|
|
21670
|
+
if (path33.length > 0) {
|
|
21367
21671
|
if (output.length > 0 && output[output.length - 1] !== "") {
|
|
21368
21672
|
output.push("");
|
|
21369
21673
|
}
|
|
21370
|
-
const left = directive.kind === "add" ? "/dev/null" : `a/${
|
|
21371
|
-
const right = directive.kind === "delete" ? "/dev/null" : `b/${
|
|
21372
|
-
output.push(`diff --git a/${
|
|
21674
|
+
const left = directive.kind === "add" ? "/dev/null" : `a/${path33}`;
|
|
21675
|
+
const right = directive.kind === "delete" ? "/dev/null" : `b/${path33}`;
|
|
21676
|
+
output.push(`diff --git a/${path33} b/${path33}`);
|
|
21373
21677
|
output.push(`--- ${left}`);
|
|
21374
21678
|
output.push(`+++ ${right}`);
|
|
21375
21679
|
sawDiffContent = true;
|
|
@@ -21439,9 +21743,9 @@ function asEditTextFields(text) {
|
|
|
21439
21743
|
function normalizeRolloutEditInput(input) {
|
|
21440
21744
|
if (typeof input === "string") {
|
|
21441
21745
|
const textFields2 = asEditTextFields(input);
|
|
21442
|
-
const
|
|
21746
|
+
const path33 = extractPatchPrimaryFilePath(input);
|
|
21443
21747
|
return {
|
|
21444
|
-
...
|
|
21748
|
+
...path33 ? { path: path33 } : {},
|
|
21445
21749
|
...textFields2.unifiedDiff ? { patch: textFields2.unifiedDiff } : {},
|
|
21446
21750
|
...textFields2.newString ? { content: textFields2.newString } : {}
|
|
21447
21751
|
};
|
|
@@ -21589,12 +21893,12 @@ function parseFileChangeDiff(entry) {
|
|
|
21589
21893
|
]);
|
|
21590
21894
|
}
|
|
21591
21895
|
function toFileChangeEntry(entry, options, fallbackPath) {
|
|
21592
|
-
const
|
|
21593
|
-
if (!
|
|
21896
|
+
const path33 = parseFileChangePath(entry, options, fallbackPath);
|
|
21897
|
+
if (!path33) {
|
|
21594
21898
|
return null;
|
|
21595
21899
|
}
|
|
21596
21900
|
return {
|
|
21597
|
-
path:
|
|
21901
|
+
path: path33,
|
|
21598
21902
|
kind: parseFileChangeKind(entry),
|
|
21599
21903
|
diff: parseFileChangeDiff(entry)
|
|
21600
21904
|
};
|
|
@@ -21616,12 +21920,12 @@ function parseFileChangeEntries(changes, options) {
|
|
|
21616
21920
|
if (singleEntry) {
|
|
21617
21921
|
return [singleEntry];
|
|
21618
21922
|
}
|
|
21619
|
-
return Object.entries(changes).map(([
|
|
21923
|
+
return Object.entries(changes).map(([path33, value]) => {
|
|
21620
21924
|
if (isRecord2(value)) {
|
|
21621
|
-
return toFileChangeEntry(value, options,
|
|
21925
|
+
return toFileChangeEntry(value, options, path33);
|
|
21622
21926
|
}
|
|
21623
21927
|
if (typeof value === "string") {
|
|
21624
|
-
const normalizedPath = normalizeCodexFilePath(
|
|
21928
|
+
const normalizedPath = normalizeCodexFilePath(path33.trim(), options?.cwd);
|
|
21625
21929
|
if (!normalizedPath) {
|
|
21626
21930
|
return null;
|
|
21627
21931
|
}
|
|
@@ -22366,16 +22670,16 @@ function isObjectSchemaNode(schema) {
|
|
|
22366
22670
|
const type = schema.type;
|
|
22367
22671
|
return isSchemaRecord(schema.properties) || type === "object" || Array.isArray(type) && type.includes("object");
|
|
22368
22672
|
}
|
|
22369
|
-
function normalizeCodexOutputSchemaNode(schema,
|
|
22673
|
+
function normalizeCodexOutputSchemaNode(schema, path33) {
|
|
22370
22674
|
if (Array.isArray(schema)) {
|
|
22371
|
-
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${
|
|
22675
|
+
return schema.map((entry, index) => normalizeCodexOutputSchemaNode(entry, `${path33}[${index}]`));
|
|
22372
22676
|
}
|
|
22373
22677
|
if (!isSchemaRecord(schema)) {
|
|
22374
22678
|
return schema;
|
|
22375
22679
|
}
|
|
22376
22680
|
const normalized = {};
|
|
22377
22681
|
for (const [key, value] of Object.entries(schema)) {
|
|
22378
|
-
normalized[key] = normalizeCodexOutputSchemaNode(value, `${
|
|
22682
|
+
normalized[key] = normalizeCodexOutputSchemaNode(value, `${path33}.${key}`);
|
|
22379
22683
|
}
|
|
22380
22684
|
if (!isObjectSchemaNode(normalized)) {
|
|
22381
22685
|
return normalized;
|
|
@@ -22384,7 +22688,7 @@ function normalizeCodexOutputSchemaNode(schema, path30) {
|
|
|
22384
22688
|
normalized.additionalProperties = false;
|
|
22385
22689
|
} else if (normalized.additionalProperties !== false) {
|
|
22386
22690
|
throw new Error(
|
|
22387
|
-
`Codex structured outputs require ${
|
|
22691
|
+
`Codex structured outputs require ${path33} to set additionalProperties to false for object schemas.`
|
|
22388
22692
|
);
|
|
22389
22693
|
}
|
|
22390
22694
|
const properties = isSchemaRecord(normalized.properties) ? normalized.properties : null;
|
|
@@ -23146,8 +23450,8 @@ function parseCodexPatchChanges(changes) {
|
|
|
23146
23450
|
}
|
|
23147
23451
|
];
|
|
23148
23452
|
}
|
|
23149
|
-
return Object.entries(recordChanges).map(([
|
|
23150
|
-
const normalizedPath =
|
|
23453
|
+
return Object.entries(recordChanges).map(([path33, value]) => {
|
|
23454
|
+
const normalizedPath = path33.trim();
|
|
23151
23455
|
if (!normalizedPath) {
|
|
23152
23456
|
return null;
|
|
23153
23457
|
}
|
|
@@ -30908,8 +31212,8 @@ function buildZodValidator(schema, schemaName) {
|
|
|
30908
31212
|
return { ok: true, value: result.data };
|
|
30909
31213
|
}
|
|
30910
31214
|
const errors = result.error.issues.map((issue) => {
|
|
30911
|
-
const
|
|
30912
|
-
return `${
|
|
31215
|
+
const path33 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
31216
|
+
return `${path33}: ${issue.message}`;
|
|
30913
31217
|
});
|
|
30914
31218
|
return { ok: false, errors };
|
|
30915
31219
|
}
|
|
@@ -30927,9 +31231,9 @@ function buildJsonSchemaValidator(schema) {
|
|
|
30927
31231
|
return { ok: true, value };
|
|
30928
31232
|
}
|
|
30929
31233
|
const errors = (validate.errors ?? []).map((error) => {
|
|
30930
|
-
const
|
|
31234
|
+
const path33 = error.instancePath && error.instancePath.length > 0 ? error.instancePath : "(root)";
|
|
30931
31235
|
const message = error.message ?? "is invalid";
|
|
30932
|
-
return `${
|
|
31236
|
+
return `${path33}: ${message}`;
|
|
30933
31237
|
});
|
|
30934
31238
|
return { ok: false, errors };
|
|
30935
31239
|
}
|
|
@@ -31968,6 +32272,12 @@ var IMAGE_MIME_TYPES = {
|
|
|
31968
32272
|
".webp": "image/webp",
|
|
31969
32273
|
".svg": "image/svg+xml"
|
|
31970
32274
|
};
|
|
32275
|
+
var FONT_MIME_TYPES = {
|
|
32276
|
+
".ttf": "font/ttf",
|
|
32277
|
+
".otf": "font/otf",
|
|
32278
|
+
".woff": "font/woff",
|
|
32279
|
+
".woff2": "font/woff2"
|
|
32280
|
+
};
|
|
31971
32281
|
async function listDirectoryEntries({
|
|
31972
32282
|
root,
|
|
31973
32283
|
relativePath = "."
|
|
@@ -32078,6 +32388,23 @@ async function deleteFile({ root, relativePath }) {
|
|
|
32078
32388
|
}
|
|
32079
32389
|
await fs6.unlink(filePath);
|
|
32080
32390
|
}
|
|
32391
|
+
async function deleteEntry({ root, relativePath }) {
|
|
32392
|
+
const entryPath = await resolveScopedPath({ root, relativePath });
|
|
32393
|
+
await fs6.rm(entryPath, { recursive: true, force: false });
|
|
32394
|
+
}
|
|
32395
|
+
async function createDirectory({ root, relativePath }) {
|
|
32396
|
+
const dirPath = await resolveScopedPath({ root, relativePath });
|
|
32397
|
+
await fs6.mkdir(dirPath, { recursive: true });
|
|
32398
|
+
}
|
|
32399
|
+
async function moveEntry({
|
|
32400
|
+
root,
|
|
32401
|
+
sourcePath,
|
|
32402
|
+
destinationPath
|
|
32403
|
+
}) {
|
|
32404
|
+
const src = await resolveScopedPath({ root, relativePath: sourcePath });
|
|
32405
|
+
const dest = await resolveScopedPath({ root, relativePath: destinationPath });
|
|
32406
|
+
await fs6.rename(src, dest);
|
|
32407
|
+
}
|
|
32081
32408
|
async function getDownloadableFileInfo({ root, relativePath }) {
|
|
32082
32409
|
const filePath = await resolveScopedPath({ root, relativePath });
|
|
32083
32410
|
const stats = await fs6.stat(filePath);
|
|
@@ -32087,7 +32414,9 @@ async function getDownloadableFileInfo({ root, relativePath }) {
|
|
|
32087
32414
|
const ext = path11.extname(filePath).toLowerCase();
|
|
32088
32415
|
let mimeType = "application/octet-stream";
|
|
32089
32416
|
if (ext in IMAGE_MIME_TYPES) {
|
|
32090
|
-
mimeType = IMAGE_MIME_TYPES[ext];
|
|
32417
|
+
mimeType = IMAGE_MIME_TYPES[ext] ?? mimeType;
|
|
32418
|
+
} else if (ext in FONT_MIME_TYPES) {
|
|
32419
|
+
mimeType = FONT_MIME_TYPES[ext] ?? mimeType;
|
|
32091
32420
|
} else {
|
|
32092
32421
|
const handle = await fs6.open(filePath, "r");
|
|
32093
32422
|
const sample = Buffer.alloc(8192);
|
|
@@ -32508,15 +32837,15 @@ async function getProjectIcon(projectDir) {
|
|
|
32508
32837
|
|
|
32509
32838
|
// ../server/src/utils/path.ts
|
|
32510
32839
|
import os5 from "os";
|
|
32511
|
-
function expandTilde(
|
|
32512
|
-
if (
|
|
32840
|
+
function expandTilde(path33) {
|
|
32841
|
+
if (path33.startsWith("~/")) {
|
|
32513
32842
|
const homeDir3 = process.env.HOME || os5.homedir();
|
|
32514
|
-
return
|
|
32843
|
+
return path33.replace("~", homeDir3);
|
|
32515
32844
|
}
|
|
32516
|
-
if (
|
|
32845
|
+
if (path33 === "~") {
|
|
32517
32846
|
return process.env.HOME || os5.homedir();
|
|
32518
32847
|
}
|
|
32519
|
-
return
|
|
32848
|
+
return path33;
|
|
32520
32849
|
}
|
|
32521
32850
|
|
|
32522
32851
|
// ../server/src/server/skills/scanner.ts
|
|
@@ -34023,7 +34352,7 @@ function buildChatMentionNotification(input) {
|
|
|
34023
34352
|
].join("\n");
|
|
34024
34353
|
}
|
|
34025
34354
|
|
|
34026
|
-
// ../server/src/server/
|
|
34355
|
+
// ../server/src/server/roles/scanner.ts
|
|
34027
34356
|
import fs9 from "node:fs/promises";
|
|
34028
34357
|
import os7 from "node:os";
|
|
34029
34358
|
import path16 from "node:path";
|
|
@@ -34036,11 +34365,41 @@ function resolveScopeDir2(scope, workspaceRoot) {
|
|
|
34036
34365
|
if (!workspaceRoot) {
|
|
34037
34366
|
throw new Error('workspaceRoot is required for scope "project"');
|
|
34038
34367
|
}
|
|
34039
|
-
return path16.join(workspaceRoot, ".
|
|
34368
|
+
return path16.join(workspaceRoot, ".roles");
|
|
34369
|
+
}
|
|
34370
|
+
return path16.join(homeDir2(), ".appostle", ".roles");
|
|
34371
|
+
}
|
|
34372
|
+
function allowedRoots2(workspaceRoot) {
|
|
34373
|
+
const roots = [path16.join(homeDir2(), ".appostle", ".roles")];
|
|
34374
|
+
if (workspaceRoot) {
|
|
34375
|
+
roots.push(path16.join(workspaceRoot, ".roles"));
|
|
34040
34376
|
}
|
|
34041
|
-
return
|
|
34377
|
+
return roots.map((r) => path16.resolve(r));
|
|
34378
|
+
}
|
|
34379
|
+
function isInsideAllowedRoot2(absPath, workspaceRoot) {
|
|
34380
|
+
const resolved = path16.resolve(absPath);
|
|
34381
|
+
for (const root of allowedRoots2(workspaceRoot)) {
|
|
34382
|
+
const rel = path16.relative(root, resolved);
|
|
34383
|
+
if (rel === "" || !rel.startsWith("..") && !path16.isAbsolute(rel)) {
|
|
34384
|
+
return true;
|
|
34385
|
+
}
|
|
34386
|
+
}
|
|
34387
|
+
return false;
|
|
34042
34388
|
}
|
|
34043
34389
|
var FRONTMATTER_RE2 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
34390
|
+
function parseRoleFile(text) {
|
|
34391
|
+
const match = FRONTMATTER_RE2.exec(text);
|
|
34392
|
+
if (!match) {
|
|
34393
|
+
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
34394
|
+
}
|
|
34395
|
+
const yamlBlock = match[1] ?? "";
|
|
34396
|
+
const body = match[2] ?? "";
|
|
34397
|
+
return {
|
|
34398
|
+
rawFrontmatterLines: yamlBlock.split(/\r?\n/),
|
|
34399
|
+
body,
|
|
34400
|
+
hadFrontmatter: true
|
|
34401
|
+
};
|
|
34402
|
+
}
|
|
34044
34403
|
function unquote(value) {
|
|
34045
34404
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
34046
34405
|
return value.slice(1, -1);
|
|
@@ -34048,36 +34407,165 @@ function unquote(value) {
|
|
|
34048
34407
|
return value;
|
|
34049
34408
|
}
|
|
34050
34409
|
function parseFrontmatter(text) {
|
|
34051
|
-
const match = FRONTMATTER_RE2.exec(text);
|
|
34052
34410
|
const empty = {
|
|
34053
34411
|
description: "",
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34057
|
-
|
|
34058
|
-
|
|
34059
|
-
|
|
34060
|
-
}
|
|
34061
|
-
|
|
34412
|
+
triggerWords: [],
|
|
34413
|
+
allowedTools: [],
|
|
34414
|
+
provider: "",
|
|
34415
|
+
model: "",
|
|
34416
|
+
mode: ""
|
|
34417
|
+
};
|
|
34418
|
+
const { rawFrontmatterLines: lines, hadFrontmatter } = parseRoleFile(text);
|
|
34419
|
+
if (!hadFrontmatter) return empty;
|
|
34062
34420
|
const out = { ...empty };
|
|
34063
|
-
|
|
34421
|
+
let i = 0;
|
|
34422
|
+
while (i < lines.length) {
|
|
34423
|
+
const line = lines[i] ?? "";
|
|
34064
34424
|
const m = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
34065
|
-
if (!m)
|
|
34425
|
+
if (!m) {
|
|
34426
|
+
i++;
|
|
34427
|
+
continue;
|
|
34428
|
+
}
|
|
34066
34429
|
const key = m[1];
|
|
34067
34430
|
const value = unquote((m[2] ?? "").trim());
|
|
34068
34431
|
if (key === "description") {
|
|
34069
34432
|
out.description = value;
|
|
34070
34433
|
} else if (key === "provider") {
|
|
34071
|
-
out.provider = value
|
|
34434
|
+
out.provider = value;
|
|
34072
34435
|
} else if (key === "model") {
|
|
34073
|
-
out.model = value
|
|
34436
|
+
out.model = value;
|
|
34074
34437
|
} else if (key === "mode") {
|
|
34075
|
-
out.mode = value
|
|
34438
|
+
out.mode = value;
|
|
34439
|
+
} else if (key === "trigger-words" || key === "allowed-tools") {
|
|
34440
|
+
const target = key === "trigger-words" ? "triggerWords" : "allowedTools";
|
|
34441
|
+
if (value) {
|
|
34442
|
+
out[target] = value.replace(/^\[|\]$/g, "").split(",").map((s) => unquote(s.trim())).filter((s) => s.length > 0);
|
|
34443
|
+
} else {
|
|
34444
|
+
const items = [];
|
|
34445
|
+
let j = i + 1;
|
|
34446
|
+
while (j < lines.length) {
|
|
34447
|
+
const next = lines[j] ?? "";
|
|
34448
|
+
const itemMatch = /^\s+-\s+(.+)$/.exec(next);
|
|
34449
|
+
if (!itemMatch) break;
|
|
34450
|
+
items.push(unquote(itemMatch[1].trim()));
|
|
34451
|
+
j++;
|
|
34452
|
+
}
|
|
34453
|
+
out[target] = items;
|
|
34454
|
+
i = j;
|
|
34455
|
+
continue;
|
|
34456
|
+
}
|
|
34076
34457
|
}
|
|
34458
|
+
i++;
|
|
34077
34459
|
}
|
|
34078
34460
|
return out;
|
|
34079
34461
|
}
|
|
34080
|
-
|
|
34462
|
+
var OWNED_KEYS = [
|
|
34463
|
+
"name",
|
|
34464
|
+
"category",
|
|
34465
|
+
"description",
|
|
34466
|
+
"trigger-words",
|
|
34467
|
+
"allowed-tools",
|
|
34468
|
+
"provider",
|
|
34469
|
+
"model",
|
|
34470
|
+
"mode"
|
|
34471
|
+
];
|
|
34472
|
+
function findOwnedSpans2(lines) {
|
|
34473
|
+
const spans = [];
|
|
34474
|
+
let i = 0;
|
|
34475
|
+
while (i < lines.length) {
|
|
34476
|
+
const line = lines[i] ?? "";
|
|
34477
|
+
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
34478
|
+
if (!keyMatch) {
|
|
34479
|
+
i++;
|
|
34480
|
+
continue;
|
|
34481
|
+
}
|
|
34482
|
+
const key = keyMatch[1];
|
|
34483
|
+
const value = keyMatch[2];
|
|
34484
|
+
if (!OWNED_KEYS.includes(key)) {
|
|
34485
|
+
i++;
|
|
34486
|
+
continue;
|
|
34487
|
+
}
|
|
34488
|
+
let end = i + 1;
|
|
34489
|
+
if (value === "") {
|
|
34490
|
+
while (end < lines.length) {
|
|
34491
|
+
const next = lines[end] ?? "";
|
|
34492
|
+
if (/^\s+-\s+/.test(next) || /^\s*$/.test(next)) {
|
|
34493
|
+
end++;
|
|
34494
|
+
} else {
|
|
34495
|
+
break;
|
|
34496
|
+
}
|
|
34497
|
+
}
|
|
34498
|
+
}
|
|
34499
|
+
spans.push({ key, startLine: i, endLine: end });
|
|
34500
|
+
i = end;
|
|
34501
|
+
}
|
|
34502
|
+
return spans;
|
|
34503
|
+
}
|
|
34504
|
+
function emitFrontmatterValue2(key, value) {
|
|
34505
|
+
if (key === "trigger-words" || key === "allowed-tools") {
|
|
34506
|
+
if (!Array.isArray(value) || value.length === 0) return [];
|
|
34507
|
+
return [`${key}:`, ...value.map((v) => ` - ${v}`)];
|
|
34508
|
+
}
|
|
34509
|
+
const text = typeof value === "string" ? value : "";
|
|
34510
|
+
if (text.length === 0) return [];
|
|
34511
|
+
const needsQuote = /[:#\n]/.test(text) || text.startsWith(" ") || text.endsWith(" ");
|
|
34512
|
+
const formatted = needsQuote ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
34513
|
+
return [`${key}: ${formatted}`];
|
|
34514
|
+
}
|
|
34515
|
+
function rewriteFrontmatter2(originalLines, next) {
|
|
34516
|
+
const spans = findOwnedSpans2(originalLines);
|
|
34517
|
+
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
34518
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
34519
|
+
if (next.name !== void 0) {
|
|
34520
|
+
replacements.set("name", emitFrontmatterValue2("name", next.name));
|
|
34521
|
+
}
|
|
34522
|
+
if (next.category !== void 0) {
|
|
34523
|
+
replacements.set("category", emitFrontmatterValue2("category", next.category));
|
|
34524
|
+
}
|
|
34525
|
+
if (next.description !== void 0) {
|
|
34526
|
+
replacements.set("description", emitFrontmatterValue2("description", next.description));
|
|
34527
|
+
}
|
|
34528
|
+
if (next.triggerWords !== void 0) {
|
|
34529
|
+
replacements.set("trigger-words", emitFrontmatterValue2("trigger-words", next.triggerWords));
|
|
34530
|
+
}
|
|
34531
|
+
if (next.allowedTools !== void 0) {
|
|
34532
|
+
replacements.set("allowed-tools", emitFrontmatterValue2("allowed-tools", next.allowedTools));
|
|
34533
|
+
}
|
|
34534
|
+
if (next.provider !== void 0) {
|
|
34535
|
+
replacements.set("provider", emitFrontmatterValue2("provider", next.provider));
|
|
34536
|
+
}
|
|
34537
|
+
if (next.model !== void 0) {
|
|
34538
|
+
replacements.set("model", emitFrontmatterValue2("model", next.model));
|
|
34539
|
+
}
|
|
34540
|
+
if (next.mode !== void 0) {
|
|
34541
|
+
replacements.set("mode", emitFrontmatterValue2("mode", next.mode));
|
|
34542
|
+
}
|
|
34543
|
+
const out = [];
|
|
34544
|
+
let i = 0;
|
|
34545
|
+
while (i < originalLines.length) {
|
|
34546
|
+
const span = spans.find((s) => s.startLine === i);
|
|
34547
|
+
if (span) {
|
|
34548
|
+
if (replacements.has(span.key)) {
|
|
34549
|
+
out.push(...replacements.get(span.key) ?? []);
|
|
34550
|
+
} else {
|
|
34551
|
+
for (let j = span.startLine; j < span.endLine; j++) {
|
|
34552
|
+
out.push(originalLines[j] ?? "");
|
|
34553
|
+
}
|
|
34554
|
+
}
|
|
34555
|
+
i = span.endLine;
|
|
34556
|
+
continue;
|
|
34557
|
+
}
|
|
34558
|
+
out.push(originalLines[i] ?? "");
|
|
34559
|
+
i++;
|
|
34560
|
+
}
|
|
34561
|
+
for (const key of OWNED_KEYS) {
|
|
34562
|
+
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
34563
|
+
out.push(...replacements.get(key) ?? []);
|
|
34564
|
+
}
|
|
34565
|
+
}
|
|
34566
|
+
return out;
|
|
34567
|
+
}
|
|
34568
|
+
async function readRolesFromDir(scope, dir, category) {
|
|
34081
34569
|
let entries;
|
|
34082
34570
|
try {
|
|
34083
34571
|
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
@@ -34103,13 +34591,23 @@ async function readRolesFromDir(scope, dir) {
|
|
|
34103
34591
|
const text = await fs9.readFile(fullPath, "utf8");
|
|
34104
34592
|
parsed = parseFrontmatter(text);
|
|
34105
34593
|
} catch {
|
|
34106
|
-
parsed = {
|
|
34594
|
+
parsed = {
|
|
34595
|
+
description: "",
|
|
34596
|
+
triggerWords: [],
|
|
34597
|
+
allowedTools: [],
|
|
34598
|
+
provider: "",
|
|
34599
|
+
model: "",
|
|
34600
|
+
mode: ""
|
|
34601
|
+
};
|
|
34107
34602
|
}
|
|
34603
|
+
const id = category ? `${scope}:${category}/${name}` : `${scope}:${name}`;
|
|
34108
34604
|
results.push({
|
|
34109
|
-
id
|
|
34605
|
+
id,
|
|
34110
34606
|
scope,
|
|
34607
|
+
category,
|
|
34111
34608
|
name,
|
|
34112
34609
|
description: parsed.description,
|
|
34610
|
+
triggerWords: parsed.triggerWords,
|
|
34113
34611
|
provider: parsed.provider,
|
|
34114
34612
|
model: parsed.model,
|
|
34115
34613
|
mode: parsed.mode,
|
|
@@ -34118,16 +34616,635 @@ async function readRolesFromDir(scope, dir) {
|
|
|
34118
34616
|
size: stat10.size
|
|
34119
34617
|
});
|
|
34120
34618
|
}
|
|
34619
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
34121
34620
|
return results;
|
|
34122
34621
|
}
|
|
34123
|
-
async function
|
|
34124
|
-
|
|
34125
|
-
|
|
34126
|
-
|
|
34127
|
-
|
|
34128
|
-
|
|
34622
|
+
async function readRolesFromScopeDir(scope, scopeDir) {
|
|
34623
|
+
let topEntries;
|
|
34624
|
+
try {
|
|
34625
|
+
topEntries = await fs9.readdir(scopeDir, { withFileTypes: true });
|
|
34626
|
+
} catch {
|
|
34627
|
+
return [];
|
|
34628
|
+
}
|
|
34629
|
+
const flat = await readRolesFromDir(scope, scopeDir, null);
|
|
34630
|
+
const categoryResults = await Promise.all(
|
|
34631
|
+
topEntries.filter((e) => e.isDirectory() && NAME_REGEX2.test(e.name)).map((e) => readRolesFromDir(scope, path16.join(scopeDir, e.name), e.name))
|
|
34632
|
+
);
|
|
34633
|
+
const all = [...flat, ...categoryResults.flat()];
|
|
34634
|
+
all.sort((a, b) => {
|
|
34635
|
+
const catA = a.category ?? "";
|
|
34636
|
+
const catB = b.category ?? "";
|
|
34637
|
+
if (catA !== catB) return catA.localeCompare(catB);
|
|
34638
|
+
return a.name.localeCompare(b.name);
|
|
34639
|
+
});
|
|
34640
|
+
return all;
|
|
34641
|
+
}
|
|
34642
|
+
async function listRoles(args) {
|
|
34643
|
+
if (args.scope === "project") {
|
|
34644
|
+
return readRolesFromScopeDir("project", resolveScopeDir2("project", args.workspaceRoot));
|
|
34645
|
+
}
|
|
34646
|
+
if (args.scope === "global") {
|
|
34647
|
+
return readRolesFromScopeDir("global", resolveScopeDir2("global"));
|
|
34648
|
+
}
|
|
34649
|
+
const project = args.workspaceRoot ? await readRolesFromScopeDir("project", resolveScopeDir2("project", args.workspaceRoot)) : [];
|
|
34650
|
+
const global = await readRolesFromScopeDir("global", resolveScopeDir2("global"));
|
|
34651
|
+
const projectIds = new Set(project.map((r) => r.id));
|
|
34652
|
+
const merged = [...project, ...global.filter((r) => !projectIds.has(r.id))];
|
|
34653
|
+
merged.sort((a, b) => {
|
|
34654
|
+
const catA = a.category ?? "";
|
|
34655
|
+
const catB = b.category ?? "";
|
|
34656
|
+
if (catA !== catB) return catA.localeCompare(catB);
|
|
34657
|
+
return a.name.localeCompare(b.name);
|
|
34658
|
+
});
|
|
34129
34659
|
return merged;
|
|
34130
34660
|
}
|
|
34661
|
+
async function resolveRole(args) {
|
|
34662
|
+
const roles = await listRoles({ workspaceRoot: args.workspaceRoot });
|
|
34663
|
+
const match = roles.find((r) => r.name === args.roleName);
|
|
34664
|
+
if (!match) return null;
|
|
34665
|
+
try {
|
|
34666
|
+
const text = await fs9.readFile(match.path, "utf8");
|
|
34667
|
+
const parsed = parseRoleFile(text);
|
|
34668
|
+
const fm = parseFrontmatter(text);
|
|
34669
|
+
const body = parsed.body.trim();
|
|
34670
|
+
if (!body && fm.allowedTools.length === 0) return null;
|
|
34671
|
+
return { body, allowedTools: fm.allowedTools };
|
|
34672
|
+
} catch {
|
|
34673
|
+
return null;
|
|
34674
|
+
}
|
|
34675
|
+
}
|
|
34676
|
+
async function createRole(args) {
|
|
34677
|
+
if (!NAME_REGEX2.test(args.name)) {
|
|
34678
|
+
throw new Error(
|
|
34679
|
+
`Invalid role name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
34680
|
+
);
|
|
34681
|
+
}
|
|
34682
|
+
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
34683
|
+
throw new Error(`Role name must not contain path separators or "..".`);
|
|
34684
|
+
}
|
|
34685
|
+
const scopeDir = resolveScopeDir2(args.scope, args.workspaceRoot);
|
|
34686
|
+
const dir = args.category ? path16.join(scopeDir, args.category) : scopeDir;
|
|
34687
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
34688
|
+
const filePath = path16.join(dir, `${args.name}.md`);
|
|
34689
|
+
try {
|
|
34690
|
+
await fs9.access(filePath);
|
|
34691
|
+
throw new Error(`A role named "${args.name}" already exists at ${filePath}`);
|
|
34692
|
+
} catch (err) {
|
|
34693
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
34694
|
+
throw err;
|
|
34695
|
+
}
|
|
34696
|
+
if (err instanceof Error && err.message.includes("already exists")) {
|
|
34697
|
+
throw err;
|
|
34698
|
+
}
|
|
34699
|
+
}
|
|
34700
|
+
const initial = buildStarterRole(args.name);
|
|
34701
|
+
await fs9.writeFile(filePath, initial, "utf8");
|
|
34702
|
+
return { path: filePath };
|
|
34703
|
+
}
|
|
34704
|
+
function buildStarterRole(name) {
|
|
34705
|
+
return `---
|
|
34706
|
+
name: ${name}
|
|
34707
|
+
description: ""
|
|
34708
|
+
trigger-words: []
|
|
34709
|
+
allowed-tools: []
|
|
34710
|
+
provider: claude/opus
|
|
34711
|
+
mode: default
|
|
34712
|
+
---
|
|
34713
|
+
|
|
34714
|
+
# ${name}
|
|
34715
|
+
|
|
34716
|
+
You are a ${name}. Describe the role's behavior, scope, and any constraints
|
|
34717
|
+
here. The body of this file is loaded as the agent's system prompt when
|
|
34718
|
+
the role is invoked.
|
|
34719
|
+
`;
|
|
34720
|
+
}
|
|
34721
|
+
async function writeRoleFrontmatter(args, workspaceRoot) {
|
|
34722
|
+
if (!path16.isAbsolute(args.path)) {
|
|
34723
|
+
throw new Error(`writeRoleFrontmatter expects an absolute path; got "${args.path}"`);
|
|
34724
|
+
}
|
|
34725
|
+
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
34726
|
+
throw new Error(`Path "${args.path}" is not inside an allowlisted role root`);
|
|
34727
|
+
}
|
|
34728
|
+
let original;
|
|
34729
|
+
try {
|
|
34730
|
+
original = await fs9.readFile(args.path, "utf8");
|
|
34731
|
+
} catch (err) {
|
|
34732
|
+
throw new Error(
|
|
34733
|
+
`Failed to read role file: ${err instanceof Error ? err.message : String(err)}`
|
|
34734
|
+
);
|
|
34735
|
+
}
|
|
34736
|
+
const parsed = parseRoleFile(original);
|
|
34737
|
+
const newLines = rewriteFrontmatter2(parsed.rawFrontmatterLines, args.frontmatter);
|
|
34738
|
+
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
34739
|
+
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
34740
|
+
${parsed.body}` : `${nextFrontmatter}
|
|
34741
|
+
|
|
34742
|
+
${original}`;
|
|
34743
|
+
await fs9.writeFile(args.path, nextContent, "utf8");
|
|
34744
|
+
}
|
|
34745
|
+
async function moveRole(args, workspaceRoot) {
|
|
34746
|
+
if (!path16.isAbsolute(args.path)) {
|
|
34747
|
+
throw new Error(`moveRole expects an absolute path; got "${args.path}"`);
|
|
34748
|
+
}
|
|
34749
|
+
if (!isInsideAllowedRoot2(args.path, workspaceRoot)) {
|
|
34750
|
+
throw new Error(`Path "${args.path}" is not inside an allowlisted role root`);
|
|
34751
|
+
}
|
|
34752
|
+
const oldDir = path16.dirname(args.path);
|
|
34753
|
+
const oldFilename = path16.basename(args.path, ".md");
|
|
34754
|
+
const roots = allowedRoots2(workspaceRoot);
|
|
34755
|
+
const rolesRoot = roots.find((r) => path16.resolve(args.path).startsWith(r));
|
|
34756
|
+
if (!rolesRoot) {
|
|
34757
|
+
throw new Error(`Cannot determine roles root for "${args.path}"`);
|
|
34758
|
+
}
|
|
34759
|
+
const relFromRoot = path16.relative(rolesRoot, path16.dirname(args.path));
|
|
34760
|
+
const currentCategory = relFromRoot && relFromRoot !== "." ? relFromRoot : "";
|
|
34761
|
+
const newName = args.newName ?? oldFilename;
|
|
34762
|
+
const newCategory = args.newCategory !== void 0 ? args.newCategory : currentCategory;
|
|
34763
|
+
if (!NAME_REGEX2.test(newName)) {
|
|
34764
|
+
throw new Error(`Invalid role name: "${newName}"`);
|
|
34765
|
+
}
|
|
34766
|
+
if (newCategory && !NAME_REGEX2.test(newCategory)) {
|
|
34767
|
+
throw new Error(`Invalid category name: "${newCategory}"`);
|
|
34768
|
+
}
|
|
34769
|
+
const newDir = newCategory ? path16.join(rolesRoot, newCategory) : rolesRoot;
|
|
34770
|
+
const newPath = path16.join(newDir, `${newName}.md`);
|
|
34771
|
+
if (path16.resolve(newPath) === path16.resolve(args.path)) {
|
|
34772
|
+
return { path: args.path };
|
|
34773
|
+
}
|
|
34774
|
+
await fs9.mkdir(newDir, { recursive: true });
|
|
34775
|
+
try {
|
|
34776
|
+
await fs9.access(newPath);
|
|
34777
|
+
throw new Error(`A role already exists at "${newPath}"`);
|
|
34778
|
+
} catch (err) {
|
|
34779
|
+
if (err.code !== "ENOENT") throw err;
|
|
34780
|
+
}
|
|
34781
|
+
await fs9.rename(args.path, newPath);
|
|
34782
|
+
const frontmatterPatch = {};
|
|
34783
|
+
if (newName !== oldFilename) {
|
|
34784
|
+
frontmatterPatch.description = void 0;
|
|
34785
|
+
}
|
|
34786
|
+
await writeRoleFrontmatter(
|
|
34787
|
+
{
|
|
34788
|
+
path: newPath,
|
|
34789
|
+
frontmatter: {
|
|
34790
|
+
...newName !== oldFilename ? { name: newName } : {}
|
|
34791
|
+
}
|
|
34792
|
+
},
|
|
34793
|
+
workspaceRoot
|
|
34794
|
+
);
|
|
34795
|
+
if (oldDir !== rolesRoot) {
|
|
34796
|
+
try {
|
|
34797
|
+
const remaining = await fs9.readdir(oldDir);
|
|
34798
|
+
if (remaining.length === 0) {
|
|
34799
|
+
await fs9.rmdir(oldDir);
|
|
34800
|
+
}
|
|
34801
|
+
} catch {
|
|
34802
|
+
}
|
|
34803
|
+
}
|
|
34804
|
+
return { path: newPath };
|
|
34805
|
+
}
|
|
34806
|
+
|
|
34807
|
+
// ../server/src/server/brands/scanner.ts
|
|
34808
|
+
import fs10 from "node:fs/promises";
|
|
34809
|
+
import path17 from "node:path";
|
|
34810
|
+
var NAME_REGEX3 = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
34811
|
+
function resolveScopeDir3(scope, workspaceRoot) {
|
|
34812
|
+
if (scope === "project") {
|
|
34813
|
+
if (!workspaceRoot) {
|
|
34814
|
+
throw new Error('workspaceRoot is required for scope "project"');
|
|
34815
|
+
}
|
|
34816
|
+
return path17.join(workspaceRoot, ".brand");
|
|
34817
|
+
}
|
|
34818
|
+
throw new Error(`Unknown scope: ${scope}`);
|
|
34819
|
+
}
|
|
34820
|
+
function allowedRoots3(workspaceRoot) {
|
|
34821
|
+
const roots = [];
|
|
34822
|
+
if (workspaceRoot) {
|
|
34823
|
+
roots.push(path17.join(workspaceRoot, ".brand"));
|
|
34824
|
+
}
|
|
34825
|
+
return roots.map((r) => path17.resolve(r));
|
|
34826
|
+
}
|
|
34827
|
+
function isInsideAllowedRoot3(absPath, workspaceRoot) {
|
|
34828
|
+
const resolved = path17.resolve(absPath);
|
|
34829
|
+
for (const root of allowedRoots3(workspaceRoot)) {
|
|
34830
|
+
const rel = path17.relative(root, resolved);
|
|
34831
|
+
if (rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel)) {
|
|
34832
|
+
return true;
|
|
34833
|
+
}
|
|
34834
|
+
}
|
|
34835
|
+
return false;
|
|
34836
|
+
}
|
|
34837
|
+
var FRONTMATTER_RE3 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
34838
|
+
function parseBrandFile(text) {
|
|
34839
|
+
const match = FRONTMATTER_RE3.exec(text);
|
|
34840
|
+
if (!match) {
|
|
34841
|
+
return { rawFrontmatterLines: [], body: text, hadFrontmatter: false };
|
|
34842
|
+
}
|
|
34843
|
+
return {
|
|
34844
|
+
rawFrontmatterLines: (match[1] ?? "").split(/\r?\n/),
|
|
34845
|
+
body: match[2] ?? "",
|
|
34846
|
+
hadFrontmatter: true
|
|
34847
|
+
};
|
|
34848
|
+
}
|
|
34849
|
+
function unquote2(value) {
|
|
34850
|
+
const trimmed = value.trim();
|
|
34851
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
34852
|
+
return trimmed.slice(1, -1);
|
|
34853
|
+
}
|
|
34854
|
+
return trimmed;
|
|
34855
|
+
}
|
|
34856
|
+
function quoteIfNeeded(value) {
|
|
34857
|
+
if (/[:#\n\[\]]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
|
|
34858
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
34859
|
+
}
|
|
34860
|
+
return value;
|
|
34861
|
+
}
|
|
34862
|
+
function parseFrontmatter2(lines) {
|
|
34863
|
+
const result = {
|
|
34864
|
+
description: "",
|
|
34865
|
+
tags: [],
|
|
34866
|
+
variables: []
|
|
34867
|
+
};
|
|
34868
|
+
let i = 0;
|
|
34869
|
+
while (i < lines.length) {
|
|
34870
|
+
const line = lines[i] ?? "";
|
|
34871
|
+
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
34872
|
+
if (!keyMatch) {
|
|
34873
|
+
i++;
|
|
34874
|
+
continue;
|
|
34875
|
+
}
|
|
34876
|
+
const key = keyMatch[1];
|
|
34877
|
+
const rawValue = (keyMatch[2] ?? "").trim();
|
|
34878
|
+
if (key === "description") {
|
|
34879
|
+
result.description = unquote2(rawValue);
|
|
34880
|
+
i++;
|
|
34881
|
+
} else if (key === "tags") {
|
|
34882
|
+
if (rawValue) {
|
|
34883
|
+
result.tags = rawValue.replace(/^\[|\]$/g, "").split(",").map((s) => unquote2(s)).filter((s) => s.length > 0);
|
|
34884
|
+
} else {
|
|
34885
|
+
const items = [];
|
|
34886
|
+
let j = i + 1;
|
|
34887
|
+
while (j < lines.length) {
|
|
34888
|
+
const next = lines[j] ?? "";
|
|
34889
|
+
const m = /^\s+-\s+(.+)$/.exec(next);
|
|
34890
|
+
if (!m) break;
|
|
34891
|
+
items.push(unquote2(m[1]));
|
|
34892
|
+
j++;
|
|
34893
|
+
}
|
|
34894
|
+
result.tags = items;
|
|
34895
|
+
i = j;
|
|
34896
|
+
continue;
|
|
34897
|
+
}
|
|
34898
|
+
i++;
|
|
34899
|
+
} else if (key === "variables") {
|
|
34900
|
+
const vars = [];
|
|
34901
|
+
let j = i + 1;
|
|
34902
|
+
while (j < lines.length) {
|
|
34903
|
+
const next = lines[j] ?? "";
|
|
34904
|
+
if (/^\s+-\s+key\s*:\s*/.test(next)) {
|
|
34905
|
+
const keyVal = unquote2((/^\s+-\s+key\s*:\s*(.*)$/.exec(next) ?? [])[1] ?? "");
|
|
34906
|
+
const varObj = { key: keyVal, type: "text", label: "", value: "" };
|
|
34907
|
+
j++;
|
|
34908
|
+
while (j < lines.length) {
|
|
34909
|
+
const sub = lines[j] ?? "";
|
|
34910
|
+
if (/^\s+-\s+/.test(sub) && !/^\s{4,}-\s+/.test(sub)) break;
|
|
34911
|
+
const subMatch = /^\s+(\w+)\s*:\s*(.*)$/.exec(sub);
|
|
34912
|
+
if (!subMatch) {
|
|
34913
|
+
j++;
|
|
34914
|
+
break;
|
|
34915
|
+
}
|
|
34916
|
+
const subKey = subMatch[1];
|
|
34917
|
+
const subVal = unquote2(subMatch[2] ?? "");
|
|
34918
|
+
if (subKey === "type") {
|
|
34919
|
+
varObj.type = subVal;
|
|
34920
|
+
} else if (subKey === "label") {
|
|
34921
|
+
varObj.label = subVal;
|
|
34922
|
+
} else if (subKey === "value") {
|
|
34923
|
+
varObj.value = subVal;
|
|
34924
|
+
} else if (subKey === "section") {
|
|
34925
|
+
varObj.section = subVal;
|
|
34926
|
+
} else if (subKey === "options") {
|
|
34927
|
+
varObj.options = subVal.replace(/^\[|\]$/g, "").split(",").map((s) => unquote2(s)).filter((s) => s.length > 0);
|
|
34928
|
+
} else if (subKey === "fileRef") {
|
|
34929
|
+
varObj.fileRef = subVal;
|
|
34930
|
+
}
|
|
34931
|
+
j++;
|
|
34932
|
+
}
|
|
34933
|
+
vars.push(varObj);
|
|
34934
|
+
} else if (/^[A-Za-z]/.test(next)) {
|
|
34935
|
+
break;
|
|
34936
|
+
} else {
|
|
34937
|
+
j++;
|
|
34938
|
+
}
|
|
34939
|
+
}
|
|
34940
|
+
result.variables = vars;
|
|
34941
|
+
i = j;
|
|
34942
|
+
continue;
|
|
34943
|
+
} else {
|
|
34944
|
+
i++;
|
|
34945
|
+
}
|
|
34946
|
+
}
|
|
34947
|
+
return result;
|
|
34948
|
+
}
|
|
34949
|
+
function serializeVariables(variables) {
|
|
34950
|
+
if (variables.length === 0) return ["variables: []"];
|
|
34951
|
+
const lines = ["variables:"];
|
|
34952
|
+
for (const v of variables) {
|
|
34953
|
+
lines.push(` - key: ${quoteIfNeeded(v.key)}`);
|
|
34954
|
+
lines.push(` type: ${v.type}`);
|
|
34955
|
+
lines.push(` label: ${quoteIfNeeded(v.label)}`);
|
|
34956
|
+
lines.push(` value: ${quoteIfNeeded(v.value)}`);
|
|
34957
|
+
if (v.section) {
|
|
34958
|
+
lines.push(` section: ${v.section}`);
|
|
34959
|
+
}
|
|
34960
|
+
if (v.options && v.options.length > 0) {
|
|
34961
|
+
lines.push(` options: [${v.options.map(quoteIfNeeded).join(", ")}]`);
|
|
34962
|
+
}
|
|
34963
|
+
if (v.fileRef) {
|
|
34964
|
+
lines.push(` fileRef: ${quoteIfNeeded(v.fileRef)}`);
|
|
34965
|
+
}
|
|
34966
|
+
}
|
|
34967
|
+
return lines;
|
|
34968
|
+
}
|
|
34969
|
+
var OWNED_KEYS2 = ["description", "tags", "variables"];
|
|
34970
|
+
function findOwnedSpans3(lines) {
|
|
34971
|
+
const spans = [];
|
|
34972
|
+
let i = 0;
|
|
34973
|
+
while (i < lines.length) {
|
|
34974
|
+
const line = lines[i] ?? "";
|
|
34975
|
+
const keyMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
34976
|
+
if (!keyMatch) {
|
|
34977
|
+
i++;
|
|
34978
|
+
continue;
|
|
34979
|
+
}
|
|
34980
|
+
const key = keyMatch[1];
|
|
34981
|
+
const rawValue = keyMatch[2] ?? "";
|
|
34982
|
+
if (!OWNED_KEYS2.includes(key)) {
|
|
34983
|
+
i++;
|
|
34984
|
+
continue;
|
|
34985
|
+
}
|
|
34986
|
+
let end = i + 1;
|
|
34987
|
+
if (rawValue.trim() === "" || rawValue.trim() === "[]") {
|
|
34988
|
+
while (end < lines.length) {
|
|
34989
|
+
const next = lines[end] ?? "";
|
|
34990
|
+
if (/^\s+/.test(next)) {
|
|
34991
|
+
end++;
|
|
34992
|
+
} else {
|
|
34993
|
+
break;
|
|
34994
|
+
}
|
|
34995
|
+
}
|
|
34996
|
+
}
|
|
34997
|
+
spans.push({ key, startLine: i, endLine: end });
|
|
34998
|
+
i = end;
|
|
34999
|
+
}
|
|
35000
|
+
return spans;
|
|
35001
|
+
}
|
|
35002
|
+
function rewriteFrontmatter3(originalLines, next) {
|
|
35003
|
+
const spans = findOwnedSpans3(originalLines);
|
|
35004
|
+
const ownedKeys = new Set(spans.map((s) => s.key));
|
|
35005
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
35006
|
+
if (next.description !== void 0) {
|
|
35007
|
+
const v = next.description;
|
|
35008
|
+
replacements.set("description", v.length > 0 ? [`description: ${quoteIfNeeded(v)}`] : []);
|
|
35009
|
+
}
|
|
35010
|
+
if (next.tags !== void 0) {
|
|
35011
|
+
if (next.tags.length === 0) {
|
|
35012
|
+
replacements.set("tags", []);
|
|
35013
|
+
} else {
|
|
35014
|
+
replacements.set("tags", [`tags: [${next.tags.map(quoteIfNeeded).join(", ")}]`]);
|
|
35015
|
+
}
|
|
35016
|
+
}
|
|
35017
|
+
if (next.variables !== void 0) {
|
|
35018
|
+
replacements.set("variables", serializeVariables(next.variables));
|
|
35019
|
+
}
|
|
35020
|
+
const out = [];
|
|
35021
|
+
let i = 0;
|
|
35022
|
+
while (i < originalLines.length) {
|
|
35023
|
+
const span = spans.find((s) => s.startLine === i);
|
|
35024
|
+
if (span) {
|
|
35025
|
+
if (replacements.has(span.key)) {
|
|
35026
|
+
out.push(...replacements.get(span.key) ?? []);
|
|
35027
|
+
} else {
|
|
35028
|
+
for (let j = span.startLine; j < span.endLine; j++) {
|
|
35029
|
+
out.push(originalLines[j] ?? "");
|
|
35030
|
+
}
|
|
35031
|
+
}
|
|
35032
|
+
i = span.endLine;
|
|
35033
|
+
continue;
|
|
35034
|
+
}
|
|
35035
|
+
out.push(originalLines[i] ?? "");
|
|
35036
|
+
i++;
|
|
35037
|
+
}
|
|
35038
|
+
for (const key of OWNED_KEYS2) {
|
|
35039
|
+
if (replacements.has(key) && !ownedKeys.has(key)) {
|
|
35040
|
+
out.push(...replacements.get(key) ?? []);
|
|
35041
|
+
}
|
|
35042
|
+
}
|
|
35043
|
+
return out;
|
|
35044
|
+
}
|
|
35045
|
+
async function readBrandsFromDir(scope, dir) {
|
|
35046
|
+
let entries;
|
|
35047
|
+
try {
|
|
35048
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
35049
|
+
} catch {
|
|
35050
|
+
return [];
|
|
35051
|
+
}
|
|
35052
|
+
const results = [];
|
|
35053
|
+
for (const entry of entries) {
|
|
35054
|
+
if (!entry.isFile()) continue;
|
|
35055
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
35056
|
+
const name = entry.name.slice(0, -".md".length);
|
|
35057
|
+
if (!name || !NAME_REGEX3.test(name)) continue;
|
|
35058
|
+
const fullPath = path17.join(dir, entry.name);
|
|
35059
|
+
let stat10;
|
|
35060
|
+
try {
|
|
35061
|
+
const s = await fs10.stat(fullPath);
|
|
35062
|
+
stat10 = { mtime: s.mtime, size: s.size };
|
|
35063
|
+
} catch {
|
|
35064
|
+
continue;
|
|
35065
|
+
}
|
|
35066
|
+
let parsed;
|
|
35067
|
+
try {
|
|
35068
|
+
const text = await fs10.readFile(fullPath, "utf8");
|
|
35069
|
+
const { rawFrontmatterLines, hadFrontmatter } = parseBrandFile(text);
|
|
35070
|
+
parsed = hadFrontmatter ? parseFrontmatter2(rawFrontmatterLines) : { description: "", tags: [], variables: [] };
|
|
35071
|
+
} catch {
|
|
35072
|
+
parsed = { description: "", tags: [], variables: [] };
|
|
35073
|
+
}
|
|
35074
|
+
results.push({
|
|
35075
|
+
id: `${scope}:${name}`,
|
|
35076
|
+
scope,
|
|
35077
|
+
name,
|
|
35078
|
+
description: parsed.description,
|
|
35079
|
+
tags: parsed.tags,
|
|
35080
|
+
variables: parsed.variables,
|
|
35081
|
+
path: fullPath,
|
|
35082
|
+
modifiedAt: stat10.mtime.toISOString(),
|
|
35083
|
+
size: stat10.size
|
|
35084
|
+
});
|
|
35085
|
+
}
|
|
35086
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
35087
|
+
return results;
|
|
35088
|
+
}
|
|
35089
|
+
async function listBrands(args) {
|
|
35090
|
+
return readBrandsFromDir("project", resolveScopeDir3("project", args.workspaceRoot));
|
|
35091
|
+
}
|
|
35092
|
+
async function createBrand(args) {
|
|
35093
|
+
if (!NAME_REGEX3.test(args.name)) {
|
|
35094
|
+
throw new Error(
|
|
35095
|
+
`Invalid brand name "${args.name}". Use letters, digits, dot, underscore, dash.`
|
|
35096
|
+
);
|
|
35097
|
+
}
|
|
35098
|
+
if (args.name.includes("/") || args.name.includes("\\") || args.name.includes("..")) {
|
|
35099
|
+
throw new Error(`Brand name must not contain path separators or "..".`);
|
|
35100
|
+
}
|
|
35101
|
+
const dir = resolveScopeDir3("project", args.workspaceRoot);
|
|
35102
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
35103
|
+
const filePath = path17.join(dir, `${args.name}.md`);
|
|
35104
|
+
try {
|
|
35105
|
+
await fs10.access(filePath);
|
|
35106
|
+
throw new Error(`A brand named "${args.name}" already exists at ${filePath}`);
|
|
35107
|
+
} catch (err) {
|
|
35108
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
35109
|
+
throw err;
|
|
35110
|
+
}
|
|
35111
|
+
if (err instanceof Error && err.message.includes("already exists")) {
|
|
35112
|
+
throw err;
|
|
35113
|
+
}
|
|
35114
|
+
}
|
|
35115
|
+
await fs10.writeFile(filePath, buildStarterBrand(args.name), "utf8");
|
|
35116
|
+
return { path: filePath };
|
|
35117
|
+
}
|
|
35118
|
+
function buildStarterBrand(name) {
|
|
35119
|
+
return `---
|
|
35120
|
+
name: ${name}
|
|
35121
|
+
description: ""
|
|
35122
|
+
tags: []
|
|
35123
|
+
variables:
|
|
35124
|
+
- key: color.primary
|
|
35125
|
+
type: color
|
|
35126
|
+
label: Primary Color
|
|
35127
|
+
value: "#000000"
|
|
35128
|
+
- key: color.accent
|
|
35129
|
+
type: color
|
|
35130
|
+
label: Accent Color
|
|
35131
|
+
value: "#ffffff"
|
|
35132
|
+
- key: font.heading
|
|
35133
|
+
type: font
|
|
35134
|
+
label: Heading Font
|
|
35135
|
+
value: ""
|
|
35136
|
+
- key: font.body
|
|
35137
|
+
type: font
|
|
35138
|
+
label: Body Font
|
|
35139
|
+
value: ""
|
|
35140
|
+
- key: logo
|
|
35141
|
+
type: asset
|
|
35142
|
+
label: Logo
|
|
35143
|
+
value: ""
|
|
35144
|
+
- key: tone
|
|
35145
|
+
type: text
|
|
35146
|
+
label: Tone of Voice
|
|
35147
|
+
value: ""
|
|
35148
|
+
---
|
|
35149
|
+
|
|
35150
|
+
# ${name}
|
|
35151
|
+
|
|
35152
|
+
Describe this brand's identity, design philosophy, and usage guidelines here.
|
|
35153
|
+
`;
|
|
35154
|
+
}
|
|
35155
|
+
var TARGET_NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
35156
|
+
async function copyBrandAsset(args) {
|
|
35157
|
+
if (!args.workspaceRoot) {
|
|
35158
|
+
throw new Error("workspaceRoot is required to copy a brand asset");
|
|
35159
|
+
}
|
|
35160
|
+
if (!args.sourcePath || !path17.isAbsolute(args.sourcePath)) {
|
|
35161
|
+
throw new Error(`copyBrandAsset expects an absolute sourcePath; got "${args.sourcePath}"`);
|
|
35162
|
+
}
|
|
35163
|
+
if (!TARGET_NAME_REGEX.test(args.targetName) || args.targetName.includes("..")) {
|
|
35164
|
+
throw new Error(
|
|
35165
|
+
`Invalid targetName "${args.targetName}". Use letters, digits, dot, underscore, dash.`
|
|
35166
|
+
);
|
|
35167
|
+
}
|
|
35168
|
+
const stats = await fs10.stat(args.sourcePath);
|
|
35169
|
+
if (!stats.isFile()) {
|
|
35170
|
+
throw new Error(`Source path is not a regular file: ${args.sourcePath}`);
|
|
35171
|
+
}
|
|
35172
|
+
const ext = path17.extname(args.sourcePath).toLowerCase();
|
|
35173
|
+
const fileName = `${args.targetName}${ext}`;
|
|
35174
|
+
const assetsDir = path17.join(args.workspaceRoot, ".brand", "assets");
|
|
35175
|
+
const destAbs = path17.resolve(assetsDir, fileName);
|
|
35176
|
+
const rel = path17.relative(path17.resolve(assetsDir), destAbs);
|
|
35177
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel)) {
|
|
35178
|
+
throw new Error(`Refusing to write outside of .brand/assets: ${destAbs}`);
|
|
35179
|
+
}
|
|
35180
|
+
await fs10.mkdir(assetsDir, { recursive: true });
|
|
35181
|
+
await fs10.copyFile(args.sourcePath, destAbs);
|
|
35182
|
+
return {
|
|
35183
|
+
relativePath: `assets/${fileName}`,
|
|
35184
|
+
absolutePath: destAbs
|
|
35185
|
+
};
|
|
35186
|
+
}
|
|
35187
|
+
async function uploadBrandAsset(args) {
|
|
35188
|
+
if (!args.workspaceRoot) {
|
|
35189
|
+
throw new Error("workspaceRoot is required to upload a brand asset");
|
|
35190
|
+
}
|
|
35191
|
+
if (!TARGET_NAME_REGEX.test(args.targetName) || args.targetName.includes("..")) {
|
|
35192
|
+
throw new Error(
|
|
35193
|
+
`Invalid targetName "${args.targetName}". Use letters, digits, dot, underscore, dash.`
|
|
35194
|
+
);
|
|
35195
|
+
}
|
|
35196
|
+
if (!args.dataBase64 || args.dataBase64.trim().length === 0) {
|
|
35197
|
+
throw new Error("No file data provided for brand asset upload");
|
|
35198
|
+
}
|
|
35199
|
+
const extFromSource = args.sourceName ? path17.extname(args.sourceName).toLowerCase() : "";
|
|
35200
|
+
const ext = extFromSource || ".png";
|
|
35201
|
+
const fileName = `${args.targetName}${ext}`;
|
|
35202
|
+
const assetsDir = path17.join(args.workspaceRoot, ".brand", "assets");
|
|
35203
|
+
const destAbs = path17.resolve(assetsDir, fileName);
|
|
35204
|
+
const rel = path17.relative(path17.resolve(assetsDir), destAbs);
|
|
35205
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel)) {
|
|
35206
|
+
throw new Error(`Refusing to write outside of .brand/assets: ${destAbs}`);
|
|
35207
|
+
}
|
|
35208
|
+
let data;
|
|
35209
|
+
try {
|
|
35210
|
+
data = Buffer.from(args.dataBase64, "base64");
|
|
35211
|
+
} catch {
|
|
35212
|
+
throw new Error("Failed to decode uploaded brand asset");
|
|
35213
|
+
}
|
|
35214
|
+
if (data.length === 0) {
|
|
35215
|
+
throw new Error("Uploaded brand asset is empty");
|
|
35216
|
+
}
|
|
35217
|
+
await fs10.mkdir(assetsDir, { recursive: true });
|
|
35218
|
+
await fs10.writeFile(destAbs, data);
|
|
35219
|
+
return {
|
|
35220
|
+
relativePath: `assets/${fileName}`,
|
|
35221
|
+
absolutePath: destAbs
|
|
35222
|
+
};
|
|
35223
|
+
}
|
|
35224
|
+
async function writeBrandFrontmatter(args, workspaceRoot) {
|
|
35225
|
+
if (!path17.isAbsolute(args.path)) {
|
|
35226
|
+
throw new Error(`writeBrandFrontmatter expects an absolute path; got "${args.path}"`);
|
|
35227
|
+
}
|
|
35228
|
+
if (!isInsideAllowedRoot3(args.path, workspaceRoot)) {
|
|
35229
|
+
throw new Error(`Path "${args.path}" is not inside an allowlisted brand root`);
|
|
35230
|
+
}
|
|
35231
|
+
let original;
|
|
35232
|
+
try {
|
|
35233
|
+
original = await fs10.readFile(args.path, "utf8");
|
|
35234
|
+
} catch (err) {
|
|
35235
|
+
throw new Error(
|
|
35236
|
+
`Failed to read brand file: ${err instanceof Error ? err.message : String(err)}`
|
|
35237
|
+
);
|
|
35238
|
+
}
|
|
35239
|
+
const parsed = parseBrandFile(original);
|
|
35240
|
+
const newLines = rewriteFrontmatter3(parsed.rawFrontmatterLines, args.frontmatter);
|
|
35241
|
+
const nextFrontmatter = ["---", ...newLines, "---"].join("\n");
|
|
35242
|
+
const nextContent = parsed.hadFrontmatter ? `${nextFrontmatter}
|
|
35243
|
+
${parsed.body}` : `${nextFrontmatter}
|
|
35244
|
+
|
|
35245
|
+
${original}`;
|
|
35246
|
+
await fs10.writeFile(args.path, nextContent, "utf8");
|
|
35247
|
+
}
|
|
34131
35248
|
|
|
34132
35249
|
// ../server/src/services/oauth-service.ts
|
|
34133
35250
|
import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
|
|
@@ -34374,57 +35491,57 @@ async function fetchGitLabUsername(fetchImpl, accessToken) {
|
|
|
34374
35491
|
return null;
|
|
34375
35492
|
}
|
|
34376
35493
|
}
|
|
34377
|
-
async function readCredential(
|
|
35494
|
+
async function readCredential(path33, log) {
|
|
34378
35495
|
try {
|
|
34379
|
-
const raw = await readFile3(
|
|
35496
|
+
const raw = await readFile3(path33, "utf8");
|
|
34380
35497
|
const parsed = JSON.parse(raw);
|
|
34381
35498
|
return parsed.gitlab ?? null;
|
|
34382
35499
|
} catch (error) {
|
|
34383
35500
|
if (isNotFound(error)) return null;
|
|
34384
|
-
log.warn({ err: error, path:
|
|
35501
|
+
log.warn({ err: error, path: path33 }, "oauth.credentials.read_failed");
|
|
34385
35502
|
return null;
|
|
34386
35503
|
}
|
|
34387
35504
|
}
|
|
34388
|
-
async function persistCredential(
|
|
34389
|
-
await mkdir4(dirname4(
|
|
35505
|
+
async function persistCredential(path33, credential, log) {
|
|
35506
|
+
await mkdir4(dirname4(path33), { recursive: true });
|
|
34390
35507
|
let current = {};
|
|
34391
35508
|
try {
|
|
34392
|
-
const raw = await readFile3(
|
|
35509
|
+
const raw = await readFile3(path33, "utf8");
|
|
34393
35510
|
current = JSON.parse(raw);
|
|
34394
35511
|
} catch (error) {
|
|
34395
35512
|
if (!isNotFound(error)) {
|
|
34396
|
-
log.warn({ err: error, path:
|
|
35513
|
+
log.warn({ err: error, path: path33 }, "oauth.credentials.read_failed_overwriting");
|
|
34397
35514
|
}
|
|
34398
35515
|
}
|
|
34399
35516
|
const next = { ...current, gitlab: credential };
|
|
34400
|
-
const tmpPath = `${
|
|
35517
|
+
const tmpPath = `${path33}.tmp-${process.pid}-${Date.now()}`;
|
|
34401
35518
|
await writeFile4(tmpPath, JSON.stringify(next, null, 2), { mode: 384 });
|
|
34402
|
-
await rename(tmpPath,
|
|
35519
|
+
await rename(tmpPath, path33);
|
|
34403
35520
|
}
|
|
34404
|
-
async function deleteCredential(
|
|
35521
|
+
async function deleteCredential(path33, log) {
|
|
34405
35522
|
let current = {};
|
|
34406
35523
|
try {
|
|
34407
|
-
const raw = await readFile3(
|
|
35524
|
+
const raw = await readFile3(path33, "utf8");
|
|
34408
35525
|
current = JSON.parse(raw);
|
|
34409
35526
|
} catch (error) {
|
|
34410
35527
|
if (isNotFound(error)) return;
|
|
34411
|
-
log.warn({ err: error, path:
|
|
35528
|
+
log.warn({ err: error, path: path33 }, "oauth.credentials.delete_read_failed");
|
|
34412
35529
|
return;
|
|
34413
35530
|
}
|
|
34414
35531
|
delete current.gitlab;
|
|
34415
35532
|
if (Object.keys(current).length === 0) {
|
|
34416
35533
|
try {
|
|
34417
|
-
await unlink(
|
|
35534
|
+
await unlink(path33);
|
|
34418
35535
|
} catch (error) {
|
|
34419
35536
|
if (!isNotFound(error)) {
|
|
34420
|
-
log.warn({ err: error, path:
|
|
35537
|
+
log.warn({ err: error, path: path33 }, "oauth.credentials.unlink_failed");
|
|
34421
35538
|
}
|
|
34422
35539
|
}
|
|
34423
35540
|
return;
|
|
34424
35541
|
}
|
|
34425
|
-
const tmpPath = `${
|
|
35542
|
+
const tmpPath = `${path33}.tmp-${process.pid}-${Date.now()}`;
|
|
34426
35543
|
await writeFile4(tmpPath, JSON.stringify(current, null, 2), { mode: 384 });
|
|
34427
|
-
await rename(tmpPath,
|
|
35544
|
+
await rename(tmpPath, path33);
|
|
34428
35545
|
}
|
|
34429
35546
|
function defaultGlabConfigPath() {
|
|
34430
35547
|
const home = homedir4();
|
|
@@ -34439,15 +35556,15 @@ function defaultGlabConfigPath() {
|
|
|
34439
35556
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
34440
35557
|
return join9(xdg && xdg.length > 0 ? xdg : join9(home, ".config"), "glab-cli", "config.yml");
|
|
34441
35558
|
}
|
|
34442
|
-
async function writeGlabConfig(
|
|
34443
|
-
await mkdir4(dirname4(
|
|
35559
|
+
async function writeGlabConfig(path33, credential, log) {
|
|
35560
|
+
await mkdir4(dirname4(path33), { recursive: true });
|
|
34444
35561
|
let doc;
|
|
34445
35562
|
try {
|
|
34446
|
-
const raw = await readFile3(
|
|
35563
|
+
const raw = await readFile3(path33, "utf8");
|
|
34447
35564
|
doc = YAML.parseDocument(raw);
|
|
34448
35565
|
if (doc.errors.length > 0) {
|
|
34449
35566
|
log.warn(
|
|
34450
|
-
{ errors: doc.errors.map((e) => e.message), path:
|
|
35567
|
+
{ errors: doc.errors.map((e) => e.message), path: path33 },
|
|
34451
35568
|
"oauth.glab.parse_errors_replacing"
|
|
34452
35569
|
);
|
|
34453
35570
|
doc = YAML.parseDocument("{}");
|
|
@@ -34456,7 +35573,7 @@ async function writeGlabConfig(path30, credential, log) {
|
|
|
34456
35573
|
if (isNotFound(error)) {
|
|
34457
35574
|
doc = YAML.parseDocument("{}");
|
|
34458
35575
|
} else {
|
|
34459
|
-
log.warn({ err: error, path:
|
|
35576
|
+
log.warn({ err: error, path: path33 }, "oauth.glab.read_failed_replacing");
|
|
34460
35577
|
doc = YAML.parseDocument("{}");
|
|
34461
35578
|
}
|
|
34462
35579
|
}
|
|
@@ -34469,18 +35586,18 @@ async function writeGlabConfig(path30, credential, log) {
|
|
|
34469
35586
|
if (credential.username) {
|
|
34470
35587
|
hostEntry.set("user", credential.username);
|
|
34471
35588
|
}
|
|
34472
|
-
const tmpPath = `${
|
|
35589
|
+
const tmpPath = `${path33}.tmp-${process.pid}-${Date.now()}`;
|
|
34473
35590
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
34474
|
-
await rename(tmpPath,
|
|
35591
|
+
await rename(tmpPath, path33);
|
|
34475
35592
|
}
|
|
34476
|
-
async function removeGlabHost(
|
|
35593
|
+
async function removeGlabHost(path33, log) {
|
|
34477
35594
|
let doc;
|
|
34478
35595
|
try {
|
|
34479
|
-
const raw = await readFile3(
|
|
35596
|
+
const raw = await readFile3(path33, "utf8");
|
|
34480
35597
|
doc = YAML.parseDocument(raw);
|
|
34481
35598
|
} catch (error) {
|
|
34482
35599
|
if (isNotFound(error)) return;
|
|
34483
|
-
log.warn({ err: error, path:
|
|
35600
|
+
log.warn({ err: error, path: path33 }, "oauth.glab.remove_read_failed");
|
|
34484
35601
|
return;
|
|
34485
35602
|
}
|
|
34486
35603
|
const hosts = doc.get("hosts");
|
|
@@ -34489,9 +35606,9 @@ async function removeGlabHost(path30, log) {
|
|
|
34489
35606
|
if (hosts.items.length === 0) {
|
|
34490
35607
|
doc.delete("hosts");
|
|
34491
35608
|
}
|
|
34492
|
-
const tmpPath = `${
|
|
35609
|
+
const tmpPath = `${path33}.tmp-${process.pid}-${Date.now()}`;
|
|
34493
35610
|
await writeFile4(tmpPath, doc.toString(), { mode: 384 });
|
|
34494
|
-
await rename(tmpPath,
|
|
35611
|
+
await rename(tmpPath, path33);
|
|
34495
35612
|
}
|
|
34496
35613
|
function ensureMap(doc, key) {
|
|
34497
35614
|
const existing = doc.get(key);
|
|
@@ -35602,7 +36719,7 @@ var Session = class _Session {
|
|
|
35602
36719
|
chatService,
|
|
35603
36720
|
scheduleService,
|
|
35604
36721
|
loopService,
|
|
35605
|
-
|
|
36722
|
+
questService,
|
|
35606
36723
|
checkoutDiffManager,
|
|
35607
36724
|
github,
|
|
35608
36725
|
gitlab,
|
|
@@ -35649,7 +36766,7 @@ var Session = class _Session {
|
|
|
35649
36766
|
this.chatService = chatService;
|
|
35650
36767
|
this.scheduleService = scheduleService;
|
|
35651
36768
|
this.loopService = loopService;
|
|
35652
|
-
this.
|
|
36769
|
+
this.questService = questService;
|
|
35653
36770
|
this.checkoutDiffManager = checkoutDiffManager;
|
|
35654
36771
|
this.github = github ?? createGitHubService();
|
|
35655
36772
|
this.gitlab = gitlab ?? createGitLabService();
|
|
@@ -36228,7 +37345,13 @@ var Session = class _Session {
|
|
|
36228
37345
|
await this.handleCloseItemsRequest(msg);
|
|
36229
37346
|
break;
|
|
36230
37347
|
case "update_agent_request":
|
|
36231
|
-
await this.handleUpdateAgentRequest(
|
|
37348
|
+
await this.handleUpdateAgentRequest(
|
|
37349
|
+
msg.agentId,
|
|
37350
|
+
msg.name,
|
|
37351
|
+
msg.labels,
|
|
37352
|
+
msg.requestId,
|
|
37353
|
+
msg.internal
|
|
37354
|
+
);
|
|
36232
37355
|
break;
|
|
36233
37356
|
case "set_voice_mode":
|
|
36234
37357
|
await this.handleSetVoiceMode(msg.enabled, msg.agentId, msg.requestId);
|
|
@@ -36458,6 +37581,15 @@ var Session = class _Session {
|
|
|
36458
37581
|
case "file_delete_request":
|
|
36459
37582
|
await this.handleFileDeleteRequest(msg);
|
|
36460
37583
|
break;
|
|
37584
|
+
case "file_explorer_delete_request":
|
|
37585
|
+
await this.handleFileExplorerDeleteRequest(msg);
|
|
37586
|
+
break;
|
|
37587
|
+
case "file_mkdir_request":
|
|
37588
|
+
await this.handleFileMkdirRequest(msg);
|
|
37589
|
+
break;
|
|
37590
|
+
case "file_move_request":
|
|
37591
|
+
await this.handleFileMoveRequest(msg);
|
|
37592
|
+
break;
|
|
36461
37593
|
case "project_icon_request":
|
|
36462
37594
|
await this.handleProjectIconRequest(msg);
|
|
36463
37595
|
break;
|
|
@@ -36618,20 +37750,47 @@ var Session = class _Session {
|
|
|
36618
37750
|
case "loop/stop":
|
|
36619
37751
|
await this.handleLoopStopRequest(msg);
|
|
36620
37752
|
break;
|
|
36621
|
-
case "
|
|
36622
|
-
await this.
|
|
37753
|
+
case "quest/run":
|
|
37754
|
+
await this.handleQuestRunRequest(msg);
|
|
36623
37755
|
break;
|
|
36624
|
-
case "
|
|
36625
|
-
await this.
|
|
37756
|
+
case "quest/list":
|
|
37757
|
+
await this.handleQuestListRequest(msg);
|
|
36626
37758
|
break;
|
|
36627
|
-
case "
|
|
36628
|
-
await this.
|
|
37759
|
+
case "quest/inspect":
|
|
37760
|
+
await this.handleQuestInspectRequest(msg);
|
|
36629
37761
|
break;
|
|
36630
|
-
case "
|
|
36631
|
-
await this.
|
|
37762
|
+
case "quest/stop":
|
|
37763
|
+
await this.handleQuestStopRequest(msg);
|
|
36632
37764
|
break;
|
|
36633
|
-
case "
|
|
36634
|
-
await this.
|
|
37765
|
+
case "roles/list":
|
|
37766
|
+
await this.handleRolesListRequest(msg);
|
|
37767
|
+
break;
|
|
37768
|
+
case "roles/list-scope":
|
|
37769
|
+
await this.handleRolesListScopeRequest(msg);
|
|
37770
|
+
break;
|
|
37771
|
+
case "roles/create":
|
|
37772
|
+
await this.handleRoleCreateRequest(msg);
|
|
37773
|
+
break;
|
|
37774
|
+
case "roles/write-frontmatter":
|
|
37775
|
+
await this.handleRoleWriteFrontmatterRequest(msg);
|
|
37776
|
+
break;
|
|
37777
|
+
case "roles/move":
|
|
37778
|
+
await this.handleRoleMoveRequest(msg);
|
|
37779
|
+
break;
|
|
37780
|
+
case "brands/list-scope":
|
|
37781
|
+
await this.handleBrandsListScopeRequest(msg);
|
|
37782
|
+
break;
|
|
37783
|
+
case "brands/create":
|
|
37784
|
+
await this.handleBrandCreateRequest(msg);
|
|
37785
|
+
break;
|
|
37786
|
+
case "brands/write-frontmatter":
|
|
37787
|
+
await this.handleBrandWriteFrontmatterRequest(msg);
|
|
37788
|
+
break;
|
|
37789
|
+
case "brands/asset-copy":
|
|
37790
|
+
await this.handleBrandAssetCopyRequest(msg);
|
|
37791
|
+
break;
|
|
37792
|
+
case "brands/asset-upload":
|
|
37793
|
+
await this.handleBrandAssetUploadRequest(msg);
|
|
36635
37794
|
break;
|
|
36636
37795
|
}
|
|
36637
37796
|
} catch (error) {
|
|
@@ -37063,26 +38222,27 @@ var Session = class _Session {
|
|
|
37063
38222
|
}
|
|
37064
38223
|
await unarchiveAgentState(this.agentStorage, this.agentManager, matched.id);
|
|
37065
38224
|
}
|
|
37066
|
-
async handleUpdateAgentRequest(agentId, name, labels, requestId) {
|
|
38225
|
+
async handleUpdateAgentRequest(agentId, name, labels, requestId, internal) {
|
|
37067
38226
|
this.sessionLogger.info(
|
|
37068
38227
|
{
|
|
37069
38228
|
agentId,
|
|
37070
38229
|
requestId,
|
|
37071
38230
|
hasName: typeof name === "string",
|
|
37072
|
-
labelCount: labels ? Object.keys(labels).length : 0
|
|
38231
|
+
labelCount: labels ? Object.keys(labels).length : 0,
|
|
38232
|
+
internal
|
|
37073
38233
|
},
|
|
37074
38234
|
"session: update_agent_request"
|
|
37075
38235
|
);
|
|
37076
38236
|
const normalizedName = name?.trim();
|
|
37077
38237
|
const normalizedLabels = labels && Object.keys(labels).length > 0 ? labels : void 0;
|
|
37078
|
-
if (!normalizedName && !normalizedLabels) {
|
|
38238
|
+
if (!normalizedName && !normalizedLabels && internal === void 0) {
|
|
37079
38239
|
this.emit({
|
|
37080
38240
|
type: "update_agent_response",
|
|
37081
38241
|
payload: {
|
|
37082
38242
|
requestId,
|
|
37083
38243
|
agentId,
|
|
37084
38244
|
accepted: false,
|
|
37085
|
-
error: "Nothing to update (provide name and/or
|
|
38245
|
+
error: "Nothing to update (provide name, labels, and/or internal)"
|
|
37086
38246
|
}
|
|
37087
38247
|
});
|
|
37088
38248
|
return;
|
|
@@ -37090,7 +38250,8 @@ var Session = class _Session {
|
|
|
37090
38250
|
try {
|
|
37091
38251
|
await this.agentManager.updateAgentMetadata(agentId, {
|
|
37092
38252
|
...normalizedName ? { title: normalizedName } : {},
|
|
37093
|
-
...normalizedLabels ? { labels: normalizedLabels } : {}
|
|
38253
|
+
...normalizedLabels ? { labels: normalizedLabels } : {},
|
|
38254
|
+
...internal !== void 0 ? { internal } : {}
|
|
37094
38255
|
});
|
|
37095
38256
|
this.emit({
|
|
37096
38257
|
type: "update_agent_response",
|
|
@@ -38985,7 +40146,7 @@ var Session = class _Session {
|
|
|
38985
40146
|
homeDir: process.env.HOME ?? homedir5(),
|
|
38986
40147
|
query: query2,
|
|
38987
40148
|
limit
|
|
38988
|
-
})).map((
|
|
40149
|
+
})).map((path33) => ({ path: path33, kind: "directory" }));
|
|
38989
40150
|
const directories = entries.filter((entry) => entry.kind === "directory").map((entry) => entry.path);
|
|
38990
40151
|
this.emit({
|
|
38991
40152
|
type: "directory_suggestions_response",
|
|
@@ -39882,6 +41043,104 @@ ${details}`.trim());
|
|
|
39882
41043
|
});
|
|
39883
41044
|
}
|
|
39884
41045
|
}
|
|
41046
|
+
async handleFileExplorerDeleteRequest(request) {
|
|
41047
|
+
const { cwd: workspaceCwd, path: requestedPath, requestId } = request;
|
|
41048
|
+
const cwd = workspaceCwd.trim();
|
|
41049
|
+
if (!cwd) {
|
|
41050
|
+
this.emit({
|
|
41051
|
+
type: "file_explorer_delete_response",
|
|
41052
|
+
payload: { cwd: workspaceCwd, path: requestedPath, error: "cwd is required", requestId }
|
|
41053
|
+
});
|
|
41054
|
+
return;
|
|
41055
|
+
}
|
|
41056
|
+
try {
|
|
41057
|
+
await deleteEntry({ root: cwd, relativePath: requestedPath });
|
|
41058
|
+
this.emit({
|
|
41059
|
+
type: "file_explorer_delete_response",
|
|
41060
|
+
payload: { cwd, path: requestedPath, error: null, requestId }
|
|
41061
|
+
});
|
|
41062
|
+
} catch (error) {
|
|
41063
|
+
this.sessionLogger.error(
|
|
41064
|
+
{ err: error, cwd, path: requestedPath },
|
|
41065
|
+
`Failed to delete entry ${requestedPath} in workspace ${cwd}`
|
|
41066
|
+
);
|
|
41067
|
+
this.emit({
|
|
41068
|
+
type: "file_explorer_delete_response",
|
|
41069
|
+
payload: { cwd, path: requestedPath, error: error?.message ?? "delete failed", requestId }
|
|
41070
|
+
});
|
|
41071
|
+
}
|
|
41072
|
+
}
|
|
41073
|
+
async handleFileMkdirRequest(request) {
|
|
41074
|
+
const { cwd: workspaceCwd, path: requestedPath, requestId } = request;
|
|
41075
|
+
const cwd = workspaceCwd.trim();
|
|
41076
|
+
if (!cwd) {
|
|
41077
|
+
this.emit({
|
|
41078
|
+
type: "file_mkdir_response",
|
|
41079
|
+
payload: { cwd: workspaceCwd, path: requestedPath, error: "cwd is required", requestId }
|
|
41080
|
+
});
|
|
41081
|
+
return;
|
|
41082
|
+
}
|
|
41083
|
+
try {
|
|
41084
|
+
await createDirectory({ root: cwd, relativePath: requestedPath });
|
|
41085
|
+
this.emit({
|
|
41086
|
+
type: "file_mkdir_response",
|
|
41087
|
+
payload: { cwd, path: requestedPath, error: null, requestId }
|
|
41088
|
+
});
|
|
41089
|
+
} catch (error) {
|
|
41090
|
+
this.sessionLogger.error(
|
|
41091
|
+
{ err: error, cwd, path: requestedPath },
|
|
41092
|
+
`Failed to create directory ${requestedPath} in workspace ${cwd}`
|
|
41093
|
+
);
|
|
41094
|
+
this.emit({
|
|
41095
|
+
type: "file_mkdir_response",
|
|
41096
|
+
payload: {
|
|
41097
|
+
cwd,
|
|
41098
|
+
path: requestedPath,
|
|
41099
|
+
error: error?.message ?? "mkdir failed",
|
|
41100
|
+
requestId
|
|
41101
|
+
}
|
|
41102
|
+
});
|
|
41103
|
+
}
|
|
41104
|
+
}
|
|
41105
|
+
async handleFileMoveRequest(request) {
|
|
41106
|
+
const { cwd: workspaceCwd, sourcePath, destinationPath, requestId } = request;
|
|
41107
|
+
const cwd = workspaceCwd.trim();
|
|
41108
|
+
if (!cwd) {
|
|
41109
|
+
this.emit({
|
|
41110
|
+
type: "file_move_response",
|
|
41111
|
+
payload: {
|
|
41112
|
+
cwd: workspaceCwd,
|
|
41113
|
+
sourcePath,
|
|
41114
|
+
destinationPath,
|
|
41115
|
+
error: "cwd is required",
|
|
41116
|
+
requestId
|
|
41117
|
+
}
|
|
41118
|
+
});
|
|
41119
|
+
return;
|
|
41120
|
+
}
|
|
41121
|
+
try {
|
|
41122
|
+
await moveEntry({ root: cwd, sourcePath, destinationPath });
|
|
41123
|
+
this.emit({
|
|
41124
|
+
type: "file_move_response",
|
|
41125
|
+
payload: { cwd, sourcePath, destinationPath, error: null, requestId }
|
|
41126
|
+
});
|
|
41127
|
+
} catch (error) {
|
|
41128
|
+
this.sessionLogger.error(
|
|
41129
|
+
{ err: error, cwd, sourcePath, destinationPath },
|
|
41130
|
+
`Failed to move ${sourcePath} to ${destinationPath} in workspace ${cwd}`
|
|
41131
|
+
);
|
|
41132
|
+
this.emit({
|
|
41133
|
+
type: "file_move_response",
|
|
41134
|
+
payload: {
|
|
41135
|
+
cwd,
|
|
41136
|
+
sourcePath,
|
|
41137
|
+
destinationPath,
|
|
41138
|
+
error: error?.message ?? "move failed",
|
|
41139
|
+
requestId
|
|
41140
|
+
}
|
|
41141
|
+
});
|
|
41142
|
+
}
|
|
41143
|
+
}
|
|
39885
41144
|
/**
|
|
39886
41145
|
* Handle project icon request for a given cwd
|
|
39887
41146
|
*/
|
|
@@ -42543,17 +43802,17 @@ ${details}`.trim());
|
|
|
42543
43802
|
this.emitLoopRpcError(request, error);
|
|
42544
43803
|
}
|
|
42545
43804
|
}
|
|
42546
|
-
|
|
43805
|
+
emitQuestRpcError(request, error) {
|
|
42547
43806
|
const message = error instanceof Error ? error.message : String(error);
|
|
42548
43807
|
const responseType = `${request.type}/response`;
|
|
42549
|
-
if (responseType === "
|
|
43808
|
+
if (responseType === "quest/list/response") {
|
|
42550
43809
|
this.emit({
|
|
42551
43810
|
type: responseType,
|
|
42552
|
-
payload: { requestId: request.requestId,
|
|
43811
|
+
payload: { requestId: request.requestId, quests: [], error: message }
|
|
42553
43812
|
});
|
|
42554
43813
|
return;
|
|
42555
43814
|
}
|
|
42556
|
-
if (responseType === "
|
|
43815
|
+
if (responseType === "roles/list/response") {
|
|
42557
43816
|
this.emit({
|
|
42558
43817
|
type: responseType,
|
|
42559
43818
|
payload: { requestId: request.requestId, roles: [], error: message }
|
|
@@ -42562,12 +43821,12 @@ ${details}`.trim());
|
|
|
42562
43821
|
}
|
|
42563
43822
|
this.emit({
|
|
42564
43823
|
type: responseType,
|
|
42565
|
-
payload: { requestId: request.requestId,
|
|
43824
|
+
payload: { requestId: request.requestId, quest: null, error: message }
|
|
42566
43825
|
});
|
|
42567
43826
|
}
|
|
42568
|
-
async
|
|
43827
|
+
async handleQuestRunRequest(request) {
|
|
42569
43828
|
try {
|
|
42570
|
-
const
|
|
43829
|
+
const quest = await this.questService.run({
|
|
42571
43830
|
kind: request.kind,
|
|
42572
43831
|
cwd: request.cwd,
|
|
42573
43832
|
prompt: request.prompt,
|
|
@@ -42578,16 +43837,16 @@ ${details}`.trim());
|
|
|
42578
43837
|
summarizerEnabled: request.summarizerEnabled
|
|
42579
43838
|
});
|
|
42580
43839
|
this.emit({
|
|
42581
|
-
type: "
|
|
42582
|
-
payload: { requestId: request.requestId,
|
|
43840
|
+
type: "quest/run/response",
|
|
43841
|
+
payload: { requestId: request.requestId, quest, error: null }
|
|
42583
43842
|
});
|
|
42584
43843
|
} catch (error) {
|
|
42585
|
-
this.
|
|
43844
|
+
this.emitQuestRpcError(request, error);
|
|
42586
43845
|
}
|
|
42587
43846
|
}
|
|
42588
|
-
async
|
|
43847
|
+
async handleQuestListRequest(request) {
|
|
42589
43848
|
try {
|
|
42590
|
-
const
|
|
43849
|
+
const quests = this.questService.list().map((w) => ({
|
|
42591
43850
|
id: w.id,
|
|
42592
43851
|
kind: w.kind,
|
|
42593
43852
|
status: w.status,
|
|
@@ -42597,44 +43856,247 @@ ${details}`.trim());
|
|
|
42597
43856
|
updatedAt: w.updatedAt
|
|
42598
43857
|
}));
|
|
42599
43858
|
this.emit({
|
|
42600
|
-
type: "
|
|
42601
|
-
payload: { requestId: request.requestId,
|
|
43859
|
+
type: "quest/list/response",
|
|
43860
|
+
payload: { requestId: request.requestId, quests, error: null }
|
|
42602
43861
|
});
|
|
42603
43862
|
} catch (error) {
|
|
42604
|
-
this.
|
|
43863
|
+
this.emitQuestRpcError(request, error);
|
|
42605
43864
|
}
|
|
42606
43865
|
}
|
|
42607
|
-
async
|
|
43866
|
+
async handleQuestInspectRequest(request) {
|
|
42608
43867
|
try {
|
|
42609
|
-
const
|
|
43868
|
+
const quest = this.questService.inspect(request.id);
|
|
42610
43869
|
this.emit({
|
|
42611
|
-
type: "
|
|
42612
|
-
payload: { requestId: request.requestId,
|
|
43870
|
+
type: "quest/inspect/response",
|
|
43871
|
+
payload: { requestId: request.requestId, quest, error: null }
|
|
42613
43872
|
});
|
|
42614
43873
|
} catch (error) {
|
|
42615
|
-
this.
|
|
43874
|
+
this.emitQuestRpcError(request, error);
|
|
42616
43875
|
}
|
|
42617
43876
|
}
|
|
42618
|
-
async
|
|
43877
|
+
async handleQuestStopRequest(request) {
|
|
42619
43878
|
try {
|
|
42620
|
-
const
|
|
43879
|
+
const quest = await this.questService.stop(request.id);
|
|
42621
43880
|
this.emit({
|
|
42622
|
-
type: "
|
|
42623
|
-
payload: { requestId: request.requestId,
|
|
43881
|
+
type: "quest/stop/response",
|
|
43882
|
+
payload: { requestId: request.requestId, quest, error: null }
|
|
42624
43883
|
});
|
|
42625
43884
|
} catch (error) {
|
|
42626
|
-
this.
|
|
43885
|
+
this.emitQuestRpcError(request, error);
|
|
42627
43886
|
}
|
|
42628
43887
|
}
|
|
42629
|
-
async
|
|
43888
|
+
async handleRolesListRequest(request) {
|
|
42630
43889
|
try {
|
|
42631
|
-
const roles = await
|
|
43890
|
+
const roles = await listRoles({ workspaceRoot: request.workspaceRoot });
|
|
42632
43891
|
this.emit({
|
|
42633
|
-
type: "
|
|
43892
|
+
type: "roles/list/response",
|
|
42634
43893
|
payload: { requestId: request.requestId, roles, error: null }
|
|
42635
43894
|
});
|
|
42636
43895
|
} catch (error) {
|
|
42637
|
-
this.
|
|
43896
|
+
this.emitQuestRpcError(request, error);
|
|
43897
|
+
}
|
|
43898
|
+
}
|
|
43899
|
+
async handleRolesListScopeRequest(request) {
|
|
43900
|
+
const { scope, workspaceRoot, requestId } = request;
|
|
43901
|
+
try {
|
|
43902
|
+
const roles = await listRoles({
|
|
43903
|
+
scope,
|
|
43904
|
+
...workspaceRoot ? { workspaceRoot: expandTilde(workspaceRoot) } : {}
|
|
43905
|
+
});
|
|
43906
|
+
this.emit({
|
|
43907
|
+
type: "roles/list-scope/response",
|
|
43908
|
+
payload: { requestId, roles, error: null }
|
|
43909
|
+
});
|
|
43910
|
+
} catch (error) {
|
|
43911
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43912
|
+
this.sessionLogger.error({ err: error, scope }, "Failed to list roles by scope");
|
|
43913
|
+
this.emit({
|
|
43914
|
+
type: "roles/list-scope/response",
|
|
43915
|
+
payload: { requestId, roles: [], error: message }
|
|
43916
|
+
});
|
|
43917
|
+
}
|
|
43918
|
+
}
|
|
43919
|
+
async handleRoleCreateRequest(request) {
|
|
43920
|
+
const { scope, name, category, workspaceRoot, requestId } = request;
|
|
43921
|
+
try {
|
|
43922
|
+
const result = await createRole({
|
|
43923
|
+
scope,
|
|
43924
|
+
name,
|
|
43925
|
+
...category ? { category } : {},
|
|
43926
|
+
...workspaceRoot ? { workspaceRoot: expandTilde(workspaceRoot) } : {}
|
|
43927
|
+
});
|
|
43928
|
+
this.emit({
|
|
43929
|
+
type: "roles/create/response",
|
|
43930
|
+
payload: { requestId, path: result.path, error: null }
|
|
43931
|
+
});
|
|
43932
|
+
} catch (error) {
|
|
43933
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43934
|
+
this.sessionLogger.error({ err: error, scope, name }, "Failed to create role");
|
|
43935
|
+
this.emit({
|
|
43936
|
+
type: "roles/create/response",
|
|
43937
|
+
payload: { requestId, path: "", error: message }
|
|
43938
|
+
});
|
|
43939
|
+
}
|
|
43940
|
+
}
|
|
43941
|
+
async handleRoleWriteFrontmatterRequest(request) {
|
|
43942
|
+
const { path: rolePath, workspaceRoot, frontmatter, requestId } = request;
|
|
43943
|
+
try {
|
|
43944
|
+
await writeRoleFrontmatter(
|
|
43945
|
+
{ path: rolePath, frontmatter },
|
|
43946
|
+
workspaceRoot ? expandTilde(workspaceRoot) : void 0
|
|
43947
|
+
);
|
|
43948
|
+
this.emit({
|
|
43949
|
+
type: "roles/write-frontmatter/response",
|
|
43950
|
+
payload: { requestId, path: rolePath, error: null }
|
|
43951
|
+
});
|
|
43952
|
+
} catch (error) {
|
|
43953
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43954
|
+
this.sessionLogger.error({ err: error, path: rolePath }, "Failed to write role frontmatter");
|
|
43955
|
+
this.emit({
|
|
43956
|
+
type: "roles/write-frontmatter/response",
|
|
43957
|
+
payload: { requestId, path: rolePath, error: message }
|
|
43958
|
+
});
|
|
43959
|
+
}
|
|
43960
|
+
}
|
|
43961
|
+
async handleRoleMoveRequest(request) {
|
|
43962
|
+
const { path: rolePath, newName, newCategory, workspaceRoot, requestId } = request;
|
|
43963
|
+
try {
|
|
43964
|
+
const result = await moveRole(
|
|
43965
|
+
{ path: rolePath, newName, newCategory },
|
|
43966
|
+
workspaceRoot ? expandTilde(workspaceRoot) : void 0
|
|
43967
|
+
);
|
|
43968
|
+
this.emit({
|
|
43969
|
+
type: "roles/move/response",
|
|
43970
|
+
payload: { requestId, path: result.path, error: null }
|
|
43971
|
+
});
|
|
43972
|
+
} catch (error) {
|
|
43973
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43974
|
+
this.sessionLogger.error({ err: error, path: rolePath }, "Failed to move role");
|
|
43975
|
+
this.emit({
|
|
43976
|
+
type: "roles/move/response",
|
|
43977
|
+
payload: { requestId, path: rolePath, error: message }
|
|
43978
|
+
});
|
|
43979
|
+
}
|
|
43980
|
+
}
|
|
43981
|
+
async handleBrandsListScopeRequest(request) {
|
|
43982
|
+
const { workspaceRoot, requestId } = request;
|
|
43983
|
+
try {
|
|
43984
|
+
const brands = await listBrands({
|
|
43985
|
+
...workspaceRoot ? { workspaceRoot: expandTilde(workspaceRoot) } : {}
|
|
43986
|
+
});
|
|
43987
|
+
this.emit({
|
|
43988
|
+
type: "brands/list-scope/response",
|
|
43989
|
+
payload: { requestId, brands, error: null }
|
|
43990
|
+
});
|
|
43991
|
+
} catch (error) {
|
|
43992
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43993
|
+
this.sessionLogger.error({ err: error }, "Failed to list brands");
|
|
43994
|
+
this.emit({
|
|
43995
|
+
type: "brands/list-scope/response",
|
|
43996
|
+
payload: { requestId, brands: [], error: message }
|
|
43997
|
+
});
|
|
43998
|
+
}
|
|
43999
|
+
}
|
|
44000
|
+
async handleBrandCreateRequest(request) {
|
|
44001
|
+
const { name, workspaceRoot, requestId } = request;
|
|
44002
|
+
try {
|
|
44003
|
+
const result = await createBrand({
|
|
44004
|
+
name,
|
|
44005
|
+
...workspaceRoot ? { workspaceRoot: expandTilde(workspaceRoot) } : {}
|
|
44006
|
+
});
|
|
44007
|
+
this.emit({
|
|
44008
|
+
type: "brands/create/response",
|
|
44009
|
+
payload: { requestId, path: result.path, error: null }
|
|
44010
|
+
});
|
|
44011
|
+
} catch (error) {
|
|
44012
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44013
|
+
this.sessionLogger.error({ err: error, name }, "Failed to create brand");
|
|
44014
|
+
this.emit({
|
|
44015
|
+
type: "brands/create/response",
|
|
44016
|
+
payload: { requestId, path: "", error: message }
|
|
44017
|
+
});
|
|
44018
|
+
}
|
|
44019
|
+
}
|
|
44020
|
+
async handleBrandWriteFrontmatterRequest(request) {
|
|
44021
|
+
const { path: brandPath, workspaceRoot, frontmatter, requestId } = request;
|
|
44022
|
+
try {
|
|
44023
|
+
await writeBrandFrontmatter(
|
|
44024
|
+
{ path: brandPath, frontmatter },
|
|
44025
|
+
workspaceRoot ? expandTilde(workspaceRoot) : void 0
|
|
44026
|
+
);
|
|
44027
|
+
this.emit({
|
|
44028
|
+
type: "brands/write-frontmatter/response",
|
|
44029
|
+
payload: { requestId, path: brandPath, error: null }
|
|
44030
|
+
});
|
|
44031
|
+
} catch (error) {
|
|
44032
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44033
|
+
this.sessionLogger.error(
|
|
44034
|
+
{ err: error, path: brandPath },
|
|
44035
|
+
"Failed to write brand frontmatter"
|
|
44036
|
+
);
|
|
44037
|
+
this.emit({
|
|
44038
|
+
type: "brands/write-frontmatter/response",
|
|
44039
|
+
payload: { requestId, path: brandPath, error: message }
|
|
44040
|
+
});
|
|
44041
|
+
}
|
|
44042
|
+
}
|
|
44043
|
+
async handleBrandAssetCopyRequest(request) {
|
|
44044
|
+
const { workspaceRoot, sourcePath, targetName, requestId } = request;
|
|
44045
|
+
try {
|
|
44046
|
+
const result = await copyBrandAsset({
|
|
44047
|
+
workspaceRoot: expandTilde(workspaceRoot),
|
|
44048
|
+
sourcePath: expandTilde(sourcePath),
|
|
44049
|
+
targetName
|
|
44050
|
+
});
|
|
44051
|
+
this.emit({
|
|
44052
|
+
type: "brands/asset-copy/response",
|
|
44053
|
+
payload: {
|
|
44054
|
+
requestId,
|
|
44055
|
+
relativePath: result.relativePath,
|
|
44056
|
+
absolutePath: result.absolutePath,
|
|
44057
|
+
error: null
|
|
44058
|
+
}
|
|
44059
|
+
});
|
|
44060
|
+
} catch (error) {
|
|
44061
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44062
|
+
this.sessionLogger.error(
|
|
44063
|
+
{ err: error, sourcePath, targetName },
|
|
44064
|
+
"Failed to copy brand asset"
|
|
44065
|
+
);
|
|
44066
|
+
this.emit({
|
|
44067
|
+
type: "brands/asset-copy/response",
|
|
44068
|
+
payload: { requestId, relativePath: "", absolutePath: "", error: message }
|
|
44069
|
+
});
|
|
44070
|
+
}
|
|
44071
|
+
}
|
|
44072
|
+
async handleBrandAssetUploadRequest(request) {
|
|
44073
|
+
const { workspaceRoot, targetName, sourceName, dataBase64, requestId } = request;
|
|
44074
|
+
try {
|
|
44075
|
+
const result = await uploadBrandAsset({
|
|
44076
|
+
workspaceRoot: expandTilde(workspaceRoot),
|
|
44077
|
+
targetName,
|
|
44078
|
+
sourceName,
|
|
44079
|
+
dataBase64
|
|
44080
|
+
});
|
|
44081
|
+
this.emit({
|
|
44082
|
+
type: "brands/asset-upload/response",
|
|
44083
|
+
payload: {
|
|
44084
|
+
requestId,
|
|
44085
|
+
relativePath: result.relativePath,
|
|
44086
|
+
absolutePath: result.absolutePath,
|
|
44087
|
+
error: null
|
|
44088
|
+
}
|
|
44089
|
+
});
|
|
44090
|
+
} catch (error) {
|
|
44091
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44092
|
+
this.sessionLogger.error(
|
|
44093
|
+
{ err: error, targetName, sourceName },
|
|
44094
|
+
"Failed to upload brand asset"
|
|
44095
|
+
);
|
|
44096
|
+
this.emit({
|
|
44097
|
+
type: "brands/asset-upload/response",
|
|
44098
|
+
payload: { requestId, relativePath: "", absolutePath: "", error: message }
|
|
44099
|
+
});
|
|
42638
44100
|
}
|
|
42639
44101
|
}
|
|
42640
44102
|
emitTerminalsChangedSnapshot(input) {
|
|
@@ -43974,7 +45436,7 @@ var MissingDaemonVersionError = class extends Error {
|
|
|
43974
45436
|
}
|
|
43975
45437
|
};
|
|
43976
45438
|
var VoiceAssistantWebSocketServer = class {
|
|
43977
|
-
constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, appostleHome, daemonConfigStore, mcpBaseUrl, wsConfig, speech, terminalManager, dictation, agentProviderRuntimeSettings, providerOverrides, daemonVersion, onLifecycleIntent, projectRegistry, workspaceRegistry, chatService, loopService,
|
|
45439
|
+
constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, appostleHome, daemonConfigStore, mcpBaseUrl, wsConfig, speech, terminalManager, dictation, agentProviderRuntimeSettings, providerOverrides, daemonVersion, onLifecycleIntent, projectRegistry, workspaceRegistry, chatService, loopService, questService, scheduleService, checkoutDiffManager, scriptRouteStore, scriptRuntimeStore, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, resolveScriptHealth, workspaceGitService, github, gitlab) {
|
|
43978
45440
|
this.pendingConnections = /* @__PURE__ */ new Map();
|
|
43979
45441
|
this.sessions = /* @__PURE__ */ new Map();
|
|
43980
45442
|
this.externalSessionsByKey = /* @__PURE__ */ new Map();
|
|
@@ -44029,10 +45491,10 @@ var VoiceAssistantWebSocketServer = class {
|
|
|
44029
45491
|
throw new Error("VoiceAssistantWebSocketServer requires a loop service.");
|
|
44030
45492
|
}
|
|
44031
45493
|
this.loopService = loopService;
|
|
44032
|
-
if (!
|
|
44033
|
-
throw new Error("VoiceAssistantWebSocketServer requires a
|
|
45494
|
+
if (!questService) {
|
|
45495
|
+
throw new Error("VoiceAssistantWebSocketServer requires a quest service.");
|
|
44034
45496
|
}
|
|
44035
|
-
this.
|
|
45497
|
+
this.questService = questService;
|
|
44036
45498
|
if (!scheduleService) {
|
|
44037
45499
|
throw new Error("VoiceAssistantWebSocketServer requires a schedule service.");
|
|
44038
45500
|
}
|
|
@@ -44333,7 +45795,7 @@ var VoiceAssistantWebSocketServer = class {
|
|
|
44333
45795
|
workspaceRegistry: this.workspaceRegistry,
|
|
44334
45796
|
chatService: this.chatService,
|
|
44335
45797
|
loopService: this.loopService,
|
|
44336
|
-
|
|
45798
|
+
questService: this.questService,
|
|
44337
45799
|
scheduleService: this.scheduleService,
|
|
44338
45800
|
checkoutDiffManager: this.checkoutDiffManager,
|
|
44339
45801
|
github: this.github,
|
|
@@ -45153,7 +46615,7 @@ import { join as join12 } from "node:path";
|
|
|
45153
46615
|
// ../server/src/server/speech/providers/local/sherpa/model-downloader.ts
|
|
45154
46616
|
import { createWriteStream } from "node:fs";
|
|
45155
46617
|
import { mkdir as mkdir5, rename as rename2, rm as rm2, stat as stat5 } from "node:fs/promises";
|
|
45156
|
-
import
|
|
46618
|
+
import path18 from "node:path";
|
|
45157
46619
|
import { Readable as Readable2 } from "node:stream";
|
|
45158
46620
|
import { pipeline } from "node:stream/promises";
|
|
45159
46621
|
import { spawn as spawn7 } from "node:child_process";
|
|
@@ -45289,11 +46751,11 @@ function getSherpaOnnxModelSpec(id) {
|
|
|
45289
46751
|
// ../server/src/server/speech/providers/local/sherpa/model-downloader.ts
|
|
45290
46752
|
function getSherpaOnnxModelDir(modelsDir, modelId) {
|
|
45291
46753
|
const spec = getSherpaOnnxModelSpec(modelId);
|
|
45292
|
-
return
|
|
46754
|
+
return path18.join(modelsDir, spec.extractedDir);
|
|
45293
46755
|
}
|
|
45294
46756
|
async function hasRequiredFiles(modelDir, requiredFiles) {
|
|
45295
46757
|
for (const rel of requiredFiles) {
|
|
45296
|
-
const abs =
|
|
46758
|
+
const abs = path18.join(modelDir, rel);
|
|
45297
46759
|
try {
|
|
45298
46760
|
const s = await stat5(abs);
|
|
45299
46761
|
if (s.isDirectory()) {
|
|
@@ -45319,7 +46781,7 @@ async function downloadToFile(options) {
|
|
|
45319
46781
|
throw new Error(`Failed to download ${url}: missing response body`);
|
|
45320
46782
|
}
|
|
45321
46783
|
const tmpPath = `${outputPath}.tmp-${Date.now()}`;
|
|
45322
|
-
await mkdir5(
|
|
46784
|
+
await mkdir5(path18.dirname(outputPath), { recursive: true });
|
|
45323
46785
|
const nodeStream = Readable2.fromWeb(res.body);
|
|
45324
46786
|
try {
|
|
45325
46787
|
await pipeline(nodeStream, createWriteStream(tmpPath));
|
|
@@ -45356,16 +46818,16 @@ async function ensureSherpaOnnxModel(options) {
|
|
|
45356
46818
|
modelId: options.modelId
|
|
45357
46819
|
});
|
|
45358
46820
|
const spec = getSherpaOnnxModelSpec(options.modelId);
|
|
45359
|
-
const modelDir =
|
|
46821
|
+
const modelDir = path18.join(options.modelsDir, spec.extractedDir);
|
|
45360
46822
|
if (await hasRequiredFiles(modelDir, spec.requiredFiles)) {
|
|
45361
46823
|
return modelDir;
|
|
45362
46824
|
}
|
|
45363
46825
|
logger.info({ modelsDir: options.modelsDir }, "Starting model download");
|
|
45364
46826
|
try {
|
|
45365
46827
|
if (spec.archiveUrl) {
|
|
45366
|
-
const downloadsDir =
|
|
45367
|
-
const archiveFilename =
|
|
45368
|
-
const archivePath =
|
|
46828
|
+
const downloadsDir = path18.join(options.modelsDir, ".downloads");
|
|
46829
|
+
const archiveFilename = path18.basename(new URL(spec.archiveUrl).pathname);
|
|
46830
|
+
const archivePath = path18.join(downloadsDir, archiveFilename);
|
|
45369
46831
|
if (!await isNonEmptyFile(archivePath)) {
|
|
45370
46832
|
await downloadToFile({
|
|
45371
46833
|
url: spec.archiveUrl,
|
|
@@ -45410,7 +46872,7 @@ async function ensureSherpaOnnxModel(options) {
|
|
|
45410
46872
|
if (spec.downloadFiles && spec.downloadFiles.length > 0) {
|
|
45411
46873
|
await mkdir5(modelDir, { recursive: true });
|
|
45412
46874
|
for (const file of spec.downloadFiles) {
|
|
45413
|
-
const dst =
|
|
46875
|
+
const dst = path18.join(modelDir, file.relPath);
|
|
45414
46876
|
if (await isNonEmptyFile(dst)) {
|
|
45415
46877
|
continue;
|
|
45416
46878
|
}
|
|
@@ -45473,13 +46935,13 @@ import { existsSync as existsSync13 } from "node:fs";
|
|
|
45473
46935
|
|
|
45474
46936
|
// ../server/src/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.ts
|
|
45475
46937
|
import { createRequire as createRequire4 } from "node:module";
|
|
45476
|
-
import
|
|
46938
|
+
import path20 from "node:path";
|
|
45477
46939
|
import { existsSync as existsSync12 } from "node:fs";
|
|
45478
46940
|
import { spawnSync } from "node:child_process";
|
|
45479
46941
|
|
|
45480
46942
|
// ../server/src/server/speech/providers/local/sherpa/sherpa-runtime-env.ts
|
|
45481
46943
|
import { createRequire as createRequire3 } from "node:module";
|
|
45482
|
-
import
|
|
46944
|
+
import path19 from "node:path";
|
|
45483
46945
|
function sherpaPlatformArch(platform = process.platform, arch = process.arch) {
|
|
45484
46946
|
const normalizedPlatform = platform === "win32" ? "win" : platform;
|
|
45485
46947
|
return `${normalizedPlatform}-${arch}`;
|
|
@@ -45500,11 +46962,11 @@ function sherpaLoaderEnvKey(platform = process.platform) {
|
|
|
45500
46962
|
return null;
|
|
45501
46963
|
}
|
|
45502
46964
|
function prependEnvPath(existing, value) {
|
|
45503
|
-
const parts = (existing ?? "").split(
|
|
46965
|
+
const parts = (existing ?? "").split(path19.delimiter).filter(Boolean);
|
|
45504
46966
|
if (parts.includes(value)) {
|
|
45505
|
-
return parts.join(
|
|
46967
|
+
return parts.join(path19.delimiter);
|
|
45506
46968
|
}
|
|
45507
|
-
return [value, ...parts].join(
|
|
46969
|
+
return [value, ...parts].join(path19.delimiter);
|
|
45508
46970
|
}
|
|
45509
46971
|
function resolveSherpaLoaderEnv(platform = process.platform, arch = process.arch) {
|
|
45510
46972
|
const key = sherpaLoaderEnvKey(platform);
|
|
@@ -45517,7 +46979,7 @@ function resolveSherpaLoaderEnv(platform = process.platform, arch = process.arch
|
|
|
45517
46979
|
const pkgJson = require4.resolve(`${packageName}/package.json`);
|
|
45518
46980
|
return {
|
|
45519
46981
|
key,
|
|
45520
|
-
libDir:
|
|
46982
|
+
libDir: path19.dirname(pkgJson),
|
|
45521
46983
|
packageName
|
|
45522
46984
|
};
|
|
45523
46985
|
} catch {
|
|
@@ -45626,7 +47088,7 @@ function loadSherpaOnnxNode() {
|
|
|
45626
47088
|
const platformPkgDir = resolvedEnv?.libDir ?? null;
|
|
45627
47089
|
if (platformPkgDir) {
|
|
45628
47090
|
applySherpaLoaderEnv(process.env);
|
|
45629
|
-
const addonPath =
|
|
47091
|
+
const addonPath = path20.join(platformPkgDir, "sherpa-onnx.node");
|
|
45630
47092
|
if (existsSync12(addonPath)) {
|
|
45631
47093
|
const byPath = loadWithRequire(require4, addonPath, attempts);
|
|
45632
47094
|
if (byPath) {
|
|
@@ -46398,7 +47860,7 @@ var SherpaOnnxTTS = class {
|
|
|
46398
47860
|
|
|
46399
47861
|
// ../server/src/server/speech/providers/local/sherpa/silero-vad-provider.ts
|
|
46400
47862
|
import { copyFile, mkdir as mkdir6, stat as stat6 } from "node:fs/promises";
|
|
46401
|
-
import
|
|
47863
|
+
import path21 from "node:path";
|
|
46402
47864
|
|
|
46403
47865
|
// ../server/src/server/speech/providers/local/sherpa/silero-vad-session.ts
|
|
46404
47866
|
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
@@ -46590,8 +48052,8 @@ var SherpaSileroVadSession = class extends EventEmitter6 {
|
|
|
46590
48052
|
var SILERO_VAD_DIR = "silero-vad";
|
|
46591
48053
|
var SILERO_VAD_FILE = "silero_vad.onnx";
|
|
46592
48054
|
async function ensureSileroVadModel(modelsDir, logger) {
|
|
46593
|
-
const destDir =
|
|
46594
|
-
const destPath =
|
|
48055
|
+
const destDir = path21.join(modelsDir, SILERO_VAD_DIR);
|
|
48056
|
+
const destPath = path21.join(destDir, SILERO_VAD_FILE);
|
|
46595
48057
|
try {
|
|
46596
48058
|
const s = await stat6(destPath);
|
|
46597
48059
|
if (s.isFile() && s.size > 0) return destPath;
|
|
@@ -47245,7 +48707,7 @@ var OpenAISTT = class {
|
|
|
47245
48707
|
const supportsLogprobs = modelToUse === "gpt-4o-transcribe" || modelToUse === "gpt-4o-mini-transcribe";
|
|
47246
48708
|
const includeLogprobs = ["logprobs"];
|
|
47247
48709
|
const response = await this.openaiClient.audio.transcriptions.create({
|
|
47248
|
-
file: await import("fs").then((
|
|
48710
|
+
file: await import("fs").then((fs14) => fs14.createReadStream(tempFilePath)),
|
|
47249
48711
|
language,
|
|
47250
48712
|
model: modelToUse,
|
|
47251
48713
|
...supportsLogprobs ? { include: includeLogprobs } : {},
|
|
@@ -49213,6 +50675,11 @@ var AgentManager = class {
|
|
|
49213
50675
|
if (updates.labels) {
|
|
49214
50676
|
await this.setLabels(agentId, updates.labels);
|
|
49215
50677
|
}
|
|
50678
|
+
if (updates.internal !== void 0) {
|
|
50679
|
+
liveAgent.internal = updates.internal;
|
|
50680
|
+
await this.persistSnapshot(liveAgent, { internal: updates.internal });
|
|
50681
|
+
this.dispatch({ type: "agent_state", agent: { ...liveAgent } });
|
|
50682
|
+
}
|
|
49216
50683
|
return;
|
|
49217
50684
|
}
|
|
49218
50685
|
const registry = this.requireRegistry();
|
|
@@ -50649,8 +52116,8 @@ var AgentManager = class {
|
|
|
50649
52116
|
|
|
50650
52117
|
// ../server/src/server/agent/agent-storage.ts
|
|
50651
52118
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
50652
|
-
import { promises as
|
|
50653
|
-
import
|
|
52119
|
+
import { promises as fs11 } from "node:fs";
|
|
52120
|
+
import path22 from "node:path";
|
|
50654
52121
|
import { z as z37 } from "zod";
|
|
50655
52122
|
var SERIALIZABLE_CONFIG_SCHEMA = z37.object({
|
|
50656
52123
|
title: z37.string().nullable().optional(),
|
|
@@ -50737,12 +52204,12 @@ var AgentStorage = class {
|
|
|
50737
52204
|
}
|
|
50738
52205
|
const nextPath = this.buildRecordPath(record);
|
|
50739
52206
|
const previousPath = this.pathById.get(agentId);
|
|
50740
|
-
await
|
|
52207
|
+
await fs11.mkdir(path22.dirname(nextPath), { recursive: true });
|
|
50741
52208
|
await writeFileAtomically(nextPath, JSON.stringify(record, null, 2));
|
|
50742
52209
|
this.addIndexedPath(agentId, nextPath);
|
|
50743
52210
|
if (previousPath && previousPath !== nextPath) {
|
|
50744
52211
|
try {
|
|
50745
|
-
await
|
|
52212
|
+
await fs11.unlink(previousPath);
|
|
50746
52213
|
} catch {
|
|
50747
52214
|
}
|
|
50748
52215
|
this.removeIndexedPath(agentId, previousPath);
|
|
@@ -50770,7 +52237,7 @@ var AgentStorage = class {
|
|
|
50770
52237
|
const paths = Array.from(this.pathsById.get(agentId) ?? []);
|
|
50771
52238
|
for (const filePath of paths) {
|
|
50772
52239
|
try {
|
|
50773
|
-
await
|
|
52240
|
+
await fs11.unlink(filePath);
|
|
50774
52241
|
} catch (error) {
|
|
50775
52242
|
const code = error.code;
|
|
50776
52243
|
if (code && code !== "ENOENT") {
|
|
@@ -50844,7 +52311,7 @@ var AgentStorage = class {
|
|
|
50844
52311
|
const records = [];
|
|
50845
52312
|
let entries = [];
|
|
50846
52313
|
try {
|
|
50847
|
-
entries = await
|
|
52314
|
+
entries = await fs11.readdir(this.baseDir, { withFileTypes: true });
|
|
50848
52315
|
} catch (error) {
|
|
50849
52316
|
if (error.code === "ENOENT") {
|
|
50850
52317
|
return [];
|
|
@@ -50853,7 +52320,7 @@ var AgentStorage = class {
|
|
|
50853
52320
|
}
|
|
50854
52321
|
for (const entry of entries) {
|
|
50855
52322
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
50856
|
-
const rootPath =
|
|
52323
|
+
const rootPath = path22.join(this.baseDir, entry.name);
|
|
50857
52324
|
const rootRecord = await this.readRecordFile(rootPath);
|
|
50858
52325
|
if (!rootRecord) {
|
|
50859
52326
|
continue;
|
|
@@ -50867,10 +52334,10 @@ var AgentStorage = class {
|
|
|
50867
52334
|
if (!entry.isDirectory()) {
|
|
50868
52335
|
continue;
|
|
50869
52336
|
}
|
|
50870
|
-
const projectDir =
|
|
52337
|
+
const projectDir = path22.join(this.baseDir, entry.name);
|
|
50871
52338
|
let files = [];
|
|
50872
52339
|
try {
|
|
50873
|
-
files = await
|
|
52340
|
+
files = await fs11.readdir(projectDir, { withFileTypes: true });
|
|
50874
52341
|
} catch {
|
|
50875
52342
|
continue;
|
|
50876
52343
|
}
|
|
@@ -50878,7 +52345,7 @@ var AgentStorage = class {
|
|
|
50878
52345
|
if (!file.isFile() || !file.name.endsWith(".json")) {
|
|
50879
52346
|
continue;
|
|
50880
52347
|
}
|
|
50881
|
-
const filePath =
|
|
52348
|
+
const filePath = path22.join(projectDir, file.name);
|
|
50882
52349
|
const record = await this.readRecordFile(filePath);
|
|
50883
52350
|
if (!record) {
|
|
50884
52351
|
continue;
|
|
@@ -50893,7 +52360,7 @@ var AgentStorage = class {
|
|
|
50893
52360
|
}
|
|
50894
52361
|
async readRecordFile(filePath) {
|
|
50895
52362
|
try {
|
|
50896
|
-
const content = await
|
|
52363
|
+
const content = await fs11.readFile(filePath, "utf8");
|
|
50897
52364
|
const parsed = JSON.parse(content);
|
|
50898
52365
|
return parseStoredAgentRecord(parsed);
|
|
50899
52366
|
} catch (error) {
|
|
@@ -50903,7 +52370,7 @@ var AgentStorage = class {
|
|
|
50903
52370
|
}
|
|
50904
52371
|
buildRecordPath(record) {
|
|
50905
52372
|
const projectDir = projectDirNameFromCwd(record.cwd);
|
|
50906
|
-
return
|
|
52373
|
+
return path22.join(this.baseDir, projectDir, `${record.id}.json`);
|
|
50907
52374
|
}
|
|
50908
52375
|
addIndexedPath(agentId, filePath) {
|
|
50909
52376
|
const paths = this.pathsById.get(agentId) ?? /* @__PURE__ */ new Set();
|
|
@@ -50925,7 +52392,7 @@ var AgentStorage = class {
|
|
|
50925
52392
|
}
|
|
50926
52393
|
};
|
|
50927
52394
|
function projectDirNameFromCwd(cwd) {
|
|
50928
|
-
const { root } =
|
|
52395
|
+
const { root } = path22.win32.parse(cwd);
|
|
50929
52396
|
const withoutRoot = cwd.slice(root.length).replace(/[\\/]+$/, "");
|
|
50930
52397
|
const sanitizedRoot = root.replace(/[:\\/]+/g, "-").replace(/^-+|-+$/g, "");
|
|
50931
52398
|
const prefix = sanitizedRoot ? sanitizedRoot + "-" : "";
|
|
@@ -50935,10 +52402,10 @@ function projectDirNameFromCwd(cwd) {
|
|
|
50935
52402
|
return prefix + withoutRoot.replace(/[\\/]+/g, "-");
|
|
50936
52403
|
}
|
|
50937
52404
|
async function writeFileAtomically(targetPath, payload) {
|
|
50938
|
-
const directory =
|
|
50939
|
-
const tempPath =
|
|
50940
|
-
await
|
|
50941
|
-
await
|
|
52405
|
+
const directory = path22.dirname(targetPath);
|
|
52406
|
+
const tempPath = path22.join(directory, `.agent.tmp-${process.pid}-${Date.now()}-${randomUUID11()}`);
|
|
52407
|
+
await fs11.writeFile(tempPath, payload, "utf8");
|
|
52408
|
+
await fs11.rename(tempPath, targetPath);
|
|
50942
52409
|
}
|
|
50943
52410
|
|
|
50944
52411
|
// ../server/src/server/agent/mcp-server.ts
|
|
@@ -52444,7 +53911,7 @@ async function createMcpWorktree(options) {
|
|
|
52444
53911
|
}
|
|
52445
53912
|
|
|
52446
53913
|
// ../server/src/server/workspace-registry-bootstrap.ts
|
|
52447
|
-
import
|
|
53914
|
+
import path23 from "node:path";
|
|
52448
53915
|
function minIsoDate(left, right) {
|
|
52449
53916
|
if (!left) {
|
|
52450
53917
|
return right;
|
|
@@ -52541,8 +54008,8 @@ async function bootstrapWorkspaceRegistries(options) {
|
|
|
52541
54008
|
}
|
|
52542
54009
|
options.logger.info(
|
|
52543
54010
|
{
|
|
52544
|
-
projectsFile:
|
|
52545
|
-
workspacesFile:
|
|
54011
|
+
projectsFile: path23.join(options.appostleHome, "projects", "projects.json"),
|
|
54012
|
+
workspacesFile: path23.join(options.appostleHome, "projects", "workspaces.json"),
|
|
52546
54013
|
materializedProjects: projectRanges.size,
|
|
52547
54014
|
materializedWorkspaces: recordsByWorkspaceId.size
|
|
52548
54015
|
},
|
|
@@ -52738,8 +54205,8 @@ var CheckoutDiffManager = class {
|
|
|
52738
54205
|
|
|
52739
54206
|
// ../server/src/server/loop-service.ts
|
|
52740
54207
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
52741
|
-
import { promises as
|
|
52742
|
-
import
|
|
54208
|
+
import { promises as fs12 } from "node:fs";
|
|
54209
|
+
import path24 from "node:path";
|
|
52743
54210
|
import { z as z39 } from "zod";
|
|
52744
54211
|
var LOOP_ID_LENGTH = 8;
|
|
52745
54212
|
var DEFAULT_LOOP_PROVIDER = "claude";
|
|
@@ -52945,7 +54412,7 @@ var LoopService = class {
|
|
|
52945
54412
|
this.loops = /* @__PURE__ */ new Map();
|
|
52946
54413
|
this.persistQueue = Promise.resolve();
|
|
52947
54414
|
this.running = /* @__PURE__ */ new Map();
|
|
52948
|
-
this.storePath =
|
|
54415
|
+
this.storePath = path24.join(options.appostleHome, "loops", "loops.json");
|
|
52949
54416
|
this.logger = options.logger.child({ module: "loop-service" });
|
|
52950
54417
|
}
|
|
52951
54418
|
async initialize() {
|
|
@@ -52954,7 +54421,7 @@ var LoopService = class {
|
|
|
52954
54421
|
}
|
|
52955
54422
|
this.loops.clear();
|
|
52956
54423
|
try {
|
|
52957
|
-
const raw = await
|
|
54424
|
+
const raw = await fs12.readFile(this.storePath, "utf8");
|
|
52958
54425
|
const parsed = StoredLoopsSchema.parse(JSON.parse(raw));
|
|
52959
54426
|
for (const record of parsed) {
|
|
52960
54427
|
if (record.status === "running") {
|
|
@@ -53014,7 +54481,7 @@ var LoopService = class {
|
|
|
53014
54481
|
id: createLoopId(),
|
|
53015
54482
|
name: normalizeName(input.name),
|
|
53016
54483
|
prompt,
|
|
53017
|
-
cwd:
|
|
54484
|
+
cwd: path24.resolve(input.cwd),
|
|
53018
54485
|
provider: input.provider ?? DEFAULT_LOOP_PROVIDER,
|
|
53019
54486
|
model: normalizePrompt(input.model, "model"),
|
|
53020
54487
|
workerProvider: input.workerProvider ?? null,
|
|
@@ -53462,11 +54929,11 @@ ${output}` : `exit ${result.exitCode}`
|
|
|
53462
54929
|
}
|
|
53463
54930
|
async persist() {
|
|
53464
54931
|
const nextPersist = this.persistQueue.then(async () => {
|
|
53465
|
-
await
|
|
54932
|
+
await fs12.mkdir(path24.dirname(this.storePath), { recursive: true });
|
|
53466
54933
|
const records = Array.from(this.loops.values()).sort(
|
|
53467
54934
|
(left, right) => left.createdAt.localeCompare(right.createdAt)
|
|
53468
54935
|
);
|
|
53469
|
-
await
|
|
54936
|
+
await fs12.writeFile(this.storePath, JSON.stringify(records, null, 2), "utf8");
|
|
53470
54937
|
});
|
|
53471
54938
|
this.persistQueue = nextPersist.catch(() => {
|
|
53472
54939
|
});
|
|
@@ -54003,25 +55470,25 @@ var ScheduleService = class {
|
|
|
54003
55470
|
}
|
|
54004
55471
|
};
|
|
54005
55472
|
|
|
54006
|
-
// ../server/src/server/
|
|
55473
|
+
// ../server/src/server/quest/service.ts
|
|
54007
55474
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
54008
55475
|
|
|
54009
|
-
// ../server/src/server/
|
|
54010
|
-
import { promises as
|
|
54011
|
-
import
|
|
55476
|
+
// ../server/src/server/quest/store.ts
|
|
55477
|
+
import { promises as fs13 } from "node:fs";
|
|
55478
|
+
import path25 from "node:path";
|
|
54012
55479
|
import { z as z40 } from "zod";
|
|
54013
|
-
var
|
|
54014
|
-
var
|
|
55480
|
+
var StoredQuestsSchema = z40.array(QuestRecordSchema);
|
|
55481
|
+
var QuestStore = class {
|
|
54015
55482
|
constructor(options) {
|
|
54016
|
-
this.filePath =
|
|
55483
|
+
this.filePath = path25.join(options.appostleHome, "quests", "quests.json");
|
|
54017
55484
|
this.logger = options.logger;
|
|
54018
55485
|
this.records = /* @__PURE__ */ new Map();
|
|
54019
55486
|
this.persistQueue = Promise.resolve();
|
|
54020
55487
|
}
|
|
54021
55488
|
async initialize() {
|
|
54022
55489
|
try {
|
|
54023
|
-
const raw = await
|
|
54024
|
-
const parsed =
|
|
55490
|
+
const raw = await fs13.readFile(this.filePath, "utf8");
|
|
55491
|
+
const parsed = StoredQuestsSchema.parse(JSON.parse(raw));
|
|
54025
55492
|
this.records = new Map(parsed.map((r) => [r.id, r]));
|
|
54026
55493
|
} catch (err) {
|
|
54027
55494
|
const code = err && typeof err === "object" && "code" in err ? err.code : null;
|
|
@@ -54031,15 +55498,13 @@ var WorkflowStore = class {
|
|
|
54031
55498
|
}
|
|
54032
55499
|
this.logger.warn(
|
|
54033
55500
|
{ err: err instanceof Error ? err.message : String(err) },
|
|
54034
|
-
"
|
|
55501
|
+
"quest-store: failed to read quests.json \u2014 starting empty"
|
|
54035
55502
|
);
|
|
54036
55503
|
this.records = /* @__PURE__ */ new Map();
|
|
54037
55504
|
}
|
|
54038
55505
|
}
|
|
54039
55506
|
list() {
|
|
54040
|
-
return [...this.records.values()].sort(
|
|
54041
|
-
(a, b) => a.createdAt.localeCompare(b.createdAt)
|
|
54042
|
-
);
|
|
55507
|
+
return [...this.records.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
54043
55508
|
}
|
|
54044
55509
|
get(id) {
|
|
54045
55510
|
return this.records.get(id) ?? null;
|
|
@@ -54056,20 +55521,20 @@ var WorkflowStore = class {
|
|
|
54056
55521
|
}
|
|
54057
55522
|
async persist() {
|
|
54058
55523
|
this.persistQueue = this.persistQueue.then(async () => {
|
|
54059
|
-
await
|
|
55524
|
+
await fs13.mkdir(path25.dirname(this.filePath), { recursive: true });
|
|
54060
55525
|
const payload = JSON.stringify([...this.records.values()], null, 2);
|
|
54061
|
-
await
|
|
55526
|
+
await fs13.writeFile(this.filePath, payload, "utf8");
|
|
54062
55527
|
}).catch((err) => {
|
|
54063
55528
|
this.logger.error(
|
|
54064
55529
|
{ err: err instanceof Error ? err.message : String(err) },
|
|
54065
|
-
"
|
|
55530
|
+
"quest-store: persist failed"
|
|
54066
55531
|
);
|
|
54067
55532
|
});
|
|
54068
55533
|
return this.persistQueue;
|
|
54069
55534
|
}
|
|
54070
55535
|
};
|
|
54071
55536
|
|
|
54072
|
-
// ../server/src/server/
|
|
55537
|
+
// ../server/src/server/quest/rules.ts
|
|
54073
55538
|
var LOOP_RULES = {
|
|
54074
55539
|
perCardRole: "inherit",
|
|
54075
55540
|
perCardModel: "inherit",
|
|
@@ -54086,28 +55551,65 @@ var RALPH_RULES = {
|
|
|
54086
55551
|
summarizerCard: "off",
|
|
54087
55552
|
cardExpansion: "tool-calls"
|
|
54088
55553
|
};
|
|
55554
|
+
var RUN_RULES = {
|
|
55555
|
+
perCardRole: "free",
|
|
55556
|
+
perCardModel: "free",
|
|
55557
|
+
perCardPrompt: "locked",
|
|
55558
|
+
cardsEditableWhileRunning: false,
|
|
55559
|
+
summarizerCard: "off",
|
|
55560
|
+
cardExpansion: "tool-calls"
|
|
55561
|
+
};
|
|
55562
|
+
var ORCHESTRATE_RULES = {
|
|
55563
|
+
perCardRole: "free",
|
|
55564
|
+
perCardModel: "free",
|
|
55565
|
+
perCardPrompt: "locked",
|
|
55566
|
+
cardsEditableWhileRunning: false,
|
|
55567
|
+
summarizerCard: "off",
|
|
55568
|
+
cardExpansion: "tool-calls"
|
|
55569
|
+
};
|
|
54089
55570
|
function getRulesForKind(kind) {
|
|
54090
55571
|
switch (kind) {
|
|
54091
55572
|
case "loop":
|
|
54092
55573
|
return { ...LOOP_RULES };
|
|
54093
55574
|
case "ralph":
|
|
54094
55575
|
return { ...RALPH_RULES };
|
|
55576
|
+
case "run":
|
|
55577
|
+
return { ...RUN_RULES };
|
|
55578
|
+
case "orchestrate":
|
|
55579
|
+
return { ...ORCHESTRATE_RULES };
|
|
54095
55580
|
}
|
|
54096
55581
|
}
|
|
54097
55582
|
|
|
54098
|
-
// ../server/src/server/
|
|
54099
|
-
var
|
|
55583
|
+
// ../server/src/server/quest/service.ts
|
|
55584
|
+
var QUEST_ID_LENGTH = 8;
|
|
54100
55585
|
var DEFAULT_PROVIDER = "claude";
|
|
54101
55586
|
function nowIso2() {
|
|
54102
55587
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
54103
55588
|
}
|
|
54104
|
-
function
|
|
54105
|
-
return randomUUID14().replace(/-/g, "").slice(0,
|
|
55589
|
+
function createQuestId() {
|
|
55590
|
+
return randomUUID14().replace(/-/g, "").slice(0, QUEST_ID_LENGTH);
|
|
54106
55591
|
}
|
|
54107
55592
|
function defaultWiring() {
|
|
54108
55593
|
return { inputs: "user-prompt", outputs: "none" };
|
|
54109
55594
|
}
|
|
54110
55595
|
function buildInitialCards(options, rulesAllowOverride) {
|
|
55596
|
+
if (options.kind === "orchestrate") {
|
|
55597
|
+
const overrides2 = options.cards ?? [];
|
|
55598
|
+
return overrides2.map(
|
|
55599
|
+
(override, i) => QuestCardSchema.parse({
|
|
55600
|
+
index: i + 1,
|
|
55601
|
+
role: rulesAllowOverride.role ? override.role ?? null : null,
|
|
55602
|
+
model: rulesAllowOverride.model ? override.model ?? null : null,
|
|
55603
|
+
prompt: rulesAllowOverride.prompt ? override.prompt ?? null : null,
|
|
55604
|
+
wiring: defaultWiring(),
|
|
55605
|
+
status: "queued",
|
|
55606
|
+
agentId: null,
|
|
55607
|
+
startedAt: null,
|
|
55608
|
+
completedAt: null,
|
|
55609
|
+
verify: null
|
|
55610
|
+
})
|
|
55611
|
+
);
|
|
55612
|
+
}
|
|
54111
55613
|
if (options.termination.type !== "count") {
|
|
54112
55614
|
return [];
|
|
54113
55615
|
}
|
|
@@ -54117,7 +55619,7 @@ function buildInitialCards(options, rulesAllowOverride) {
|
|
|
54117
55619
|
for (let i = 0; i < n; i++) {
|
|
54118
55620
|
const override = overrides[i] ?? {};
|
|
54119
55621
|
cards.push(
|
|
54120
|
-
|
|
55622
|
+
QuestCardSchema.parse({
|
|
54121
55623
|
index: i + 1,
|
|
54122
55624
|
role: rulesAllowOverride.role ? override.role ?? null : null,
|
|
54123
55625
|
model: rulesAllowOverride.model ? override.model ?? null : null,
|
|
@@ -54140,7 +55642,7 @@ function buildSummarizerCard(options) {
|
|
|
54140
55642
|
if (!options.summarizerEnabled) {
|
|
54141
55643
|
return null;
|
|
54142
55644
|
}
|
|
54143
|
-
return
|
|
55645
|
+
return QuestCardSchema.parse({
|
|
54144
55646
|
index: 0,
|
|
54145
55647
|
// sentinel — summarizer is not in the cards array
|
|
54146
55648
|
role: null,
|
|
@@ -54154,14 +55656,14 @@ function buildSummarizerCard(options) {
|
|
|
54154
55656
|
verify: null
|
|
54155
55657
|
});
|
|
54156
55658
|
}
|
|
54157
|
-
var
|
|
55659
|
+
var QuestService = class {
|
|
54158
55660
|
constructor(options) {
|
|
54159
55661
|
this.options = options;
|
|
54160
55662
|
this.running = /* @__PURE__ */ new Map();
|
|
54161
55663
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
54162
55664
|
this.loaded = false;
|
|
54163
|
-
this.logger = options.logger.child({ module: "
|
|
54164
|
-
this.store = new
|
|
55665
|
+
this.logger = options.logger.child({ module: "quest-service" });
|
|
55666
|
+
this.store = new QuestStore({
|
|
54165
55667
|
appostleHome: options.appostleHome,
|
|
54166
55668
|
logger: this.logger
|
|
54167
55669
|
});
|
|
@@ -54179,7 +55681,7 @@ var WorkflowService = class {
|
|
|
54179
55681
|
get agentManager() {
|
|
54180
55682
|
return this.options.agentManager;
|
|
54181
55683
|
}
|
|
54182
|
-
/** Subscribe to
|
|
55684
|
+
/** Subscribe to quest state changes. Returns an unsubscribe function. */
|
|
54183
55685
|
subscribe(callback) {
|
|
54184
55686
|
this.subscribers.add(callback);
|
|
54185
55687
|
return () => {
|
|
@@ -54195,7 +55697,7 @@ var WorkflowService = class {
|
|
|
54195
55697
|
if (record.status !== "running") {
|
|
54196
55698
|
continue;
|
|
54197
55699
|
}
|
|
54198
|
-
const recovered =
|
|
55700
|
+
const recovered = QuestRecordSchema.parse({
|
|
54199
55701
|
...record,
|
|
54200
55702
|
status: "stopped",
|
|
54201
55703
|
updatedAt: nowIso2(),
|
|
@@ -54204,12 +55706,13 @@ var WorkflowService = class {
|
|
|
54204
55706
|
cards: record.cards.map(
|
|
54205
55707
|
(card) => card.status === "running" ? { ...card, status: "stopped", completedAt: nowIso2() } : card
|
|
54206
55708
|
),
|
|
54207
|
-
summarizerCard: record.summarizerCard && record.summarizerCard.status === "running" ? { ...record.summarizerCard, status: "stopped", completedAt: nowIso2() } : record.summarizerCard
|
|
55709
|
+
summarizerCard: record.summarizerCard && record.summarizerCard.status === "running" ? { ...record.summarizerCard, status: "stopped", completedAt: nowIso2() } : record.summarizerCard,
|
|
55710
|
+
queenCard: record.queenCard && record.queenCard.status === "running" ? { ...record.queenCard, status: "stopped", completedAt: nowIso2() } : record.queenCard
|
|
54208
55711
|
});
|
|
54209
55712
|
await this.store.upsert(recovered);
|
|
54210
55713
|
this.logger.info(
|
|
54211
|
-
{
|
|
54212
|
-
"
|
|
55714
|
+
{ questId: recovered.id },
|
|
55715
|
+
"quest-service: recovered running quest \u2192 stopped"
|
|
54213
55716
|
);
|
|
54214
55717
|
}
|
|
54215
55718
|
this.loaded = true;
|
|
@@ -54223,9 +55726,15 @@ var WorkflowService = class {
|
|
|
54223
55726
|
if (options.kind === "loop" && options.termination.type !== "count") {
|
|
54224
55727
|
throw new Error('kind "loop" requires termination.type === "count"');
|
|
54225
55728
|
}
|
|
55729
|
+
if (options.kind === "run" && options.termination.type !== "count") {
|
|
55730
|
+
throw new Error('kind "run" requires termination.type === "count"');
|
|
55731
|
+
}
|
|
54226
55732
|
if (options.kind === "ralph" && options.termination.type !== "verify") {
|
|
54227
55733
|
throw new Error('kind "ralph" requires termination.type === "verify"');
|
|
54228
55734
|
}
|
|
55735
|
+
if (options.kind === "orchestrate" && options.termination.type !== "orchestrate") {
|
|
55736
|
+
throw new Error('kind "orchestrate" requires termination.type === "orchestrate"');
|
|
55737
|
+
}
|
|
54229
55738
|
const rules = getRulesForKind(options.kind);
|
|
54230
55739
|
const overrideAllowed = {
|
|
54231
55740
|
role: rules.perCardRole !== "locked",
|
|
@@ -54233,8 +55742,8 @@ var WorkflowService = class {
|
|
|
54233
55742
|
prompt: rules.perCardPrompt !== "locked"
|
|
54234
55743
|
};
|
|
54235
55744
|
const createdAt = nowIso2();
|
|
54236
|
-
const record =
|
|
54237
|
-
id:
|
|
55745
|
+
const record = QuestRecordSchema.parse({
|
|
55746
|
+
id: createQuestId(),
|
|
54238
55747
|
kind: options.kind,
|
|
54239
55748
|
rules,
|
|
54240
55749
|
cwd: options.cwd,
|
|
@@ -54255,7 +55764,7 @@ var WorkflowService = class {
|
|
|
54255
55764
|
this.publish(record);
|
|
54256
55765
|
const runner = this.runners[options.kind];
|
|
54257
55766
|
if (!runner) {
|
|
54258
|
-
const failed =
|
|
55767
|
+
const failed = QuestRecordSchema.parse({
|
|
54259
55768
|
...record,
|
|
54260
55769
|
status: "failed",
|
|
54261
55770
|
updatedAt: nowIso2(),
|
|
@@ -54263,21 +55772,18 @@ var WorkflowService = class {
|
|
|
54263
55772
|
});
|
|
54264
55773
|
await this.store.upsert(failed);
|
|
54265
55774
|
this.publish(failed);
|
|
54266
|
-
throw new Error(`No runner registered for
|
|
55775
|
+
throw new Error(`No runner registered for quest kind "${options.kind}"`);
|
|
54267
55776
|
}
|
|
54268
55777
|
const abortController = new AbortController();
|
|
54269
55778
|
const promise = (async () => {
|
|
54270
55779
|
try {
|
|
54271
55780
|
await runner.execute({
|
|
54272
|
-
|
|
55781
|
+
questId: record.id,
|
|
54273
55782
|
signal: abortController.signal,
|
|
54274
55783
|
service: this
|
|
54275
55784
|
});
|
|
54276
55785
|
} catch (err) {
|
|
54277
|
-
this.logger.error(
|
|
54278
|
-
{ err, workflowId: record.id },
|
|
54279
|
-
"workflow-service: runner threw"
|
|
54280
|
-
);
|
|
55786
|
+
this.logger.error({ err, questId: record.id }, "quest-service: runner threw");
|
|
54281
55787
|
const current = this.store.get(record.id);
|
|
54282
55788
|
if (current && current.status === "running") {
|
|
54283
55789
|
await this.markStatus(record.id, "failed");
|
|
@@ -54298,13 +55804,13 @@ var WorkflowService = class {
|
|
|
54298
55804
|
async stop(id) {
|
|
54299
55805
|
const current = this.store.get(id);
|
|
54300
55806
|
if (!current) {
|
|
54301
|
-
throw new Error(`
|
|
55807
|
+
throw new Error(`Quest "${id}" not found`);
|
|
54302
55808
|
}
|
|
54303
55809
|
if (current.status !== "running") {
|
|
54304
55810
|
return current;
|
|
54305
55811
|
}
|
|
54306
55812
|
const stopRequestedAt = nowIso2();
|
|
54307
|
-
const updated =
|
|
55813
|
+
const updated = QuestRecordSchema.parse({
|
|
54308
55814
|
...current,
|
|
54309
55815
|
stopRequestedAt,
|
|
54310
55816
|
updatedAt: stopRequestedAt
|
|
@@ -54325,45 +55831,76 @@ var WorkflowService = class {
|
|
|
54325
55831
|
// Mutation helpers (used by runners)
|
|
54326
55832
|
// -------------------------------------------------------------------------
|
|
54327
55833
|
/** Updates a single card by index (1-based). Throws if not found. */
|
|
54328
|
-
async updateCard(
|
|
54329
|
-
const record = this.store.get(
|
|
55834
|
+
async updateCard(questId, cardIndex, patch) {
|
|
55835
|
+
const record = this.store.get(questId);
|
|
54330
55836
|
if (!record) {
|
|
54331
|
-
throw new Error(`
|
|
55837
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54332
55838
|
}
|
|
54333
55839
|
const cards = record.cards.map(
|
|
54334
|
-
(card) => card.index === cardIndex ?
|
|
55840
|
+
(card) => card.index === cardIndex ? QuestCardSchema.parse({ ...card, ...patch }) : card
|
|
54335
55841
|
);
|
|
54336
55842
|
return this.persistAndPublish(record, { cards });
|
|
54337
55843
|
}
|
|
54338
55844
|
/** Append a card (used by ralph runner as iterations execute). */
|
|
54339
|
-
async appendCard(
|
|
54340
|
-
const record = this.store.get(
|
|
55845
|
+
async appendCard(questId, card) {
|
|
55846
|
+
const record = this.store.get(questId);
|
|
54341
55847
|
if (!record) {
|
|
54342
|
-
throw new Error(`
|
|
55848
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54343
55849
|
}
|
|
54344
|
-
const cards = [...record.cards,
|
|
55850
|
+
const cards = [...record.cards, QuestCardSchema.parse(card)];
|
|
54345
55851
|
return this.persistAndPublish(record, { cards });
|
|
54346
55852
|
}
|
|
54347
55853
|
/** Set the summarizer card (creates or updates). */
|
|
54348
|
-
async updateSummarizer(
|
|
54349
|
-
const record = this.store.get(
|
|
55854
|
+
async updateSummarizer(questId, patch) {
|
|
55855
|
+
const record = this.store.get(questId);
|
|
54350
55856
|
if (!record) {
|
|
54351
|
-
throw new Error(`
|
|
55857
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54352
55858
|
}
|
|
54353
55859
|
if (!record.summarizerCard) {
|
|
54354
|
-
throw new Error(`
|
|
55860
|
+
throw new Error(`Quest "${questId}" has no summarizer card`);
|
|
54355
55861
|
}
|
|
54356
|
-
const summarizerCard =
|
|
55862
|
+
const summarizerCard = QuestCardSchema.parse({
|
|
54357
55863
|
...record.summarizerCard,
|
|
54358
55864
|
...patch
|
|
54359
55865
|
});
|
|
54360
55866
|
return this.persistAndPublish(record, { summarizerCard });
|
|
54361
55867
|
}
|
|
54362
|
-
/**
|
|
54363
|
-
async
|
|
54364
|
-
const record = this.store.get(
|
|
55868
|
+
/** Set or replace the queen-bee card on an orchestrate quest. */
|
|
55869
|
+
async setQueenCard(questId, card) {
|
|
55870
|
+
const record = this.store.get(questId);
|
|
55871
|
+
if (!record) {
|
|
55872
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
55873
|
+
}
|
|
55874
|
+
return this.persistAndPublish(record, { queenCard: QuestCardSchema.parse(card) });
|
|
55875
|
+
}
|
|
55876
|
+
/** Patch the queen-bee card. Throws if no queen card exists. */
|
|
55877
|
+
async updateQueenCard(questId, patch) {
|
|
55878
|
+
const record = this.store.get(questId);
|
|
55879
|
+
if (!record) {
|
|
55880
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
55881
|
+
}
|
|
55882
|
+
if (!record.queenCard) {
|
|
55883
|
+
throw new Error(`Quest "${questId}" has no queen card`);
|
|
55884
|
+
}
|
|
55885
|
+
const queenCard = QuestCardSchema.parse({
|
|
55886
|
+
...record.queenCard,
|
|
55887
|
+
...patch
|
|
55888
|
+
});
|
|
55889
|
+
return this.persistAndPublish(record, { queenCard });
|
|
55890
|
+
}
|
|
55891
|
+
/** Record the hivemind doc absolute path for an orchestrate quest. */
|
|
55892
|
+
async setHivemindPath(questId, hivemindPath) {
|
|
55893
|
+
const record = this.store.get(questId);
|
|
55894
|
+
if (!record) {
|
|
55895
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
55896
|
+
}
|
|
55897
|
+
return this.persistAndPublish(record, { hivemindPath });
|
|
55898
|
+
}
|
|
55899
|
+
/** Mark the quest's terminal status. Idempotent — running→stopped is fine. */
|
|
55900
|
+
async markStatus(questId, status) {
|
|
55901
|
+
const record = this.store.get(questId);
|
|
54365
55902
|
if (!record) {
|
|
54366
|
-
throw new Error(`
|
|
55903
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54367
55904
|
}
|
|
54368
55905
|
const completedAt = status === "running" ? null : record.completedAt ?? nowIso2();
|
|
54369
55906
|
return this.persistAndPublish(record, { status, completedAt });
|
|
@@ -54372,7 +55909,7 @@ var WorkflowService = class {
|
|
|
54372
55909
|
// Internals
|
|
54373
55910
|
// -------------------------------------------------------------------------
|
|
54374
55911
|
async persistAndPublish(record, patch) {
|
|
54375
|
-
const updated =
|
|
55912
|
+
const updated = QuestRecordSchema.parse({
|
|
54376
55913
|
...record,
|
|
54377
55914
|
...patch,
|
|
54378
55915
|
updatedAt: nowIso2()
|
|
@@ -54388,25 +55925,36 @@ var WorkflowService = class {
|
|
|
54388
55925
|
} catch (err) {
|
|
54389
55926
|
this.logger.warn(
|
|
54390
55927
|
{ err: err instanceof Error ? err.message : String(err) },
|
|
54391
|
-
"
|
|
55928
|
+
"quest-service: subscriber threw"
|
|
54392
55929
|
);
|
|
54393
55930
|
}
|
|
54394
55931
|
}
|
|
54395
55932
|
}
|
|
54396
55933
|
};
|
|
54397
55934
|
|
|
54398
|
-
// ../server/src/server/
|
|
55935
|
+
// ../server/src/server/quest/runner-loop.ts
|
|
54399
55936
|
function nowIso3() {
|
|
54400
55937
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
54401
55938
|
}
|
|
54402
|
-
function buildAgentConfig(record, card, titleSuffix) {
|
|
55939
|
+
async function buildAgentConfig(record, card, titleSuffix) {
|
|
54403
55940
|
const modeId = getProviderFullAccessModeId(record.defaultProvider) ?? void 0;
|
|
55941
|
+
let systemPrompt;
|
|
55942
|
+
let allowedTools;
|
|
55943
|
+
if (card.role) {
|
|
55944
|
+
const role = await resolveRole({ roleName: card.role, workspaceRoot: record.cwd });
|
|
55945
|
+
if (role) {
|
|
55946
|
+
if (role.body) systemPrompt = role.body;
|
|
55947
|
+
if (role.allowedTools.length > 0) allowedTools = role.allowedTools;
|
|
55948
|
+
}
|
|
55949
|
+
}
|
|
54404
55950
|
return {
|
|
54405
55951
|
provider: record.defaultProvider,
|
|
54406
55952
|
cwd: record.cwd,
|
|
54407
55953
|
model: card.model ?? record.defaultModel ?? void 0,
|
|
54408
55954
|
modeId,
|
|
54409
|
-
|
|
55955
|
+
systemPrompt,
|
|
55956
|
+
allowedTools,
|
|
55957
|
+
title: `quest ${record.id} ${titleSuffix}`,
|
|
54410
55958
|
internal: true
|
|
54411
55959
|
};
|
|
54412
55960
|
}
|
|
@@ -54427,21 +55975,21 @@ function resolveFinalTextFromRun(run) {
|
|
|
54427
55975
|
}
|
|
54428
55976
|
var LoopRunner = class {
|
|
54429
55977
|
constructor(options) {
|
|
54430
|
-
this.logger = options.logger.child({ module: "
|
|
55978
|
+
this.logger = options.logger.child({ module: "quest-runner-loop" });
|
|
54431
55979
|
}
|
|
54432
55980
|
async execute(args) {
|
|
54433
|
-
const {
|
|
54434
|
-
const initial = service.getRecord(
|
|
55981
|
+
const { questId, signal, service } = args;
|
|
55982
|
+
const initial = service.getRecord(questId);
|
|
54435
55983
|
if (!initial) {
|
|
54436
|
-
throw new Error(`
|
|
55984
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54437
55985
|
}
|
|
54438
|
-
if (initial.kind !== "loop") {
|
|
54439
|
-
throw new Error(`LoopRunner cannot execute
|
|
55986
|
+
if (initial.kind !== "loop" && initial.kind !== "run") {
|
|
55987
|
+
throw new Error(`LoopRunner cannot execute quest of kind "${initial.kind}"`);
|
|
54440
55988
|
}
|
|
54441
55989
|
const completedFinalTexts = [];
|
|
54442
55990
|
let aborted = false;
|
|
54443
55991
|
for (const card of initial.cards) {
|
|
54444
|
-
const current = service.getRecord(
|
|
55992
|
+
const current = service.getRecord(questId);
|
|
54445
55993
|
if (!current || current.status !== "running") {
|
|
54446
55994
|
aborted = true;
|
|
54447
55995
|
break;
|
|
@@ -54467,32 +56015,32 @@ var LoopRunner = class {
|
|
|
54467
56015
|
break;
|
|
54468
56016
|
}
|
|
54469
56017
|
if (result.status === "failed") {
|
|
54470
|
-
await service.markStatus(
|
|
56018
|
+
await service.markStatus(questId, "failed");
|
|
54471
56019
|
return;
|
|
54472
56020
|
}
|
|
54473
56021
|
}
|
|
54474
56022
|
if (aborted) {
|
|
54475
|
-
const current = service.getRecord(
|
|
56023
|
+
const current = service.getRecord(questId);
|
|
54476
56024
|
if (current) {
|
|
54477
56025
|
for (const card of current.cards) {
|
|
54478
56026
|
if (card.status === "queued" || card.status === "running") {
|
|
54479
|
-
await service.updateCard(
|
|
56027
|
+
await service.updateCard(questId, card.index, {
|
|
54480
56028
|
status: "stopped",
|
|
54481
56029
|
completedAt: nowIso3()
|
|
54482
56030
|
});
|
|
54483
56031
|
}
|
|
54484
56032
|
}
|
|
54485
56033
|
if (current.summarizerCard && current.summarizerCard.status === "queued") {
|
|
54486
|
-
await service.updateSummarizer(
|
|
56034
|
+
await service.updateSummarizer(questId, {
|
|
54487
56035
|
status: "stopped",
|
|
54488
56036
|
completedAt: nowIso3()
|
|
54489
56037
|
});
|
|
54490
56038
|
}
|
|
54491
56039
|
}
|
|
54492
|
-
await service.markStatus(
|
|
56040
|
+
await service.markStatus(questId, "stopped");
|
|
54493
56041
|
return;
|
|
54494
56042
|
}
|
|
54495
|
-
const beforeSummary = service.getRecord(
|
|
56043
|
+
const beforeSummary = service.getRecord(questId);
|
|
54496
56044
|
if (beforeSummary?.summarizerCard) {
|
|
54497
56045
|
const summaryPrompt = buildSummaryPrompt(initial.prompt, completedFinalTexts);
|
|
54498
56046
|
const summaryResult = await this.runSummarizer({
|
|
@@ -54502,21 +56050,21 @@ var LoopRunner = class {
|
|
|
54502
56050
|
signal
|
|
54503
56051
|
});
|
|
54504
56052
|
if (summaryResult.status === "failed") {
|
|
54505
|
-
await service.markStatus(
|
|
56053
|
+
await service.markStatus(questId, "failed");
|
|
54506
56054
|
return;
|
|
54507
56055
|
}
|
|
54508
56056
|
if (summaryResult.status === "stopped") {
|
|
54509
|
-
await service.markStatus(
|
|
56057
|
+
await service.markStatus(questId, "stopped");
|
|
54510
56058
|
return;
|
|
54511
56059
|
}
|
|
54512
56060
|
}
|
|
54513
|
-
await service.markStatus(
|
|
56061
|
+
await service.markStatus(questId, "succeeded");
|
|
54514
56062
|
}
|
|
54515
56063
|
async runCard(args) {
|
|
54516
56064
|
const { record, card, prompt, titleSuffix, service, signal } = args;
|
|
54517
56065
|
const startedAt = nowIso3();
|
|
54518
56066
|
const agent = await service.agentManager.createAgent(
|
|
54519
|
-
buildAgentConfig(record, card, titleSuffix)
|
|
56067
|
+
await buildAgentConfig(record, card, titleSuffix)
|
|
54520
56068
|
);
|
|
54521
56069
|
await service.updateCard(record.id, card.index, {
|
|
54522
56070
|
status: "running",
|
|
@@ -54545,7 +56093,7 @@ var LoopRunner = class {
|
|
|
54545
56093
|
return { status: "succeeded", finalText };
|
|
54546
56094
|
} catch (err) {
|
|
54547
56095
|
this.logger.error(
|
|
54548
|
-
{ err,
|
|
56096
|
+
{ err, questId: record.id, cardIndex: card.index },
|
|
54549
56097
|
"loop-runner: card failed"
|
|
54550
56098
|
);
|
|
54551
56099
|
await service.updateCard(record.id, card.index, {
|
|
@@ -54569,7 +56117,7 @@ var LoopRunner = class {
|
|
|
54569
56117
|
}
|
|
54570
56118
|
const startedAt = nowIso3();
|
|
54571
56119
|
const agent = await service.agentManager.createAgent(
|
|
54572
|
-
buildAgentConfig(record, summarizer, "summary")
|
|
56120
|
+
await buildAgentConfig(record, summarizer, "summary")
|
|
54573
56121
|
);
|
|
54574
56122
|
await service.updateSummarizer(record.id, {
|
|
54575
56123
|
status: "running",
|
|
@@ -54597,10 +56145,7 @@ var LoopRunner = class {
|
|
|
54597
56145
|
});
|
|
54598
56146
|
return { status: "succeeded", finalText };
|
|
54599
56147
|
} catch (err) {
|
|
54600
|
-
this.logger.error(
|
|
54601
|
-
{ err, workflowId: record.id },
|
|
54602
|
-
"loop-runner: summarizer failed"
|
|
54603
|
-
);
|
|
56148
|
+
this.logger.error({ err, questId: record.id }, "loop-runner: summarizer failed");
|
|
54604
56149
|
await service.updateSummarizer(record.id, {
|
|
54605
56150
|
status: "failed",
|
|
54606
56151
|
completedAt: nowIso3()
|
|
@@ -54620,23 +56165,555 @@ function buildSummaryPrompt(taskPrompt, priorOutputs) {
|
|
|
54620
56165
|
|
|
54621
56166
|
${text || "(no output)"}`).join("\n\n");
|
|
54622
56167
|
return [
|
|
54623
|
-
"
|
|
56168
|
+
"Summarise in 2 plain english paragraphs that are easily understandable for the user and not technical giberish.",
|
|
54624
56169
|
"",
|
|
54625
|
-
|
|
56170
|
+
"Original task:",
|
|
54626
56171
|
"",
|
|
54627
|
-
|
|
54628
|
-
"Produce a concise summary of what was accomplished and any open issues.",
|
|
56172
|
+
taskPrompt,
|
|
54629
56173
|
"",
|
|
54630
56174
|
sections
|
|
54631
56175
|
].join("\n");
|
|
54632
56176
|
}
|
|
54633
56177
|
|
|
54634
|
-
// ../server/src/server/
|
|
56178
|
+
// ../server/src/server/quest/runner-orchestrator.ts
|
|
56179
|
+
import { appendFile, mkdir as mkdir8, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
|
|
56180
|
+
import path26 from "node:path";
|
|
56181
|
+
function nowIso4() {
|
|
56182
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
56183
|
+
}
|
|
56184
|
+
async function resolveAllRoles(record) {
|
|
56185
|
+
const out = [];
|
|
56186
|
+
for (const card of record.cards) {
|
|
56187
|
+
if (!card.role) {
|
|
56188
|
+
out.push({
|
|
56189
|
+
cardIndex: card.index,
|
|
56190
|
+
roleName: `card-${card.index}`,
|
|
56191
|
+
body: null,
|
|
56192
|
+
allowedTools: []
|
|
56193
|
+
});
|
|
56194
|
+
continue;
|
|
56195
|
+
}
|
|
56196
|
+
const resolved = await resolveRole({ roleName: card.role, workspaceRoot: record.cwd });
|
|
56197
|
+
out.push({
|
|
56198
|
+
cardIndex: card.index,
|
|
56199
|
+
roleName: card.role,
|
|
56200
|
+
body: resolved?.body ?? null,
|
|
56201
|
+
allowedTools: resolved?.allowedTools ?? []
|
|
56202
|
+
});
|
|
56203
|
+
}
|
|
56204
|
+
return out;
|
|
56205
|
+
}
|
|
56206
|
+
function buildQueenSystemPrompt(args) {
|
|
56207
|
+
const roleSections = args.roles.map((r) => {
|
|
56208
|
+
const body = r.body ? r.body.trim() : "(no role body found \u2014 this card has no system prompt)";
|
|
56209
|
+
return [`### Card ${r.cardIndex} \xB7 role: \`${r.roleName}\``, "", body].join("\n");
|
|
56210
|
+
}).join("\n\n---\n\n");
|
|
56211
|
+
return [
|
|
56212
|
+
"You are the QUEEN BEE \u2014 the planner of a multi-agent orchestration.",
|
|
56213
|
+
"",
|
|
56214
|
+
"You do NOT do the work yourself. Your one and only job is to read the",
|
|
56215
|
+
"user's task plus the role roster below and emit ONE JSON plan that",
|
|
56216
|
+
"delegates the work across your team.",
|
|
56217
|
+
"",
|
|
56218
|
+
"A plan is an ORDERED list of waves. Each wave is a SET of role dispatches",
|
|
56219
|
+
"that the runner will execute IN PARALLEL. Wave N+1 starts only after",
|
|
56220
|
+
"every dispatch in wave N has finished.",
|
|
56221
|
+
"",
|
|
56222
|
+
"Use parallelism aggressively. Place roles in the SAME wave whenever they",
|
|
56223
|
+
"do not depend on each other's output. Place a role in a LATER wave only",
|
|
56224
|
+
"when it genuinely needs the result of an earlier wave.",
|
|
56225
|
+
"",
|
|
56226
|
+
"Each role on the roster runs AT MOST ONCE \u2014 do not schedule the same",
|
|
56227
|
+
"role twice anywhere in the plan. You may also OMIT roles that are not",
|
|
56228
|
+
"needed for the user's task.",
|
|
56229
|
+
"",
|
|
56230
|
+
"## Your team",
|
|
56231
|
+
"",
|
|
56232
|
+
"Read every role description carefully so you understand what each worker",
|
|
56233
|
+
"can and cannot do, and what inputs they need.",
|
|
56234
|
+
"",
|
|
56235
|
+
roleSections,
|
|
56236
|
+
"",
|
|
56237
|
+
"## Hivemind doc",
|
|
56238
|
+
"",
|
|
56239
|
+
`The hivemind doc lives at: ${args.hivemindPath}`,
|
|
56240
|
+
"",
|
|
56241
|
+
"After every wave the runner appends each worker's final output to that",
|
|
56242
|
+
"doc, and feeds the hivemind contents inline into the prompts of any",
|
|
56243
|
+
"workers in later waves so they can build on prior results. You do not",
|
|
56244
|
+
"need to think about this \u2014 just write self-contained dispatch prompts.",
|
|
56245
|
+
"",
|
|
56246
|
+
"## Response contract \u2014 STRICT",
|
|
56247
|
+
"",
|
|
56248
|
+
"Respond with EXACTLY one fenced JSON code block and nothing else. No",
|
|
56249
|
+
"prose before or after. The JSON must match this shape:",
|
|
56250
|
+
"",
|
|
56251
|
+
"```json",
|
|
56252
|
+
"{",
|
|
56253
|
+
' "plan": [',
|
|
56254
|
+
' { "dispatches": [',
|
|
56255
|
+
' { "role": "<roleName>", "prompt": "<concrete sub-task>" }',
|
|
56256
|
+
" ]}",
|
|
56257
|
+
" ]",
|
|
56258
|
+
"}",
|
|
56259
|
+
"```",
|
|
56260
|
+
"",
|
|
56261
|
+
"Rules:",
|
|
56262
|
+
"- `role` must match one of the role names listed above EXACTLY.",
|
|
56263
|
+
"- A role appears AT MOST ONCE across the entire plan.",
|
|
56264
|
+
"- `prompt` is a precise, self-contained task for the worker. Cite any",
|
|
56265
|
+
" expected upstream inputs by role name (the runner injects the hivemind",
|
|
56266
|
+
" contents automatically).",
|
|
56267
|
+
"- The plan ends when its last wave finishes. There is no `done` action."
|
|
56268
|
+
].join("\n");
|
|
56269
|
+
}
|
|
56270
|
+
function extractFencedJson(text) {
|
|
56271
|
+
const fence = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
|
|
56272
|
+
if (fence?.[1]) return fence[1].trim();
|
|
56273
|
+
const brace = /\{[\s\S]*\}/.exec(text);
|
|
56274
|
+
return brace ? brace[0].trim() : null;
|
|
56275
|
+
}
|
|
56276
|
+
function parseQueenPlan(text, knownRoles) {
|
|
56277
|
+
const block = extractFencedJson(text);
|
|
56278
|
+
if (!block) {
|
|
56279
|
+
return { ok: false, reason: "no JSON block found in queen response" };
|
|
56280
|
+
}
|
|
56281
|
+
let parsed;
|
|
56282
|
+
try {
|
|
56283
|
+
parsed = JSON.parse(block);
|
|
56284
|
+
} catch (err) {
|
|
56285
|
+
return {
|
|
56286
|
+
ok: false,
|
|
56287
|
+
reason: `JSON parse error: ${err instanceof Error ? err.message : String(err)}`
|
|
56288
|
+
};
|
|
56289
|
+
}
|
|
56290
|
+
if (!parsed || typeof parsed !== "object") {
|
|
56291
|
+
return { ok: false, reason: "queen response is not an object" };
|
|
56292
|
+
}
|
|
56293
|
+
const planRaw = parsed.plan;
|
|
56294
|
+
if (!Array.isArray(planRaw) || planRaw.length === 0) {
|
|
56295
|
+
return { ok: false, reason: "plan must be a non-empty array of waves" };
|
|
56296
|
+
}
|
|
56297
|
+
const seenRoles = /* @__PURE__ */ new Set();
|
|
56298
|
+
const waves = [];
|
|
56299
|
+
for (let i = 0; i < planRaw.length; i += 1) {
|
|
56300
|
+
const waveRaw = planRaw[i];
|
|
56301
|
+
if (!waveRaw || typeof waveRaw !== "object") {
|
|
56302
|
+
return { ok: false, reason: `wave ${i + 1} is not an object` };
|
|
56303
|
+
}
|
|
56304
|
+
const dispatchesRaw = waveRaw.dispatches;
|
|
56305
|
+
if (!Array.isArray(dispatchesRaw) || dispatchesRaw.length === 0) {
|
|
56306
|
+
return { ok: false, reason: `wave ${i + 1} has no dispatches` };
|
|
56307
|
+
}
|
|
56308
|
+
const dispatches = [];
|
|
56309
|
+
for (const d of dispatchesRaw) {
|
|
56310
|
+
if (!d || typeof d !== "object" || typeof d.role !== "string" || typeof d.prompt !== "string") {
|
|
56311
|
+
return {
|
|
56312
|
+
ok: false,
|
|
56313
|
+
reason: `wave ${i + 1}: every dispatch needs string \`role\` and string \`prompt\``
|
|
56314
|
+
};
|
|
56315
|
+
}
|
|
56316
|
+
const role = d.role.trim();
|
|
56317
|
+
const prompt = d.prompt;
|
|
56318
|
+
if (!knownRoles.has(role)) {
|
|
56319
|
+
return { ok: false, reason: `wave ${i + 1}: unknown role \`${role}\`` };
|
|
56320
|
+
}
|
|
56321
|
+
if (seenRoles.has(role)) {
|
|
56322
|
+
return {
|
|
56323
|
+
ok: false,
|
|
56324
|
+
reason: `wave ${i + 1}: role \`${role}\` was already scheduled in an earlier wave`
|
|
56325
|
+
};
|
|
56326
|
+
}
|
|
56327
|
+
seenRoles.add(role);
|
|
56328
|
+
dispatches.push({ role, prompt });
|
|
56329
|
+
}
|
|
56330
|
+
waves.push({ dispatches });
|
|
56331
|
+
}
|
|
56332
|
+
return { ok: true, plan: waves };
|
|
56333
|
+
}
|
|
56334
|
+
function resolveFinalTextFromRun2(run) {
|
|
56335
|
+
if (run.finalText.trim()) {
|
|
56336
|
+
return run.finalText;
|
|
56337
|
+
}
|
|
56338
|
+
for (let i = run.timeline.length - 1; i >= 0; i -= 1) {
|
|
56339
|
+
const item = run.timeline[i];
|
|
56340
|
+
if (item && typeof item === "object" && "type" in item && item.type === "assistant_message" && "text" in item && typeof item.text === "string") {
|
|
56341
|
+
const text = item.text;
|
|
56342
|
+
if (text.trim()) {
|
|
56343
|
+
return text;
|
|
56344
|
+
}
|
|
56345
|
+
}
|
|
56346
|
+
}
|
|
56347
|
+
return "";
|
|
56348
|
+
}
|
|
56349
|
+
function resolveHivemindPaths(cwd, questId) {
|
|
56350
|
+
const dir = path26.join(cwd, ".hiveminds", "orchestrator");
|
|
56351
|
+
return {
|
|
56352
|
+
dir,
|
|
56353
|
+
filePath: path26.join(dir, `orchestrator-${questId}.md`)
|
|
56354
|
+
};
|
|
56355
|
+
}
|
|
56356
|
+
async function initializeHivemind(args) {
|
|
56357
|
+
await mkdir8(args.paths.dir, { recursive: true });
|
|
56358
|
+
const roster = args.roles.map((r) => `- card ${r.cardIndex} \xB7 \`${r.roleName}\``).join("\n");
|
|
56359
|
+
const planSummary = args.plan.map(
|
|
56360
|
+
(wave, i) => `**wave ${i + 1}** \u2014 ${wave.dispatches.map((d) => `\`${d.role}\``).join(", ")}`
|
|
56361
|
+
).join("\n");
|
|
56362
|
+
const body = [
|
|
56363
|
+
`# Orchestrator ${args.record.id}`,
|
|
56364
|
+
"",
|
|
56365
|
+
`- createdAt: ${args.record.createdAt}`,
|
|
56366
|
+
`- prompt: ${args.record.prompt}`,
|
|
56367
|
+
"",
|
|
56368
|
+
"## Roster",
|
|
56369
|
+
"",
|
|
56370
|
+
roster,
|
|
56371
|
+
"",
|
|
56372
|
+
"## Plan",
|
|
56373
|
+
"",
|
|
56374
|
+
planSummary,
|
|
56375
|
+
"",
|
|
56376
|
+
"## Waves",
|
|
56377
|
+
""
|
|
56378
|
+
].join("\n");
|
|
56379
|
+
await writeFile7(args.paths.filePath, body, "utf8");
|
|
56380
|
+
}
|
|
56381
|
+
async function appendHivemind(filePath, chunk) {
|
|
56382
|
+
await appendFile(filePath, chunk, "utf8");
|
|
56383
|
+
}
|
|
56384
|
+
async function readHivemind(filePath) {
|
|
56385
|
+
try {
|
|
56386
|
+
return await readFile5(filePath, "utf8");
|
|
56387
|
+
} catch {
|
|
56388
|
+
return "";
|
|
56389
|
+
}
|
|
56390
|
+
}
|
|
56391
|
+
function buildQueenAgentConfig(args) {
|
|
56392
|
+
const modeId = getProviderFullAccessModeId(args.record.defaultProvider) ?? void 0;
|
|
56393
|
+
return {
|
|
56394
|
+
provider: args.record.defaultProvider,
|
|
56395
|
+
cwd: args.record.cwd,
|
|
56396
|
+
model: args.record.defaultModel ?? void 0,
|
|
56397
|
+
modeId,
|
|
56398
|
+
systemPrompt: args.systemPrompt,
|
|
56399
|
+
title: `quest ${args.record.id} queen`,
|
|
56400
|
+
internal: true
|
|
56401
|
+
};
|
|
56402
|
+
}
|
|
56403
|
+
function buildWorkerAgentConfig(args) {
|
|
56404
|
+
const modeId = getProviderFullAccessModeId(args.record.defaultProvider) ?? void 0;
|
|
56405
|
+
return {
|
|
56406
|
+
provider: args.record.defaultProvider,
|
|
56407
|
+
cwd: args.record.cwd,
|
|
56408
|
+
model: args.card.model ?? args.record.defaultModel ?? void 0,
|
|
56409
|
+
modeId,
|
|
56410
|
+
systemPrompt: args.roleBody ?? void 0,
|
|
56411
|
+
allowedTools: args.allowedTools.length > 0 ? args.allowedTools : void 0,
|
|
56412
|
+
title: `quest ${args.record.id} card ${args.card.index} ${args.card.role ?? ""}`.trim(),
|
|
56413
|
+
internal: true
|
|
56414
|
+
};
|
|
56415
|
+
}
|
|
56416
|
+
function buildWorkerPrompt(args) {
|
|
56417
|
+
if (args.isFirstWave) {
|
|
56418
|
+
return args.dispatchPrompt;
|
|
56419
|
+
}
|
|
56420
|
+
return [
|
|
56421
|
+
args.dispatchPrompt,
|
|
56422
|
+
"",
|
|
56423
|
+
"---",
|
|
56424
|
+
"",
|
|
56425
|
+
"## Hivemind context (outputs from earlier waves)",
|
|
56426
|
+
"",
|
|
56427
|
+
args.hivemindMarkdown
|
|
56428
|
+
].join("\n");
|
|
56429
|
+
}
|
|
56430
|
+
var OrchestratorRunner = class {
|
|
56431
|
+
constructor(options) {
|
|
56432
|
+
this.logger = options.logger.child({ module: "quest-runner-orchestrator" });
|
|
56433
|
+
}
|
|
56434
|
+
async execute(args) {
|
|
56435
|
+
const { questId, signal, service } = args;
|
|
56436
|
+
const initial = service.getRecord(questId);
|
|
56437
|
+
if (!initial) {
|
|
56438
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
56439
|
+
}
|
|
56440
|
+
if (initial.kind !== "orchestrate") {
|
|
56441
|
+
throw new Error(`OrchestratorRunner cannot execute quest of kind "${initial.kind}"`);
|
|
56442
|
+
}
|
|
56443
|
+
if (initial.termination.type !== "orchestrate") {
|
|
56444
|
+
throw new Error('OrchestratorRunner expects termination.type === "orchestrate"');
|
|
56445
|
+
}
|
|
56446
|
+
if (initial.cards.length === 0) {
|
|
56447
|
+
await service.markStatus(questId, "failed");
|
|
56448
|
+
throw new Error("Orchestrate quest requires at least one role card");
|
|
56449
|
+
}
|
|
56450
|
+
const hivemindPaths = resolveHivemindPaths(initial.cwd, initial.id);
|
|
56451
|
+
const resolvedRoles = await resolveAllRoles(initial);
|
|
56452
|
+
const knownRoles = new Set(resolvedRoles.map((r) => r.roleName));
|
|
56453
|
+
const cardIndexByRole = /* @__PURE__ */ new Map();
|
|
56454
|
+
for (const r of resolvedRoles) {
|
|
56455
|
+
if (!cardIndexByRole.has(r.roleName)) {
|
|
56456
|
+
cardIndexByRole.set(r.roleName, r.cardIndex);
|
|
56457
|
+
}
|
|
56458
|
+
}
|
|
56459
|
+
await service.setHivemindPath(questId, hivemindPaths.filePath);
|
|
56460
|
+
const queenSystemPrompt = buildQueenSystemPrompt({
|
|
56461
|
+
roles: resolvedRoles,
|
|
56462
|
+
hivemindPath: hivemindPaths.filePath
|
|
56463
|
+
});
|
|
56464
|
+
const queenStartedAt = nowIso4();
|
|
56465
|
+
const queenAgent = await service.agentManager.createAgent(
|
|
56466
|
+
buildQueenAgentConfig({ record: initial, systemPrompt: queenSystemPrompt })
|
|
56467
|
+
);
|
|
56468
|
+
const queenCard = QuestCardSchema.parse({
|
|
56469
|
+
index: 0,
|
|
56470
|
+
role: "queen",
|
|
56471
|
+
model: initial.defaultModel,
|
|
56472
|
+
prompt: null,
|
|
56473
|
+
wiring: { inputs: "user-prompt", outputs: "next-card" },
|
|
56474
|
+
status: "running",
|
|
56475
|
+
agentId: queenAgent.id,
|
|
56476
|
+
startedAt: queenStartedAt,
|
|
56477
|
+
completedAt: null,
|
|
56478
|
+
verify: null
|
|
56479
|
+
});
|
|
56480
|
+
await service.setQueenCard(questId, queenCard);
|
|
56481
|
+
const onAbort = () => {
|
|
56482
|
+
void service.agentManager.cancelAgentRun(queenAgent.id).catch(() => {
|
|
56483
|
+
});
|
|
56484
|
+
};
|
|
56485
|
+
signal.addEventListener("abort", onAbort);
|
|
56486
|
+
let plan;
|
|
56487
|
+
try {
|
|
56488
|
+
const queenRun = await service.agentManager.runAgent(queenAgent.id, initial.prompt);
|
|
56489
|
+
if (queenRun.canceled || signal.aborted) {
|
|
56490
|
+
await service.updateQueenCard(questId, {
|
|
56491
|
+
status: "stopped",
|
|
56492
|
+
completedAt: nowIso4()
|
|
56493
|
+
});
|
|
56494
|
+
await this.markRemainingCardsStopped(questId, service);
|
|
56495
|
+
await service.markStatus(questId, "stopped");
|
|
56496
|
+
return;
|
|
56497
|
+
}
|
|
56498
|
+
const queenText = resolveFinalTextFromRun2(queenRun);
|
|
56499
|
+
const result = parseQueenPlan(queenText, knownRoles);
|
|
56500
|
+
if (!result.ok) {
|
|
56501
|
+
await mkdir8(hivemindPaths.dir, { recursive: true });
|
|
56502
|
+
await writeFile7(
|
|
56503
|
+
hivemindPaths.filePath,
|
|
56504
|
+
[
|
|
56505
|
+
`# Orchestrator ${initial.id}`,
|
|
56506
|
+
"",
|
|
56507
|
+
`- createdAt: ${initial.createdAt}`,
|
|
56508
|
+
`- prompt: ${initial.prompt}`,
|
|
56509
|
+
"",
|
|
56510
|
+
"## Plan parse failed",
|
|
56511
|
+
"",
|
|
56512
|
+
`Reason: ${result.reason}`,
|
|
56513
|
+
"",
|
|
56514
|
+
"### Queen response",
|
|
56515
|
+
"",
|
|
56516
|
+
"```",
|
|
56517
|
+
queenText.slice(0, 4e3),
|
|
56518
|
+
"```",
|
|
56519
|
+
""
|
|
56520
|
+
].join("\n"),
|
|
56521
|
+
"utf8"
|
|
56522
|
+
);
|
|
56523
|
+
await service.updateQueenCard(questId, {
|
|
56524
|
+
status: "failed",
|
|
56525
|
+
completedAt: nowIso4()
|
|
56526
|
+
});
|
|
56527
|
+
await this.markRemainingCardsStopped(questId, service);
|
|
56528
|
+
await service.markStatus(questId, "failed");
|
|
56529
|
+
return;
|
|
56530
|
+
}
|
|
56531
|
+
plan = result.plan;
|
|
56532
|
+
} catch (err) {
|
|
56533
|
+
this.logger.error({ err, questId }, "orchestrator: queen errored");
|
|
56534
|
+
await service.updateQueenCard(questId, {
|
|
56535
|
+
status: "failed",
|
|
56536
|
+
completedAt: nowIso4()
|
|
56537
|
+
});
|
|
56538
|
+
await this.markRemainingCardsStopped(questId, service);
|
|
56539
|
+
await service.markStatus(questId, "failed");
|
|
56540
|
+
return;
|
|
56541
|
+
} finally {
|
|
56542
|
+
signal.removeEventListener("abort", onAbort);
|
|
56543
|
+
}
|
|
56544
|
+
await service.updateQueenCard(questId, {
|
|
56545
|
+
status: "succeeded",
|
|
56546
|
+
completedAt: nowIso4()
|
|
56547
|
+
});
|
|
56548
|
+
await initializeHivemind({
|
|
56549
|
+
paths: hivemindPaths,
|
|
56550
|
+
record: initial,
|
|
56551
|
+
roles: resolvedRoles,
|
|
56552
|
+
plan
|
|
56553
|
+
});
|
|
56554
|
+
const scheduledRoles = /* @__PURE__ */ new Set();
|
|
56555
|
+
for (const wave of plan) {
|
|
56556
|
+
for (const d of wave.dispatches) scheduledRoles.add(d.role);
|
|
56557
|
+
}
|
|
56558
|
+
for (const card of initial.cards) {
|
|
56559
|
+
if (card.role && !scheduledRoles.has(card.role) && card.status === "queued") {
|
|
56560
|
+
await service.updateCard(questId, card.index, {
|
|
56561
|
+
status: "stopped",
|
|
56562
|
+
completedAt: nowIso4()
|
|
56563
|
+
});
|
|
56564
|
+
}
|
|
56565
|
+
}
|
|
56566
|
+
let aborted = false;
|
|
56567
|
+
let anyWaveFailed = false;
|
|
56568
|
+
for (let i = 0; i < plan.length; i += 1) {
|
|
56569
|
+
const wave = plan[i];
|
|
56570
|
+
const waveNumber = i + 1;
|
|
56571
|
+
const current = service.getRecord(questId);
|
|
56572
|
+
if (!current || current.status !== "running" || signal.aborted) {
|
|
56573
|
+
aborted = true;
|
|
56574
|
+
break;
|
|
56575
|
+
}
|
|
56576
|
+
const waveHeader = [
|
|
56577
|
+
`
|
|
56578
|
+
### wave ${waveNumber} \u2014 dispatch`,
|
|
56579
|
+
"",
|
|
56580
|
+
...wave.dispatches.map(
|
|
56581
|
+
(d) => `- \`${d.role}\` \u2190 ${d.prompt.replace(/\s+/g, " ").slice(0, 200)}`
|
|
56582
|
+
),
|
|
56583
|
+
""
|
|
56584
|
+
].join("\n");
|
|
56585
|
+
await appendHivemind(hivemindPaths.filePath, waveHeader);
|
|
56586
|
+
const hivemindMarkdown = await readHivemind(hivemindPaths.filePath);
|
|
56587
|
+
const outcomes = await Promise.all(
|
|
56588
|
+
wave.dispatches.map((dispatch) => {
|
|
56589
|
+
const cardIndex = cardIndexByRole.get(dispatch.role);
|
|
56590
|
+
const role = resolvedRoles.find((r) => r.cardIndex === cardIndex);
|
|
56591
|
+
return this.runWorker({
|
|
56592
|
+
record: initial,
|
|
56593
|
+
cardIndex,
|
|
56594
|
+
workerPrompt: buildWorkerPrompt({
|
|
56595
|
+
dispatchPrompt: dispatch.prompt,
|
|
56596
|
+
hivemindMarkdown,
|
|
56597
|
+
isFirstWave: i === 0
|
|
56598
|
+
}),
|
|
56599
|
+
roleBody: role?.body ?? null,
|
|
56600
|
+
allowedTools: role?.allowedTools ?? [],
|
|
56601
|
+
service,
|
|
56602
|
+
signal
|
|
56603
|
+
});
|
|
56604
|
+
})
|
|
56605
|
+
);
|
|
56606
|
+
for (const outcome of outcomes) {
|
|
56607
|
+
const heading = `
|
|
56608
|
+
#### card ${outcome.card.index} \xB7 \`${outcome.card.role ?? "(no role)"}\` \u2014 ${outcome.status}
|
|
56609
|
+
|
|
56610
|
+
`;
|
|
56611
|
+
const bodyText = outcome.status === "succeeded" ? outcome.finalText.trim() || "_(empty output)_" : `_(${outcome.errorMessage ?? outcome.status})_`;
|
|
56612
|
+
await appendHivemind(hivemindPaths.filePath, heading + bodyText + "\n");
|
|
56613
|
+
}
|
|
56614
|
+
if (outcomes.some((o) => o.status === "stopped") || signal.aborted) {
|
|
56615
|
+
aborted = true;
|
|
56616
|
+
break;
|
|
56617
|
+
}
|
|
56618
|
+
if (outcomes.some((o) => o.status === "failed")) {
|
|
56619
|
+
anyWaveFailed = true;
|
|
56620
|
+
}
|
|
56621
|
+
}
|
|
56622
|
+
if (aborted) {
|
|
56623
|
+
await this.markRemainingCardsStopped(questId, service);
|
|
56624
|
+
await service.markStatus(questId, "stopped");
|
|
56625
|
+
return;
|
|
56626
|
+
}
|
|
56627
|
+
await service.markStatus(questId, anyWaveFailed ? "failed" : "succeeded");
|
|
56628
|
+
}
|
|
56629
|
+
async markRemainingCardsStopped(questId, service) {
|
|
56630
|
+
const current = service.getRecord(questId);
|
|
56631
|
+
if (!current) return;
|
|
56632
|
+
for (const card of current.cards) {
|
|
56633
|
+
if (card.status === "queued" || card.status === "running") {
|
|
56634
|
+
await service.updateCard(questId, card.index, {
|
|
56635
|
+
status: "stopped",
|
|
56636
|
+
completedAt: nowIso4()
|
|
56637
|
+
});
|
|
56638
|
+
}
|
|
56639
|
+
}
|
|
56640
|
+
}
|
|
56641
|
+
async runWorker(args) {
|
|
56642
|
+
const { record, cardIndex, workerPrompt, roleBody, allowedTools, service, signal } = args;
|
|
56643
|
+
const fresh = service.getRecord(record.id);
|
|
56644
|
+
const card = fresh?.cards.find((c) => c.index === cardIndex) ?? record.cards.find((c) => c.index === cardIndex);
|
|
56645
|
+
if (!card) {
|
|
56646
|
+
return {
|
|
56647
|
+
card: record.cards[0],
|
|
56648
|
+
status: "failed",
|
|
56649
|
+
finalText: "",
|
|
56650
|
+
errorMessage: `card ${cardIndex} not found`
|
|
56651
|
+
};
|
|
56652
|
+
}
|
|
56653
|
+
const startedAt = nowIso4();
|
|
56654
|
+
let agent;
|
|
56655
|
+
try {
|
|
56656
|
+
agent = await service.agentManager.createAgent(
|
|
56657
|
+
buildWorkerAgentConfig({ record, card, roleBody, allowedTools })
|
|
56658
|
+
);
|
|
56659
|
+
} catch (err) {
|
|
56660
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56661
|
+
this.logger.error({ err, questId: record.id, cardIndex }, "orchestrator: spawn failed");
|
|
56662
|
+
await service.updateCard(record.id, cardIndex, {
|
|
56663
|
+
status: "failed",
|
|
56664
|
+
completedAt: nowIso4(),
|
|
56665
|
+
prompt: workerPrompt
|
|
56666
|
+
});
|
|
56667
|
+
return { card, status: "failed", finalText: "", errorMessage: msg };
|
|
56668
|
+
}
|
|
56669
|
+
await service.updateCard(record.id, cardIndex, {
|
|
56670
|
+
status: "running",
|
|
56671
|
+
agentId: agent.id,
|
|
56672
|
+
startedAt,
|
|
56673
|
+
prompt: workerPrompt
|
|
56674
|
+
});
|
|
56675
|
+
const onAbort = () => {
|
|
56676
|
+
void service.agentManager.cancelAgentRun(agent.id).catch(() => {
|
|
56677
|
+
});
|
|
56678
|
+
};
|
|
56679
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
56680
|
+
try {
|
|
56681
|
+
const run = await service.agentManager.runAgent(agent.id, workerPrompt);
|
|
56682
|
+
const finalText = resolveFinalTextFromRun2(run);
|
|
56683
|
+
if (run.canceled) {
|
|
56684
|
+
await service.updateCard(record.id, cardIndex, {
|
|
56685
|
+
status: "stopped",
|
|
56686
|
+
completedAt: nowIso4()
|
|
56687
|
+
});
|
|
56688
|
+
return { card, status: "stopped", finalText, errorMessage: null };
|
|
56689
|
+
}
|
|
56690
|
+
await service.updateCard(record.id, cardIndex, {
|
|
56691
|
+
status: "succeeded",
|
|
56692
|
+
completedAt: nowIso4()
|
|
56693
|
+
});
|
|
56694
|
+
return { card, status: "succeeded", finalText, errorMessage: null };
|
|
56695
|
+
} catch (err) {
|
|
56696
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56697
|
+
this.logger.error({ err, questId: record.id, cardIndex }, "orchestrator: worker failed");
|
|
56698
|
+
await service.updateCard(record.id, cardIndex, {
|
|
56699
|
+
status: "failed",
|
|
56700
|
+
completedAt: nowIso4()
|
|
56701
|
+
});
|
|
56702
|
+
return { card, status: "failed", finalText: "", errorMessage: msg };
|
|
56703
|
+
} finally {
|
|
56704
|
+
signal.removeEventListener("abort", onAbort);
|
|
56705
|
+
}
|
|
56706
|
+
}
|
|
56707
|
+
};
|
|
56708
|
+
|
|
56709
|
+
// ../server/src/server/quest/runner-ralph.ts
|
|
54635
56710
|
import { promisify as promisify4 } from "node:util";
|
|
54636
56711
|
import { execFile as execFile3 } from "node:child_process";
|
|
56712
|
+
import { appendFile as appendFile2, mkdir as mkdir9, writeFile as writeFile8 } from "node:fs/promises";
|
|
56713
|
+
import path27 from "node:path";
|
|
54637
56714
|
var execFileAsync3 = promisify4(execFile3);
|
|
54638
56715
|
var MAX_VERIFY_OUTPUT_BYTES2 = 64 * 1024;
|
|
54639
|
-
function
|
|
56716
|
+
function nowIso5() {
|
|
54640
56717
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
54641
56718
|
}
|
|
54642
56719
|
function platformShell2() {
|
|
@@ -54672,12 +56749,82 @@ function buildWorkerConfig(record, iteration) {
|
|
|
54672
56749
|
cwd: record.cwd,
|
|
54673
56750
|
model: record.defaultModel ?? void 0,
|
|
54674
56751
|
modeId,
|
|
54675
|
-
title: `
|
|
56752
|
+
title: `quest ${record.id} ralph #${iteration}`,
|
|
54676
56753
|
internal: true
|
|
54677
56754
|
};
|
|
54678
56755
|
}
|
|
56756
|
+
function buildRalphIterationPrompt(args) {
|
|
56757
|
+
const checks = args.verifyChecks.length > 0 ? args.verifyChecks.map((c) => `- ${c}`).join("\n") : "- (none)";
|
|
56758
|
+
return [
|
|
56759
|
+
args.basePrompt,
|
|
56760
|
+
"",
|
|
56761
|
+
"Ralph loop completion contract (strict):",
|
|
56762
|
+
`- You may only declare completion when all verify checks pass:`,
|
|
56763
|
+
checks,
|
|
56764
|
+
`- When complete, output this exact token verbatim: ${args.completionPromise}`,
|
|
56765
|
+
"- If not complete yet, continue making changes and do not output the token."
|
|
56766
|
+
].join("\n");
|
|
56767
|
+
}
|
|
56768
|
+
function resolveFinalTextFromRun3(run) {
|
|
56769
|
+
if (run.finalText.trim()) {
|
|
56770
|
+
return run.finalText;
|
|
56771
|
+
}
|
|
56772
|
+
for (let i = run.timeline.length - 1; i >= 0; i -= 1) {
|
|
56773
|
+
const item = run.timeline[i];
|
|
56774
|
+
if (item && typeof item === "object" && "type" in item && item.type === "assistant_message" && "text" in item && typeof item.text === "string") {
|
|
56775
|
+
const text = item.text;
|
|
56776
|
+
if (text.trim()) {
|
|
56777
|
+
return text;
|
|
56778
|
+
}
|
|
56779
|
+
}
|
|
56780
|
+
}
|
|
56781
|
+
return "";
|
|
56782
|
+
}
|
|
56783
|
+
function runContainsPromise(run, completionPromise) {
|
|
56784
|
+
const finalText = resolveFinalTextFromRun3(run);
|
|
56785
|
+
return finalText.includes(completionPromise);
|
|
56786
|
+
}
|
|
56787
|
+
function resolveRalphStatePaths(cwd) {
|
|
56788
|
+
const dir = path27.join(cwd, ".hiveminds", "ralph loop");
|
|
56789
|
+
return {
|
|
56790
|
+
dir,
|
|
56791
|
+
logPath: ""
|
|
56792
|
+
};
|
|
56793
|
+
}
|
|
56794
|
+
async function initializeRalphState(args) {
|
|
56795
|
+
const base = resolveRalphStatePaths(args.record.cwd);
|
|
56796
|
+
const paths = {
|
|
56797
|
+
dir: base.dir,
|
|
56798
|
+
logPath: path27.join(base.dir, `ralphloop_${args.record.id}.md`)
|
|
56799
|
+
};
|
|
56800
|
+
await mkdir9(paths.dir, { recursive: true });
|
|
56801
|
+
await writeFile8(
|
|
56802
|
+
paths.logPath,
|
|
56803
|
+
[
|
|
56804
|
+
`# Ralph Loop ${args.record.id}`,
|
|
56805
|
+
"",
|
|
56806
|
+
`- createdAt: ${args.record.createdAt}`,
|
|
56807
|
+
`- prompt: ${args.record.prompt}`,
|
|
56808
|
+
`- completionPromise: ${args.completionPromise}`,
|
|
56809
|
+
`- maxIterations: ${args.maxIterations}`,
|
|
56810
|
+
"",
|
|
56811
|
+
"## Verify Checks",
|
|
56812
|
+
...args.verifyChecks.length > 0 ? args.verifyChecks.map((check) => `- ${check}`) : ["- (none)"],
|
|
56813
|
+
"",
|
|
56814
|
+
"## Progress"
|
|
56815
|
+
].join("\n") + "\n",
|
|
56816
|
+
"utf8"
|
|
56817
|
+
);
|
|
56818
|
+
await appendFile2(paths.logPath, `- [${nowIso5()}] started
|
|
56819
|
+
`, "utf8");
|
|
56820
|
+
return paths;
|
|
56821
|
+
}
|
|
56822
|
+
async function appendRalphProgress(paths, message) {
|
|
56823
|
+
await appendFile2(paths.logPath, `- [${nowIso5()}] ${message}
|
|
56824
|
+
`, "utf8");
|
|
56825
|
+
}
|
|
54679
56826
|
function newCard(index) {
|
|
54680
|
-
return
|
|
56827
|
+
return QuestCardSchema.parse({
|
|
54681
56828
|
index,
|
|
54682
56829
|
role: null,
|
|
54683
56830
|
model: null,
|
|
@@ -54698,46 +56845,51 @@ function newCard(index) {
|
|
|
54698
56845
|
}
|
|
54699
56846
|
var RalphRunner = class {
|
|
54700
56847
|
constructor(options) {
|
|
54701
|
-
this.logger = options.logger.child({ module: "
|
|
56848
|
+
this.logger = options.logger.child({ module: "quest-runner-ralph" });
|
|
54702
56849
|
}
|
|
54703
56850
|
async execute(args) {
|
|
54704
|
-
const {
|
|
54705
|
-
const initial = service.getRecord(
|
|
56851
|
+
const { questId, signal, service } = args;
|
|
56852
|
+
const initial = service.getRecord(questId);
|
|
54706
56853
|
if (!initial) {
|
|
54707
|
-
throw new Error(`
|
|
56854
|
+
throw new Error(`Quest "${questId}" not found`);
|
|
54708
56855
|
}
|
|
54709
56856
|
if (initial.kind !== "ralph") {
|
|
54710
|
-
throw new Error(`RalphRunner cannot execute
|
|
56857
|
+
throw new Error(`RalphRunner cannot execute quest of kind "${initial.kind}"`);
|
|
54711
56858
|
}
|
|
54712
56859
|
if (initial.termination.type !== "verify") {
|
|
54713
56860
|
throw new Error(`RalphRunner expects termination.type === "verify"`);
|
|
54714
56861
|
}
|
|
54715
|
-
const { verifyPrompt, verifyChecks, maxIterations } = initial.termination;
|
|
56862
|
+
const { verifyPrompt, verifyChecks, completionPromise, maxIterations } = initial.termination;
|
|
56863
|
+
const statePaths = await initializeRalphState({
|
|
56864
|
+
record: initial,
|
|
56865
|
+
verifyChecks,
|
|
56866
|
+
completionPromise,
|
|
56867
|
+
maxIterations
|
|
56868
|
+
});
|
|
54716
56869
|
if (verifyPrompt) {
|
|
54717
56870
|
this.logger.warn(
|
|
54718
|
-
{
|
|
56871
|
+
{ questId },
|
|
54719
56872
|
"ralph-runner: verifyPrompt is set but verifier-agent support is not yet implemented in v1 \u2014 relying on verifyChecks only"
|
|
54720
56873
|
);
|
|
54721
56874
|
}
|
|
54722
56875
|
if (verifyChecks.length === 0) {
|
|
54723
|
-
await service.markStatus(
|
|
54724
|
-
throw new Error(
|
|
54725
|
-
"Ralph workflow requires at least one verifyCheck (verifier-agent path is v2)"
|
|
54726
|
-
);
|
|
56876
|
+
await service.markStatus(questId, "failed");
|
|
56877
|
+
throw new Error("Ralph quest requires at least one verifyCheck (verifier-agent path is v2)");
|
|
54727
56878
|
}
|
|
54728
56879
|
for (let iteration = 1; iteration <= maxIterations; iteration += 1) {
|
|
54729
|
-
const current = service.getRecord(
|
|
56880
|
+
const current = service.getRecord(questId);
|
|
54730
56881
|
if (!current || current.status !== "running" || signal.aborted) {
|
|
54731
|
-
await service.markStatus(
|
|
56882
|
+
await service.markStatus(questId, "stopped");
|
|
54732
56883
|
return;
|
|
54733
56884
|
}
|
|
54734
56885
|
const card = newCard(iteration);
|
|
54735
|
-
let recordWithCard = await service.appendCard(
|
|
54736
|
-
|
|
56886
|
+
let recordWithCard = await service.appendCard(questId, card);
|
|
56887
|
+
await appendRalphProgress(statePaths, `iteration=${iteration} status=started`);
|
|
56888
|
+
const startedAt = nowIso5();
|
|
54737
56889
|
const agent = await service.agentManager.createAgent(
|
|
54738
56890
|
buildWorkerConfig(recordWithCard, iteration)
|
|
54739
56891
|
);
|
|
54740
|
-
await service.updateCard(
|
|
56892
|
+
await service.updateCard(questId, iteration, {
|
|
54741
56893
|
status: "running",
|
|
54742
56894
|
agentId: agent.id,
|
|
54743
56895
|
startedAt
|
|
@@ -54748,25 +56900,39 @@ var RalphRunner = class {
|
|
|
54748
56900
|
};
|
|
54749
56901
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
54750
56902
|
let workerOk = false;
|
|
56903
|
+
let workerRun = null;
|
|
54751
56904
|
try {
|
|
54752
|
-
const run = await service.agentManager.runAgent(
|
|
56905
|
+
const run = await service.agentManager.runAgent(
|
|
56906
|
+
agent.id,
|
|
56907
|
+
buildRalphIterationPrompt({
|
|
56908
|
+
basePrompt: recordWithCard.prompt,
|
|
56909
|
+
completionPromise,
|
|
56910
|
+
verifyChecks
|
|
56911
|
+
})
|
|
56912
|
+
);
|
|
54753
56913
|
if (run.canceled) {
|
|
54754
|
-
await
|
|
56914
|
+
await appendRalphProgress(
|
|
56915
|
+
statePaths,
|
|
56916
|
+
`iteration=${iteration} status=stopped reason=canceled`
|
|
56917
|
+
);
|
|
56918
|
+
await service.updateCard(questId, iteration, {
|
|
54755
56919
|
status: "stopped",
|
|
54756
|
-
completedAt:
|
|
56920
|
+
completedAt: nowIso5()
|
|
54757
56921
|
});
|
|
54758
|
-
await service.markStatus(
|
|
56922
|
+
await service.markStatus(questId, "stopped");
|
|
54759
56923
|
return;
|
|
54760
56924
|
}
|
|
56925
|
+
workerRun = run;
|
|
54761
56926
|
workerOk = true;
|
|
54762
56927
|
} catch (err) {
|
|
54763
|
-
|
|
54764
|
-
|
|
54765
|
-
|
|
56928
|
+
await appendRalphProgress(
|
|
56929
|
+
statePaths,
|
|
56930
|
+
`iteration=${iteration} status=failed reason=worker_error`
|
|
54766
56931
|
);
|
|
54767
|
-
|
|
56932
|
+
this.logger.error({ err, questId, iteration }, "ralph-runner: worker errored");
|
|
56933
|
+
await service.updateCard(questId, iteration, {
|
|
54768
56934
|
status: "failed",
|
|
54769
|
-
completedAt:
|
|
56935
|
+
completedAt: nowIso5()
|
|
54770
56936
|
});
|
|
54771
56937
|
} finally {
|
|
54772
56938
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -54778,12 +56944,30 @@ var RalphRunner = class {
|
|
|
54778
56944
|
if (!workerOk) {
|
|
54779
56945
|
continue;
|
|
54780
56946
|
}
|
|
54781
|
-
|
|
56947
|
+
if (!workerRun || !runContainsPromise(workerRun, completionPromise)) {
|
|
56948
|
+
await appendRalphProgress(
|
|
56949
|
+
statePaths,
|
|
56950
|
+
`iteration=${iteration} status=failed reason=missing_completion_promise`
|
|
56951
|
+
);
|
|
56952
|
+
await service.updateCard(questId, iteration, {
|
|
56953
|
+
status: "failed",
|
|
56954
|
+
completedAt: nowIso5(),
|
|
56955
|
+
verify: {
|
|
56956
|
+
verifierAgentId: null,
|
|
56957
|
+
passed: false,
|
|
56958
|
+
reason: `missing completion promise: ${completionPromise}`,
|
|
56959
|
+
startedAt: null,
|
|
56960
|
+
completedAt: nowIso5()
|
|
56961
|
+
}
|
|
56962
|
+
});
|
|
56963
|
+
continue;
|
|
56964
|
+
}
|
|
56965
|
+
const verifyStartedAt = nowIso5();
|
|
54782
56966
|
let allPassed = true;
|
|
54783
56967
|
const reasons = [];
|
|
54784
56968
|
for (const command of verifyChecks) {
|
|
54785
56969
|
if (signal.aborted) {
|
|
54786
|
-
await service.markStatus(
|
|
56970
|
+
await service.markStatus(questId, "stopped");
|
|
54787
56971
|
return;
|
|
54788
56972
|
}
|
|
54789
56973
|
const outcome = await runVerifyCheck2({
|
|
@@ -54796,10 +56980,10 @@ var RalphRunner = class {
|
|
|
54796
56980
|
break;
|
|
54797
56981
|
}
|
|
54798
56982
|
}
|
|
54799
|
-
const verifyCompletedAt =
|
|
54800
|
-
await service.updateCard(
|
|
56983
|
+
const verifyCompletedAt = nowIso5();
|
|
56984
|
+
await service.updateCard(questId, iteration, {
|
|
54801
56985
|
status: allPassed ? "succeeded" : "failed",
|
|
54802
|
-
completedAt:
|
|
56986
|
+
completedAt: nowIso5(),
|
|
54803
56987
|
verify: {
|
|
54804
56988
|
verifierAgentId: null,
|
|
54805
56989
|
passed: allPassed,
|
|
@@ -54809,12 +56993,18 @@ var RalphRunner = class {
|
|
|
54809
56993
|
}
|
|
54810
56994
|
});
|
|
54811
56995
|
if (allPassed) {
|
|
54812
|
-
await
|
|
56996
|
+
await appendRalphProgress(statePaths, `iteration=${iteration} status=succeeded`);
|
|
56997
|
+
await service.markStatus(questId, "succeeded");
|
|
54813
56998
|
return;
|
|
54814
56999
|
}
|
|
54815
|
-
|
|
57000
|
+
await appendRalphProgress(
|
|
57001
|
+
statePaths,
|
|
57002
|
+
`iteration=${iteration} status=failed reason=verify_checks`
|
|
57003
|
+
);
|
|
57004
|
+
recordWithCard = service.getRecord(questId) ?? recordWithCard;
|
|
54816
57005
|
}
|
|
54817
|
-
await
|
|
57006
|
+
await appendRalphProgress(statePaths, "quest status=failed reason=max_iterations_exhausted");
|
|
57007
|
+
await service.markStatus(questId, "failed");
|
|
54818
57008
|
}
|
|
54819
57009
|
};
|
|
54820
57010
|
|
|
@@ -54840,8 +57030,8 @@ function deepMerge(current, patch) {
|
|
|
54840
57030
|
}
|
|
54841
57031
|
return next;
|
|
54842
57032
|
}
|
|
54843
|
-
function getValueAtPath(config,
|
|
54844
|
-
return
|
|
57033
|
+
function getValueAtPath(config, path33) {
|
|
57034
|
+
return path33.split(".").reduce((value, segment) => isRecord3(value) ? value[segment] : void 0, config);
|
|
54845
57035
|
}
|
|
54846
57036
|
function isEqualValue(a, b) {
|
|
54847
57037
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -54860,20 +57050,20 @@ var DaemonConfigStore = class {
|
|
|
54860
57050
|
patch(partial) {
|
|
54861
57051
|
const parsedPatch = MutableDaemonConfigPatchSchema.parse(partial);
|
|
54862
57052
|
const next = MutableDaemonConfigSchema.parse(deepMerge(this.current, parsedPatch));
|
|
54863
|
-
const changedFieldPaths = Array.from(this.fieldChangeHandlers.keys()).filter((
|
|
54864
|
-
return !isEqualValue(getValueAtPath(this.current,
|
|
57053
|
+
const changedFieldPaths = Array.from(this.fieldChangeHandlers.keys()).filter((path33) => {
|
|
57054
|
+
return !isEqualValue(getValueAtPath(this.current, path33), getValueAtPath(next, path33));
|
|
54865
57055
|
});
|
|
54866
57056
|
if (changedFieldPaths.length === 0 && isEqualValue(this.current, next)) {
|
|
54867
57057
|
return this.current;
|
|
54868
57058
|
}
|
|
54869
57059
|
this.persistConfig(next);
|
|
54870
57060
|
this.current = next;
|
|
54871
|
-
for (const
|
|
54872
|
-
const handlers = this.fieldChangeHandlers.get(
|
|
57061
|
+
for (const path33 of changedFieldPaths) {
|
|
57062
|
+
const handlers = this.fieldChangeHandlers.get(path33);
|
|
54873
57063
|
if (!handlers) {
|
|
54874
57064
|
continue;
|
|
54875
57065
|
}
|
|
54876
|
-
const value = getValueAtPath(next,
|
|
57066
|
+
const value = getValueAtPath(next, path33);
|
|
54877
57067
|
for (const handler of handlers) {
|
|
54878
57068
|
handler(value);
|
|
54879
57069
|
}
|
|
@@ -54883,18 +57073,18 @@ var DaemonConfigStore = class {
|
|
|
54883
57073
|
}
|
|
54884
57074
|
return next;
|
|
54885
57075
|
}
|
|
54886
|
-
onFieldChange(
|
|
54887
|
-
const handlers = this.fieldChangeHandlers.get(
|
|
57076
|
+
onFieldChange(path33, handler) {
|
|
57077
|
+
const handlers = this.fieldChangeHandlers.get(path33) ?? /* @__PURE__ */ new Set();
|
|
54888
57078
|
handlers.add(handler);
|
|
54889
|
-
this.fieldChangeHandlers.set(
|
|
57079
|
+
this.fieldChangeHandlers.set(path33, handlers);
|
|
54890
57080
|
return () => {
|
|
54891
|
-
const currentHandlers = this.fieldChangeHandlers.get(
|
|
57081
|
+
const currentHandlers = this.fieldChangeHandlers.get(path33);
|
|
54892
57082
|
if (!currentHandlers) {
|
|
54893
57083
|
return;
|
|
54894
57084
|
}
|
|
54895
57085
|
currentHandlers.delete(handler);
|
|
54896
57086
|
if (currentHandlers.size === 0) {
|
|
54897
|
-
this.fieldChangeHandlers.delete(
|
|
57087
|
+
this.fieldChangeHandlers.delete(path33);
|
|
54898
57088
|
}
|
|
54899
57089
|
};
|
|
54900
57090
|
}
|
|
@@ -54934,7 +57124,7 @@ function mergeMutableConfigIntoPersistedConfig(params) {
|
|
|
54934
57124
|
|
|
54935
57125
|
// ../server/src/server/workspace-git-service.ts
|
|
54936
57126
|
import { watch } from "node:fs";
|
|
54937
|
-
import { readFile as
|
|
57127
|
+
import { readFile as readFile6, readdir as readdir5 } from "node:fs/promises";
|
|
54938
57128
|
import { join as join17, resolve as resolve12 } from "node:path";
|
|
54939
57129
|
var WORKSPACE_GIT_WATCH_DEBOUNCE_MS = 500;
|
|
54940
57130
|
var BACKGROUND_GIT_FETCH_INTERVAL_MS = 18e4;
|
|
@@ -55164,7 +57354,7 @@ var WorkspaceGitServiceImpl = class {
|
|
|
55164
57354
|
}
|
|
55165
57355
|
async resolveWorkspaceGitRefsRoot(gitDir) {
|
|
55166
57356
|
try {
|
|
55167
|
-
const commonDir = (await
|
|
57357
|
+
const commonDir = (await readFile6(join17(gitDir, "commondir"), "utf8")).trim();
|
|
55168
57358
|
if (commonDir.length > 0) {
|
|
55169
57359
|
return resolve12(gitDir, commonDir);
|
|
55170
57360
|
}
|
|
@@ -56391,7 +58581,7 @@ function createAuthServerClient(options) {
|
|
|
56391
58581
|
|
|
56392
58582
|
// ../server/src/server/package-version.ts
|
|
56393
58583
|
import { existsSync as existsSync17, readFileSync as readFileSync9 } from "node:fs";
|
|
56394
|
-
import
|
|
58584
|
+
import path28 from "node:path";
|
|
56395
58585
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
56396
58586
|
var PackageVersionResolutionError = class extends Error {
|
|
56397
58587
|
constructor(params) {
|
|
@@ -56401,9 +58591,9 @@ var PackageVersionResolutionError = class extends Error {
|
|
|
56401
58591
|
};
|
|
56402
58592
|
function resolvePackageVersion(params) {
|
|
56403
58593
|
const moduleUrl = params.moduleUrl ?? import.meta.url;
|
|
56404
|
-
let currentDir =
|
|
58594
|
+
let currentDir = path28.dirname(fileURLToPath3(moduleUrl));
|
|
56405
58595
|
while (true) {
|
|
56406
|
-
const packageJsonPath =
|
|
58596
|
+
const packageJsonPath = path28.join(currentDir, "package.json");
|
|
56407
58597
|
if (existsSync17(packageJsonPath)) {
|
|
56408
58598
|
try {
|
|
56409
58599
|
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf8"));
|
|
@@ -56422,7 +58612,7 @@ function resolvePackageVersion(params) {
|
|
|
56422
58612
|
}
|
|
56423
58613
|
}
|
|
56424
58614
|
}
|
|
56425
|
-
const parentDir =
|
|
58615
|
+
const parentDir = path28.dirname(currentDir);
|
|
56426
58616
|
if (parentDir === currentDir) {
|
|
56427
58617
|
break;
|
|
56428
58618
|
}
|
|
@@ -56925,11 +59115,11 @@ async function createAppostleDaemon(config, rootLogger) {
|
|
|
56925
59115
|
httpServer.on("upgrade", scriptProxyUpgradeHandler);
|
|
56926
59116
|
const agentStorage = new AgentStorage(config.agentStoragePath, logger);
|
|
56927
59117
|
const projectRegistry = new FileBackedProjectRegistry(
|
|
56928
|
-
|
|
59118
|
+
path29.join(config.appostleHome, "projects", "projects.json"),
|
|
56929
59119
|
logger
|
|
56930
59120
|
);
|
|
56931
59121
|
workspaceRegistry = new FileBackedWorkspaceRegistry(
|
|
56932
|
-
|
|
59122
|
+
path29.join(config.appostleHome, "projects", "workspaces.json"),
|
|
56933
59123
|
logger
|
|
56934
59124
|
);
|
|
56935
59125
|
const chatService = new FileBackedChatService({
|
|
@@ -56991,15 +59181,17 @@ async function createAppostleDaemon(config, rootLogger) {
|
|
|
56991
59181
|
});
|
|
56992
59182
|
await loopService.initialize();
|
|
56993
59183
|
logger.info({ elapsed: elapsed() }, "Loop service initialized");
|
|
56994
|
-
const
|
|
59184
|
+
const questService = new QuestService({
|
|
56995
59185
|
appostleHome: config.appostleHome,
|
|
56996
59186
|
logger,
|
|
56997
59187
|
agentManager
|
|
56998
59188
|
});
|
|
56999
|
-
|
|
57000
|
-
|
|
57001
|
-
|
|
57002
|
-
|
|
59189
|
+
questService.registerRunner("loop", new LoopRunner({ logger }));
|
|
59190
|
+
questService.registerRunner("ralph", new RalphRunner({ logger }));
|
|
59191
|
+
questService.registerRunner("run", new LoopRunner({ logger }));
|
|
59192
|
+
questService.registerRunner("orchestrate", new OrchestratorRunner({ logger }));
|
|
59193
|
+
await questService.initialize();
|
|
59194
|
+
logger.info({ elapsed: elapsed() }, "Quest service initialized");
|
|
57003
59195
|
const scheduleService = new ScheduleService({
|
|
57004
59196
|
appostleHome: config.appostleHome,
|
|
57005
59197
|
logger,
|
|
@@ -57225,7 +59417,7 @@ async function createAppostleDaemon(config, rootLogger) {
|
|
|
57225
59417
|
workspaceRegistry,
|
|
57226
59418
|
chatService,
|
|
57227
59419
|
loopService,
|
|
57228
|
-
|
|
59420
|
+
questService,
|
|
57229
59421
|
scheduleService,
|
|
57230
59422
|
checkoutDiffManager,
|
|
57231
59423
|
scriptRouteStore,
|
|
@@ -57333,16 +59525,16 @@ async function closeAllAgents(logger, agentManager) {
|
|
|
57333
59525
|
}
|
|
57334
59526
|
|
|
57335
59527
|
// ../server/src/server/config.ts
|
|
57336
|
-
import
|
|
59528
|
+
import path31 from "node:path";
|
|
57337
59529
|
import { z as z45 } from "zod";
|
|
57338
59530
|
|
|
57339
59531
|
// ../server/src/server/speech/speech-config-resolver.ts
|
|
57340
59532
|
import { z as z44 } from "zod";
|
|
57341
59533
|
|
|
57342
59534
|
// ../server/src/server/speech/providers/local/config.ts
|
|
57343
|
-
import
|
|
59535
|
+
import path30 from "node:path";
|
|
57344
59536
|
import { z as z42 } from "zod";
|
|
57345
|
-
var DEFAULT_LOCAL_MODELS_SUBDIR =
|
|
59537
|
+
var DEFAULT_LOCAL_MODELS_SUBDIR = path30.join("models", "local-speech");
|
|
57346
59538
|
var NumberLikeSchema2 = z42.union([z42.number(), z42.string().trim().min(1)]);
|
|
57347
59539
|
var OptionalFiniteNumberSchema2 = NumberLikeSchema2.pipe(z42.coerce.number().finite()).optional();
|
|
57348
59540
|
var OptionalIntegerSchema = NumberLikeSchema2.pipe(z42.coerce.number().int()).optional();
|
|
@@ -57369,7 +59561,7 @@ function resolveLocalSpeechConfig(params) {
|
|
|
57369
59561
|
const includeProviderConfig = shouldIncludeLocalProviderConfig(params);
|
|
57370
59562
|
const parsed = LocalSpeechResolutionSchema.parse({
|
|
57371
59563
|
includeProviderConfig,
|
|
57372
|
-
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ??
|
|
59564
|
+
modelsDir: params.env.APPOSTLE_LOCAL_MODELS_DIR ?? params.persisted.providers?.local?.modelsDir ?? path30.join(params.appostleHome, DEFAULT_LOCAL_MODELS_SUBDIR),
|
|
57373
59565
|
dictationLocalSttModel: params.env.APPOSTLE_DICTATION_LOCAL_STT_MODEL ?? persistedLocalFeatureModel(
|
|
57374
59566
|
params.providers.dictationStt.provider,
|
|
57375
59567
|
params.providers.dictationStt.enabled,
|
|
@@ -57625,7 +59817,7 @@ function loadConfig(appostleHome, options) {
|
|
|
57625
59817
|
chromeEnabled,
|
|
57626
59818
|
mcpDebug: env.MCP_DEBUG === "1",
|
|
57627
59819
|
daemonIcon,
|
|
57628
|
-
agentStoragePath:
|
|
59820
|
+
agentStoragePath: path31.join(appostleHome, "agents"),
|
|
57629
59821
|
staticDir: "public",
|
|
57630
59822
|
agentClients: {},
|
|
57631
59823
|
relayEnabled,
|
|
@@ -57644,7 +59836,7 @@ function loadConfig(appostleHome, options) {
|
|
|
57644
59836
|
|
|
57645
59837
|
// ../server/src/server/logger.ts
|
|
57646
59838
|
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync2, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
57647
|
-
import
|
|
59839
|
+
import path32 from "node:path";
|
|
57648
59840
|
import pino from "pino";
|
|
57649
59841
|
import pretty from "pino-pretty";
|
|
57650
59842
|
import { createStream as createRotatingFileStream } from "rotating-file-stream";
|
|
@@ -57687,14 +59879,14 @@ function parsePositiveInteger(value) {
|
|
|
57687
59879
|
return parsed;
|
|
57688
59880
|
}
|
|
57689
59881
|
function resolveFilePath(appostleHome, configuredPath) {
|
|
57690
|
-
const fallback =
|
|
59882
|
+
const fallback = path32.join(appostleHome, DEFAULT_DAEMON_LOG_FILENAME);
|
|
57691
59883
|
if (!configuredPath) {
|
|
57692
59884
|
return fallback;
|
|
57693
59885
|
}
|
|
57694
|
-
if (
|
|
59886
|
+
if (path32.isAbsolute(configuredPath)) {
|
|
57695
59887
|
return configuredPath;
|
|
57696
59888
|
}
|
|
57697
|
-
return
|
|
59889
|
+
return path32.resolve(appostleHome, configuredPath);
|
|
57698
59890
|
}
|
|
57699
59891
|
function minLogLevel(levels) {
|
|
57700
59892
|
let minLevel = levels[0];
|
|
@@ -57731,20 +59923,20 @@ function normalizeLoggerConfigInput(config) {
|
|
|
57731
59923
|
}
|
|
57732
59924
|
function rotateOnRestart(filePath, maxFiles) {
|
|
57733
59925
|
if (!existsSync19(filePath)) return;
|
|
57734
|
-
const dir =
|
|
57735
|
-
const base =
|
|
59926
|
+
const dir = path32.dirname(filePath);
|
|
59927
|
+
const base = path32.basename(filePath);
|
|
57736
59928
|
const now = /* @__PURE__ */ new Date();
|
|
57737
59929
|
const pad = (n) => String(n).padStart(2, "0");
|
|
57738
59930
|
const ts = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
57739
59931
|
try {
|
|
57740
|
-
renameSync3(filePath,
|
|
59932
|
+
renameSync3(filePath, path32.join(dir, `${ts}-00-${base}`));
|
|
57741
59933
|
} catch {
|
|
57742
59934
|
return;
|
|
57743
59935
|
}
|
|
57744
59936
|
const rotatedFiles = readdirSync2(dir).filter((f) => f.endsWith(`-${base}`) && f !== base).sort().reverse();
|
|
57745
59937
|
for (const file of rotatedFiles.slice(maxFiles)) {
|
|
57746
59938
|
try {
|
|
57747
|
-
unlinkSync2(
|
|
59939
|
+
unlinkSync2(path32.join(dir, file));
|
|
57748
59940
|
} catch {
|
|
57749
59941
|
}
|
|
57750
59942
|
}
|
|
@@ -57793,15 +59985,15 @@ function resolveLogConfig(configInput, options) {
|
|
|
57793
59985
|
}
|
|
57794
59986
|
function createRootLogger(configInput, options) {
|
|
57795
59987
|
const config = resolveLogConfig(configInput, options);
|
|
57796
|
-
mkdirSync7(
|
|
59988
|
+
mkdirSync7(path32.dirname(config.file.path), { recursive: true });
|
|
57797
59989
|
const consoleStream = config.console.format === "pretty" ? pretty({
|
|
57798
59990
|
colorize: true,
|
|
57799
59991
|
singleLine: true,
|
|
57800
59992
|
ignore: "pid,hostname"
|
|
57801
59993
|
}) : pino.destination({ dest: 1, sync: false });
|
|
57802
59994
|
rotateOnRestart(config.file.path, config.file.rotate.maxFiles);
|
|
57803
|
-
const fileStream = createRotatingFileStream(
|
|
57804
|
-
path:
|
|
59995
|
+
const fileStream = createRotatingFileStream(path32.basename(config.file.path), {
|
|
59996
|
+
path: path32.dirname(config.file.path),
|
|
57805
59997
|
size: toRotatingFileStreamSize(config.file.rotate.maxSize),
|
|
57806
59998
|
maxFiles: config.file.rotate.maxFiles
|
|
57807
59999
|
});
|
|
@@ -57815,7 +60007,7 @@ function createRootLogger(configInput, options) {
|
|
|
57815
60007
|
}
|
|
57816
60008
|
|
|
57817
60009
|
// ../server/src/server/pid-lock.ts
|
|
57818
|
-
import { open, readFile as
|
|
60010
|
+
import { open, readFile as readFile7, unlink as unlink3, mkdir as mkdir10 } from "node:fs/promises";
|
|
57819
60011
|
import { existsSync as existsSync20 } from "node:fs";
|
|
57820
60012
|
import { join as join18 } from "node:path";
|
|
57821
60013
|
import { hostname } from "node:os";
|
|
@@ -57846,11 +60038,11 @@ function resolveOwnerPid(ownerPid) {
|
|
|
57846
60038
|
async function acquirePidLock(appostleHome, listen, options) {
|
|
57847
60039
|
const pidPath = getPidFilePath(appostleHome);
|
|
57848
60040
|
if (!existsSync20(appostleHome)) {
|
|
57849
|
-
await
|
|
60041
|
+
await mkdir10(appostleHome, { recursive: true });
|
|
57850
60042
|
}
|
|
57851
60043
|
let existingLock = null;
|
|
57852
60044
|
try {
|
|
57853
|
-
const content = await
|
|
60045
|
+
const content = await readFile7(pidPath, "utf-8");
|
|
57854
60046
|
existingLock = JSON.parse(content);
|
|
57855
60047
|
} catch {
|
|
57856
60048
|
}
|
|
@@ -57883,7 +60075,7 @@ async function acquirePidLock(appostleHome, listen, options) {
|
|
|
57883
60075
|
} catch (err) {
|
|
57884
60076
|
if (err.code === "EEXIST") {
|
|
57885
60077
|
try {
|
|
57886
|
-
const content = await
|
|
60078
|
+
const content = await readFile7(pidPath, "utf-8");
|
|
57887
60079
|
const raceLock = JSON.parse(content);
|
|
57888
60080
|
throw new PidLockError(
|
|
57889
60081
|
`Another Appostle daemon is already running (PID ${raceLock.pid})`,
|
|
@@ -57902,7 +60094,7 @@ async function acquirePidLock(appostleHome, listen, options) {
|
|
|
57902
60094
|
async function updatePidLock(appostleHome, patch, options) {
|
|
57903
60095
|
const pidPath = getPidFilePath(appostleHome);
|
|
57904
60096
|
const lockOwnerPid = resolveOwnerPid(options?.ownerPid);
|
|
57905
|
-
const content = await
|
|
60097
|
+
const content = await readFile7(pidPath, "utf-8");
|
|
57906
60098
|
const existingLock = JSON.parse(content);
|
|
57907
60099
|
if (existingLock.pid !== lockOwnerPid) {
|
|
57908
60100
|
throw new PidLockError(`Cannot update PID lock owned by PID ${existingLock.pid}`, existingLock);
|
|
@@ -57923,7 +60115,7 @@ async function releasePidLock(appostleHome, options) {
|
|
|
57923
60115
|
const pidPath = getPidFilePath(appostleHome);
|
|
57924
60116
|
const lockOwnerPid = resolveOwnerPid(options?.ownerPid);
|
|
57925
60117
|
try {
|
|
57926
|
-
const content = await
|
|
60118
|
+
const content = await readFile7(pidPath, "utf-8");
|
|
57927
60119
|
const lock = JSON.parse(content);
|
|
57928
60120
|
if (lock.pid === lockOwnerPid) {
|
|
57929
60121
|
await unlink3(pidPath);
|