webmux 0.28.1 → 0.30.0
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/backend/dist/server.js +884 -120
- package/bin/webmux.js +602 -106
- package/frontend/dist/assets/{DiffDialog-1oCJ0NIo.js → DiffDialog-CjMdljRY.js} +1 -1
- package/frontend/dist/assets/index-CqUU8MH2.css +1 -0
- package/frontend/dist/assets/index-FETEudgI.js +33 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +3 -4
- package/frontend/dist/assets/index-BibFvRPy.js +0 -33
- package/frontend/dist/assets/index-D4MJ4sZs.css +0 -1
package/bin/webmux.js
CHANGED
|
@@ -7190,7 +7190,7 @@ var init_index_esm = __esm(() => {
|
|
|
7190
7190
|
});
|
|
7191
7191
|
|
|
7192
7192
|
// packages/api-contract/src/schemas.ts
|
|
7193
|
-
var BooleanLikeSchema, ErrorResponseSchema, OkResponseSchema, EnabledResponseSchema,
|
|
7193
|
+
var BooleanLikeSchema, ErrorResponseSchema, OkResponseSchema, EnabledResponseSchema, BuiltInAgentIdSchema, AgentIdSchema, WorktreeCreateModeSchema, AgentCapabilitiesSchema, AgentSummarySchema, AgentDetailsSchema, AgentListResponseSchema, UpsertCustomAgentRequestSchema, AgentResponseSchema, ValidateCustomAgentResponseSchema, WorktreeCreationPhaseSchema, AvailableBranchSchema, AvailableBranchesQuerySchema, NumberLikePathParamSchema, BranchListResponseSchema, CreateWorktreeRequestSchema, CreateWorktreeResponseSchema, SetWorktreeArchivedRequestSchema, SetWorktreeArchivedResponseSchema, ToggleEnabledRequestSchema, SendWorktreePromptRequestSchema, AgentsSendMessageRequestSchema, PullMainRequestSchema, PullMainStatusSchema, PullMainResponseSchema, ServiceStatusSchema, PrCommentSchema, CiCheckSchema, PrEntrySchema, LinearIssueLabelSchema, LinearIssueStateSchema, LinkedLinearIssueSchema, LinearIssueSchema, LinearIssueAvailabilitySchema, LinearIssuesResponseSchema, WorktreeCreationStateSchema, AppNotificationSchema, ProjectWorktreeSnapshotSchema, ProjectSnapshotSchema, WorktreeConversationProviderSchema, CodexWorktreeConversationRefSchema, ClaudeWorktreeConversationRefSchema, WorktreeConversationRefSchema, AgentsUiWorktreeSummarySchema, AgentsUiConversationMessageRoleSchema, AgentsUiConversationMessageStatusSchema, AgentsUiConversationMessageSchema, AgentsUiConversationStateSchema, AgentsUiWorktreeConversationResponseSchema, AgentsUiSendMessageResponseSchema, AgentsUiInterruptResponseSchema, AgentsUiConversationSnapshotEventSchema, AgentsUiConversationMessageDeltaEventSchema, AgentsUiConversationErrorEventSchema, AgentsUiConversationEventSchema, WorktreeListResponseSchema, UnpushedCommitSchema, WorktreeDiffResponseSchema, ServiceConfigSchema, ProfileConfigSchema, LinkedRepoInfoSchema, AppConfigSchema, CiLogsResponseSchema, WorktreeNameParamsSchema, NotificationIdParamsSchema, AgentIdParamsSchema, RunIdParamsSchema;
|
|
7194
7194
|
var init_schemas = __esm(() => {
|
|
7195
7195
|
init_zod();
|
|
7196
7196
|
BooleanLikeSchema = exports_external.union([
|
|
@@ -7208,9 +7208,45 @@ var init_schemas = __esm(() => {
|
|
|
7208
7208
|
ok: exports_external.literal(true),
|
|
7209
7209
|
enabled: exports_external.boolean()
|
|
7210
7210
|
});
|
|
7211
|
-
|
|
7212
|
-
|
|
7211
|
+
BuiltInAgentIdSchema = exports_external.enum(["claude", "codex"]);
|
|
7212
|
+
AgentIdSchema = exports_external.string().trim().min(1);
|
|
7213
7213
|
WorktreeCreateModeSchema = exports_external.enum(["new", "existing"]);
|
|
7214
|
+
AgentCapabilitiesSchema = exports_external.object({
|
|
7215
|
+
terminal: exports_external.literal(true),
|
|
7216
|
+
inAppChat: exports_external.boolean(),
|
|
7217
|
+
conversationHistory: exports_external.boolean(),
|
|
7218
|
+
interrupt: exports_external.boolean(),
|
|
7219
|
+
resume: exports_external.boolean()
|
|
7220
|
+
});
|
|
7221
|
+
AgentSummarySchema = exports_external.object({
|
|
7222
|
+
id: AgentIdSchema,
|
|
7223
|
+
label: exports_external.string(),
|
|
7224
|
+
kind: exports_external.enum(["builtin", "custom"]),
|
|
7225
|
+
capabilities: AgentCapabilitiesSchema
|
|
7226
|
+
});
|
|
7227
|
+
AgentDetailsSchema = exports_external.object({
|
|
7228
|
+
id: AgentIdSchema,
|
|
7229
|
+
label: exports_external.string(),
|
|
7230
|
+
kind: exports_external.enum(["builtin", "custom"]),
|
|
7231
|
+
capabilities: AgentCapabilitiesSchema,
|
|
7232
|
+
startCommand: exports_external.string().nullable(),
|
|
7233
|
+
resumeCommand: exports_external.string().nullable()
|
|
7234
|
+
});
|
|
7235
|
+
AgentListResponseSchema = exports_external.object({
|
|
7236
|
+
agents: exports_external.array(AgentDetailsSchema)
|
|
7237
|
+
});
|
|
7238
|
+
UpsertCustomAgentRequestSchema = exports_external.object({
|
|
7239
|
+
label: exports_external.string().trim().min(1),
|
|
7240
|
+
startCommand: exports_external.string().trim().min(1),
|
|
7241
|
+
resumeCommand: exports_external.string().trim().optional()
|
|
7242
|
+
});
|
|
7243
|
+
AgentResponseSchema = exports_external.object({
|
|
7244
|
+
agent: AgentDetailsSchema
|
|
7245
|
+
});
|
|
7246
|
+
ValidateCustomAgentResponseSchema = exports_external.object({
|
|
7247
|
+
normalizedId: AgentIdSchema,
|
|
7248
|
+
warnings: exports_external.array(exports_external.string())
|
|
7249
|
+
});
|
|
7214
7250
|
WorktreeCreationPhaseSchema = exports_external.enum([
|
|
7215
7251
|
"creating_worktree",
|
|
7216
7252
|
"preparing_runtime",
|
|
@@ -7236,7 +7272,8 @@ var init_schemas = __esm(() => {
|
|
|
7236
7272
|
branch: exports_external.string().optional(),
|
|
7237
7273
|
baseBranch: exports_external.string().optional(),
|
|
7238
7274
|
profile: exports_external.string().optional(),
|
|
7239
|
-
agent:
|
|
7275
|
+
agent: AgentIdSchema.optional(),
|
|
7276
|
+
agents: exports_external.array(AgentIdSchema).min(1).optional(),
|
|
7240
7277
|
prompt: exports_external.string().optional(),
|
|
7241
7278
|
envOverrides: exports_external.record(exports_external.string()).optional(),
|
|
7242
7279
|
createLinearTicket: exports_external.literal(true).optional(),
|
|
@@ -7367,7 +7404,8 @@ var init_schemas = __esm(() => {
|
|
|
7367
7404
|
dir: exports_external.string(),
|
|
7368
7405
|
archived: exports_external.boolean(),
|
|
7369
7406
|
profile: exports_external.string().nullable(),
|
|
7370
|
-
agentName:
|
|
7407
|
+
agentName: AgentIdSchema.nullable(),
|
|
7408
|
+
agentLabel: exports_external.string().nullable(),
|
|
7371
7409
|
mux: exports_external.boolean(),
|
|
7372
7410
|
dirty: exports_external.boolean(),
|
|
7373
7411
|
unpushed: exports_external.boolean(),
|
|
@@ -7412,7 +7450,8 @@ var init_schemas = __esm(() => {
|
|
|
7412
7450
|
path: exports_external.string(),
|
|
7413
7451
|
archived: exports_external.boolean(),
|
|
7414
7452
|
profile: exports_external.string().nullable(),
|
|
7415
|
-
agentName:
|
|
7453
|
+
agentName: AgentIdSchema.nullable(),
|
|
7454
|
+
agentLabel: exports_external.string().nullable(),
|
|
7416
7455
|
mux: exports_external.boolean(),
|
|
7417
7456
|
status: exports_external.string(),
|
|
7418
7457
|
dirty: exports_external.boolean(),
|
|
@@ -7504,7 +7543,9 @@ var init_schemas = __esm(() => {
|
|
|
7504
7543
|
name: exports_external.string(),
|
|
7505
7544
|
services: exports_external.array(ServiceConfigSchema),
|
|
7506
7545
|
profiles: exports_external.array(ProfileConfigSchema),
|
|
7546
|
+
agents: exports_external.array(AgentSummarySchema),
|
|
7507
7547
|
defaultProfileName: exports_external.string(),
|
|
7548
|
+
defaultAgentId: BuiltInAgentIdSchema,
|
|
7508
7549
|
autoName: exports_external.boolean(),
|
|
7509
7550
|
linearCreateTicketOption: exports_external.boolean(),
|
|
7510
7551
|
startupEnvs: exports_external.record(exports_external.union([exports_external.string(), exports_external.boolean()])),
|
|
@@ -7523,6 +7564,9 @@ var init_schemas = __esm(() => {
|
|
|
7523
7564
|
NotificationIdParamsSchema = exports_external.object({
|
|
7524
7565
|
id: NumberLikePathParamSchema
|
|
7525
7566
|
});
|
|
7567
|
+
AgentIdParamsSchema = exports_external.object({
|
|
7568
|
+
id: AgentIdSchema
|
|
7569
|
+
});
|
|
7526
7570
|
RunIdParamsSchema = exports_external.object({
|
|
7527
7571
|
runId: NumberLikePathParamSchema
|
|
7528
7572
|
});
|
|
@@ -7539,6 +7583,11 @@ var init_contract = __esm(() => {
|
|
|
7539
7583
|
fetchAvailableBranches: "/api/branches",
|
|
7540
7584
|
fetchBaseBranches: "/api/base-branches",
|
|
7541
7585
|
fetchProject: "/api/project",
|
|
7586
|
+
fetchAgents: "/api/agents",
|
|
7587
|
+
createAgent: "/api/agents",
|
|
7588
|
+
updateAgent: "/api/agents/:id",
|
|
7589
|
+
deleteAgent: "/api/agents/:id",
|
|
7590
|
+
validateAgent: "/api/agents/validate",
|
|
7542
7591
|
attachAgentsWorktreeConversation: "/api/agents/worktrees/:name/attach",
|
|
7543
7592
|
fetchAgentsWorktreeConversationHistory: "/api/agents/worktrees/:name/history",
|
|
7544
7593
|
sendAgentsWorktreeConversationMessage: "/api/agents/worktrees/:name/messages",
|
|
@@ -7603,6 +7652,60 @@ var init_contract = __esm(() => {
|
|
|
7603
7652
|
502: ErrorResponseSchema
|
|
7604
7653
|
}
|
|
7605
7654
|
},
|
|
7655
|
+
fetchAgents: {
|
|
7656
|
+
method: "GET",
|
|
7657
|
+
path: apiPaths.fetchAgents,
|
|
7658
|
+
responses: {
|
|
7659
|
+
200: AgentListResponseSchema,
|
|
7660
|
+
500: ErrorResponseSchema
|
|
7661
|
+
}
|
|
7662
|
+
},
|
|
7663
|
+
createAgent: {
|
|
7664
|
+
method: "POST",
|
|
7665
|
+
path: apiPaths.createAgent,
|
|
7666
|
+
body: UpsertCustomAgentRequestSchema,
|
|
7667
|
+
responses: {
|
|
7668
|
+
200: AgentResponseSchema,
|
|
7669
|
+
400: ErrorResponseSchema,
|
|
7670
|
+
409: ErrorResponseSchema,
|
|
7671
|
+
500: ErrorResponseSchema
|
|
7672
|
+
}
|
|
7673
|
+
},
|
|
7674
|
+
updateAgent: {
|
|
7675
|
+
method: "PUT",
|
|
7676
|
+
path: apiPaths.updateAgent,
|
|
7677
|
+
pathParams: AgentIdParamsSchema,
|
|
7678
|
+
body: UpsertCustomAgentRequestSchema,
|
|
7679
|
+
responses: {
|
|
7680
|
+
200: AgentResponseSchema,
|
|
7681
|
+
400: ErrorResponseSchema,
|
|
7682
|
+
404: ErrorResponseSchema,
|
|
7683
|
+
409: ErrorResponseSchema,
|
|
7684
|
+
500: ErrorResponseSchema
|
|
7685
|
+
}
|
|
7686
|
+
},
|
|
7687
|
+
deleteAgent: {
|
|
7688
|
+
method: "DELETE",
|
|
7689
|
+
path: apiPaths.deleteAgent,
|
|
7690
|
+
pathParams: AgentIdParamsSchema,
|
|
7691
|
+
body: c2.noBody(),
|
|
7692
|
+
responses: {
|
|
7693
|
+
200: OkResponseSchema,
|
|
7694
|
+
400: ErrorResponseSchema,
|
|
7695
|
+
404: ErrorResponseSchema,
|
|
7696
|
+
500: ErrorResponseSchema
|
|
7697
|
+
}
|
|
7698
|
+
},
|
|
7699
|
+
validateAgent: {
|
|
7700
|
+
method: "POST",
|
|
7701
|
+
path: apiPaths.validateAgent,
|
|
7702
|
+
body: UpsertCustomAgentRequestSchema,
|
|
7703
|
+
responses: {
|
|
7704
|
+
200: ValidateCustomAgentResponseSchema,
|
|
7705
|
+
400: ErrorResponseSchema,
|
|
7706
|
+
500: ErrorResponseSchema
|
|
7707
|
+
}
|
|
7708
|
+
},
|
|
7606
7709
|
attachAgentsWorktreeConversation: {
|
|
7607
7710
|
method: "POST",
|
|
7608
7711
|
path: apiPaths.attachAgentsWorktreeConversation,
|
|
@@ -15342,6 +15445,38 @@ function parseProfiles(raw, includeDefaultProfile) {
|
|
|
15342
15445
|
}
|
|
15343
15446
|
return profiles;
|
|
15344
15447
|
}
|
|
15448
|
+
function cloneAgentConfig(agent) {
|
|
15449
|
+
return { ...agent };
|
|
15450
|
+
}
|
|
15451
|
+
function cloneAgents(agents) {
|
|
15452
|
+
return Object.fromEntries(Object.entries(agents).map(([id, agent]) => [id, cloneAgentConfig(agent)]));
|
|
15453
|
+
}
|
|
15454
|
+
function parseCustomAgent(raw) {
|
|
15455
|
+
if (!isRecord4(raw))
|
|
15456
|
+
return null;
|
|
15457
|
+
if (typeof raw.label !== "string" || !raw.label.trim())
|
|
15458
|
+
return null;
|
|
15459
|
+
if (typeof raw.startCommand !== "string" || !raw.startCommand.trim())
|
|
15460
|
+
return null;
|
|
15461
|
+
return {
|
|
15462
|
+
label: raw.label.trim(),
|
|
15463
|
+
startCommand: raw.startCommand.trim(),
|
|
15464
|
+
...typeof raw.resumeCommand === "string" && raw.resumeCommand.trim() ? { resumeCommand: raw.resumeCommand.trim() } : {}
|
|
15465
|
+
};
|
|
15466
|
+
}
|
|
15467
|
+
function parseCustomAgents(raw) {
|
|
15468
|
+
if (!isRecord4(raw))
|
|
15469
|
+
return {};
|
|
15470
|
+
return Object.entries(raw).reduce((acc, [id, value]) => {
|
|
15471
|
+
if (!id.trim())
|
|
15472
|
+
return acc;
|
|
15473
|
+
const parsed = parseCustomAgent(value);
|
|
15474
|
+
if (parsed) {
|
|
15475
|
+
acc[id.trim()] = parsed;
|
|
15476
|
+
}
|
|
15477
|
+
return acc;
|
|
15478
|
+
}, {});
|
|
15479
|
+
}
|
|
15345
15480
|
function parseServices(raw) {
|
|
15346
15481
|
if (!Array.isArray(raw))
|
|
15347
15482
|
return [];
|
|
@@ -15434,6 +15569,7 @@ function parseProjectConfig(parsed) {
|
|
|
15434
15569
|
autoPull: isRecord4(parsed.workspace) ? parseAutoPull(parsed.workspace.autoPull) : DEFAULT_CONFIG.workspace.autoPull
|
|
15435
15570
|
},
|
|
15436
15571
|
profiles: parseProfiles(parsed.profiles, true),
|
|
15572
|
+
agents: {},
|
|
15437
15573
|
services: parseServices(parsed.services),
|
|
15438
15574
|
startupEnvs: parseStartupEnvs(parsed.startupEnvs),
|
|
15439
15575
|
integrations: {
|
|
@@ -15501,20 +15637,21 @@ function loadLocalProjectConfigOverlay(root) {
|
|
|
15501
15637
|
try {
|
|
15502
15638
|
const text = readLocalConfigFile(root).trim();
|
|
15503
15639
|
if (!text) {
|
|
15504
|
-
return { worktreeRoot: null, profiles: {}, lifecycleHooks: {}, linear: null, github: null, autoPull: null };
|
|
15640
|
+
return { worktreeRoot: null, profiles: {}, agents: {}, lifecycleHooks: {}, linear: null, github: null, autoPull: null };
|
|
15505
15641
|
}
|
|
15506
15642
|
const parsed = parseConfigDocument(text);
|
|
15507
15643
|
const ws = isRecord4(parsed.workspace) ? parsed.workspace : null;
|
|
15508
15644
|
return {
|
|
15509
15645
|
worktreeRoot: ws && typeof ws.worktreeRoot === "string" ? ws.worktreeRoot : null,
|
|
15510
15646
|
profiles: parseProfiles(parsed.profiles, false),
|
|
15647
|
+
agents: parseCustomAgents(parsed.agents),
|
|
15511
15648
|
lifecycleHooks: parseLifecycleHooks(parsed.lifecycleHooks),
|
|
15512
15649
|
linear: parseLocalLinearOverlay(parsed),
|
|
15513
15650
|
github: parseLocalGitHubOverlay(parsed),
|
|
15514
15651
|
autoPull: parseLocalAutoPullOverlay(parsed)
|
|
15515
15652
|
};
|
|
15516
15653
|
} catch {
|
|
15517
|
-
return { worktreeRoot: null, profiles: {}, lifecycleHooks: {}, linear: null, github: null, autoPull: null };
|
|
15654
|
+
return { worktreeRoot: null, profiles: {}, agents: {}, lifecycleHooks: {}, linear: null, github: null, autoPull: null };
|
|
15518
15655
|
}
|
|
15519
15656
|
}
|
|
15520
15657
|
function mergeHookCommand(projectCommand, localCommand) {
|
|
@@ -15574,6 +15711,10 @@ function loadConfig(dir, options = {}) {
|
|
|
15574
15711
|
...cloneProfiles(projectConfig.profiles),
|
|
15575
15712
|
...cloneProfiles(localOverlay.profiles)
|
|
15576
15713
|
},
|
|
15714
|
+
agents: {
|
|
15715
|
+
...cloneAgents(projectConfig.agents),
|
|
15716
|
+
...cloneAgents(localOverlay.agents)
|
|
15717
|
+
},
|
|
15577
15718
|
lifecycleHooks: mergeLifecycleHooks(projectConfig.lifecycleHooks, localOverlay.lifecycleHooks),
|
|
15578
15719
|
integrations
|
|
15579
15720
|
};
|
|
@@ -15603,6 +15744,7 @@ var init_config = __esm(() => {
|
|
|
15603
15744
|
panes: clonePanes(DEFAULT_PANES)
|
|
15604
15745
|
}
|
|
15605
15746
|
},
|
|
15747
|
+
agents: {},
|
|
15606
15748
|
services: [],
|
|
15607
15749
|
startupEnvs: {},
|
|
15608
15750
|
integrations: {
|
|
@@ -16184,7 +16326,7 @@ class AutoNameService {
|
|
|
16184
16326
|
return normalizeGeneratedBranchName(output);
|
|
16185
16327
|
}
|
|
16186
16328
|
}
|
|
16187
|
-
var MAX_BRANCH_LENGTH = 40, DEFAULT_AUTO_NAME_MODEL = "claude-haiku-4-5-20251001", AUTO_NAME_TIMEOUT_MS =
|
|
16329
|
+
var MAX_BRANCH_LENGTH = 40, DEFAULT_AUTO_NAME_MODEL = "claude-haiku-4-5-20251001", AUTO_NAME_TIMEOUT_MS = 15000, DEFAULT_SYSTEM_PROMPT, AutoNameTimeoutError;
|
|
16188
16330
|
var init_auto_name_service = __esm(() => {
|
|
16189
16331
|
init_policies();
|
|
16190
16332
|
init_branch_name();
|
|
@@ -16271,7 +16413,7 @@ var init_archive_state_service = __esm(() => {
|
|
|
16271
16413
|
|
|
16272
16414
|
// backend/src/adapters/agent-runtime.ts
|
|
16273
16415
|
import { chmod as chmod2, mkdir as mkdir3 } from "fs/promises";
|
|
16274
|
-
import { dirname as dirname4, join as join9 } from "path";
|
|
16416
|
+
import { dirname as dirname4, join as join9, resolve as resolve6 } from "path";
|
|
16275
16417
|
function shellQuote(value) {
|
|
16276
16418
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
16277
16419
|
}
|
|
@@ -16290,6 +16432,7 @@ from pathlib import Path
|
|
|
16290
16432
|
|
|
16291
16433
|
|
|
16292
16434
|
CONTROL_ENV_PATH = Path(__file__).resolve().with_name("control.env")
|
|
16435
|
+
CONTROL_REQUEST_TIMEOUT_SECONDS = 2
|
|
16293
16436
|
|
|
16294
16437
|
|
|
16295
16438
|
def read_control_env():
|
|
@@ -16319,6 +16462,7 @@ def build_parser():
|
|
|
16319
16462
|
|
|
16320
16463
|
status_changed = subparsers.add_parser("status-changed")
|
|
16321
16464
|
status_changed.add_argument("--lifecycle", choices=["starting", "running", "idle", "stopped"], required=True)
|
|
16465
|
+
status_changed.add_argument("--best-effort", action="store_true")
|
|
16322
16466
|
|
|
16323
16467
|
pr_opened = subparsers.add_parser("pr-opened")
|
|
16324
16468
|
pr_opened.add_argument("--url")
|
|
@@ -16328,6 +16472,11 @@ def build_parser():
|
|
|
16328
16472
|
|
|
16329
16473
|
subparsers.add_parser("claude-user-prompt-submit")
|
|
16330
16474
|
subparsers.add_parser("claude-post-tool-use")
|
|
16475
|
+
subparsers.add_parser("codex-session-start")
|
|
16476
|
+
subparsers.add_parser("codex-user-prompt-submit")
|
|
16477
|
+
subparsers.add_parser("codex-permission-request")
|
|
16478
|
+
subparsers.add_parser("codex-post-tool-use")
|
|
16479
|
+
subparsers.add_parser("codex-stop")
|
|
16331
16480
|
|
|
16332
16481
|
return parser
|
|
16333
16482
|
|
|
@@ -16370,6 +16519,41 @@ def read_hook_payload():
|
|
|
16370
16519
|
return parsed if isinstance(parsed, dict) else {}
|
|
16371
16520
|
|
|
16372
16521
|
|
|
16522
|
+
def iter_string_values(value):
|
|
16523
|
+
if isinstance(value, str):
|
|
16524
|
+
yield value
|
|
16525
|
+
return
|
|
16526
|
+
if isinstance(value, dict):
|
|
16527
|
+
for child in value.values():
|
|
16528
|
+
yield from iter_string_values(child)
|
|
16529
|
+
return
|
|
16530
|
+
if isinstance(value, list):
|
|
16531
|
+
for child in value:
|
|
16532
|
+
yield from iter_string_values(child)
|
|
16533
|
+
|
|
16534
|
+
|
|
16535
|
+
def find_pr_url(value):
|
|
16536
|
+
for text in iter_string_values(value):
|
|
16537
|
+
match = re.search(r"https://github\\.com/[^\\s\\"]+/pull/\\d+", text)
|
|
16538
|
+
if match:
|
|
16539
|
+
return match.group(0)
|
|
16540
|
+
return None
|
|
16541
|
+
|
|
16542
|
+
|
|
16543
|
+
def maybe_send_pr_opened(hook_payload, control_env):
|
|
16544
|
+
tool_name = hook_payload.get("tool_name")
|
|
16545
|
+
tool_input = hook_payload.get("tool_input")
|
|
16546
|
+
if not isinstance(tool_input, dict) or tool_name != "Bash":
|
|
16547
|
+
return True
|
|
16548
|
+
|
|
16549
|
+
command = tool_input.get("command")
|
|
16550
|
+
if not isinstance(command, str) or "gh pr create" not in command:
|
|
16551
|
+
return True
|
|
16552
|
+
|
|
16553
|
+
pr_args = argparse.Namespace(url=find_pr_url(hook_payload.get("tool_response")))
|
|
16554
|
+
return send_payload(build_payload("pr-opened", pr_args, control_env), control_env)
|
|
16555
|
+
|
|
16556
|
+
|
|
16373
16557
|
def send_payload(payload, control_env):
|
|
16374
16558
|
request = urllib.request.Request(
|
|
16375
16559
|
control_env["WEBMUX_CONTROL_URL"],
|
|
@@ -16382,7 +16566,7 @@ def send_payload(payload, control_env):
|
|
|
16382
16566
|
)
|
|
16383
16567
|
|
|
16384
16568
|
try:
|
|
16385
|
-
with urllib.request.urlopen(request, timeout=
|
|
16569
|
+
with urllib.request.urlopen(request, timeout=CONTROL_REQUEST_TIMEOUT_SECONDS) as response:
|
|
16386
16570
|
if response.status < 200 or response.status >= 300:
|
|
16387
16571
|
print(f"control endpoint returned HTTP {response.status}", file=sys.stderr)
|
|
16388
16572
|
return False
|
|
@@ -16416,34 +16600,40 @@ def main():
|
|
|
16416
16600
|
print(f"missing control env keys: {', '.join(missing)}", file=sys.stderr)
|
|
16417
16601
|
return 1
|
|
16418
16602
|
|
|
16603
|
+
if parsed.command == "codex-session-start":
|
|
16604
|
+
send_payload(build_payload("status-changed", argparse.Namespace(lifecycle="idle"), control_env), control_env)
|
|
16605
|
+
return 0
|
|
16606
|
+
|
|
16607
|
+
if parsed.command == "codex-user-prompt-submit":
|
|
16608
|
+
send_payload(build_payload("status-changed", argparse.Namespace(lifecycle="running"), control_env), control_env)
|
|
16609
|
+
return 0
|
|
16610
|
+
|
|
16419
16611
|
if parsed.command == "claude-user-prompt-submit":
|
|
16420
16612
|
if not send_payload(build_payload("status-changed", argparse.Namespace(lifecycle="running"), control_env), control_env):
|
|
16421
16613
|
return 1
|
|
16422
16614
|
return 0
|
|
16423
16615
|
|
|
16424
|
-
if parsed.command == "
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
tool_input = hook_payload.get("tool_input")
|
|
16428
|
-
if not isinstance(tool_input, dict) or tool_name != "Bash":
|
|
16429
|
-
return 0
|
|
16616
|
+
if parsed.command == "codex-permission-request":
|
|
16617
|
+
send_payload(build_payload("status-changed", argparse.Namespace(lifecycle="idle"), control_env), control_env)
|
|
16618
|
+
return 0
|
|
16430
16619
|
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16620
|
+
if parsed.command == "codex-post-tool-use":
|
|
16621
|
+
hook_payload = read_hook_payload()
|
|
16622
|
+
maybe_send_pr_opened(hook_payload, control_env)
|
|
16623
|
+
return 0
|
|
16434
16624
|
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
if
|
|
16438
|
-
match = re.search(r"https://github\\.com/[^\\s\\"]+/pull/\\d+", tool_response)
|
|
16439
|
-
if match:
|
|
16440
|
-
pr_args.url = match.group(0)
|
|
16625
|
+
if parsed.command == "claude-post-tool-use":
|
|
16626
|
+
hook_payload = read_hook_payload()
|
|
16627
|
+
return 0 if maybe_send_pr_opened(hook_payload, control_env) else 1
|
|
16441
16628
|
|
|
16442
|
-
|
|
16629
|
+
if parsed.command == "codex-stop":
|
|
16630
|
+
send_payload(build_payload("agent-stopped", parsed, control_env), control_env)
|
|
16631
|
+
print(json.dumps({}))
|
|
16632
|
+
return 0
|
|
16443
16633
|
|
|
16444
16634
|
payload = build_payload(parsed.command, parsed, control_env)
|
|
16445
16635
|
if not send_payload(payload, control_env):
|
|
16446
|
-
return 1
|
|
16636
|
+
return 0 if getattr(parsed, "best_effort", False) else 1
|
|
16447
16637
|
|
|
16448
16638
|
return 0
|
|
16449
16639
|
|
|
@@ -16513,6 +16703,81 @@ function buildClaudeHookSettings(input) {
|
|
|
16513
16703
|
}
|
|
16514
16704
|
};
|
|
16515
16705
|
}
|
|
16706
|
+
function buildCodexHookSettings(input) {
|
|
16707
|
+
const statusCommand = `${shellQuote(input.agentCtlPath)} status-changed --lifecycle running --best-effort`;
|
|
16708
|
+
return {
|
|
16709
|
+
hooks: {
|
|
16710
|
+
SessionStart: [
|
|
16711
|
+
{
|
|
16712
|
+
matcher: "startup|resume|clear",
|
|
16713
|
+
hooks: [
|
|
16714
|
+
{
|
|
16715
|
+
type: "command",
|
|
16716
|
+
command: `${shellQuote(input.agentCtlPath)} codex-session-start`,
|
|
16717
|
+
timeout: 30
|
|
16718
|
+
}
|
|
16719
|
+
]
|
|
16720
|
+
}
|
|
16721
|
+
],
|
|
16722
|
+
UserPromptSubmit: [
|
|
16723
|
+
{
|
|
16724
|
+
hooks: [
|
|
16725
|
+
{
|
|
16726
|
+
type: "command",
|
|
16727
|
+
command: `${shellQuote(input.agentCtlPath)} codex-user-prompt-submit`,
|
|
16728
|
+
timeout: 30
|
|
16729
|
+
}
|
|
16730
|
+
]
|
|
16731
|
+
}
|
|
16732
|
+
],
|
|
16733
|
+
PermissionRequest: [
|
|
16734
|
+
{
|
|
16735
|
+
hooks: [
|
|
16736
|
+
{
|
|
16737
|
+
type: "command",
|
|
16738
|
+
command: `${shellQuote(input.agentCtlPath)} codex-permission-request`,
|
|
16739
|
+
timeout: 30
|
|
16740
|
+
}
|
|
16741
|
+
]
|
|
16742
|
+
}
|
|
16743
|
+
],
|
|
16744
|
+
PreToolUse: [
|
|
16745
|
+
{
|
|
16746
|
+
hooks: [
|
|
16747
|
+
{
|
|
16748
|
+
type: "command",
|
|
16749
|
+
command: statusCommand,
|
|
16750
|
+
timeout: 30
|
|
16751
|
+
}
|
|
16752
|
+
]
|
|
16753
|
+
}
|
|
16754
|
+
],
|
|
16755
|
+
PostToolUse: [
|
|
16756
|
+
{
|
|
16757
|
+
matcher: "Bash",
|
|
16758
|
+
hooks: [
|
|
16759
|
+
{
|
|
16760
|
+
type: "command",
|
|
16761
|
+
command: `${shellQuote(input.agentCtlPath)} codex-post-tool-use`,
|
|
16762
|
+
timeout: 30
|
|
16763
|
+
}
|
|
16764
|
+
]
|
|
16765
|
+
}
|
|
16766
|
+
],
|
|
16767
|
+
Stop: [
|
|
16768
|
+
{
|
|
16769
|
+
hooks: [
|
|
16770
|
+
{
|
|
16771
|
+
type: "command",
|
|
16772
|
+
command: `${shellQuote(input.agentCtlPath)} codex-stop`,
|
|
16773
|
+
timeout: 30
|
|
16774
|
+
}
|
|
16775
|
+
]
|
|
16776
|
+
}
|
|
16777
|
+
]
|
|
16778
|
+
}
|
|
16779
|
+
};
|
|
16780
|
+
}
|
|
16516
16781
|
async function mergeClaudeSettings(settingsPath, hookSettings) {
|
|
16517
16782
|
let existing = {};
|
|
16518
16783
|
try {
|
|
@@ -16532,13 +16797,77 @@ async function mergeClaudeSettings(settingsPath, hookSettings) {
|
|
|
16532
16797
|
await Bun.write(settingsPath, JSON.stringify(merged, null, 2) + `
|
|
16533
16798
|
`);
|
|
16534
16799
|
}
|
|
16800
|
+
function commandStartsWithAgentCtl(command, agentCtlPath) {
|
|
16801
|
+
const trimmedCommand = command.trimStart();
|
|
16802
|
+
const quotedAgentCtlPath = shellQuote(agentCtlPath);
|
|
16803
|
+
return trimmedCommand === agentCtlPath || trimmedCommand.startsWith(`${agentCtlPath} `) || trimmedCommand === quotedAgentCtlPath || trimmedCommand.startsWith(`${quotedAgentCtlPath} `);
|
|
16804
|
+
}
|
|
16805
|
+
function isWebmuxHookGroup(group, agentCtlPath) {
|
|
16806
|
+
if (!isRecord5(group) || !Array.isArray(group.hooks))
|
|
16807
|
+
return false;
|
|
16808
|
+
return group.hooks.some((hook) => isRecord5(hook) && typeof hook.command === "string" && commandStartsWithAgentCtl(hook.command, agentCtlPath));
|
|
16809
|
+
}
|
|
16810
|
+
async function mergeCodexHooksFile(hooksPath, hookSettings, agentCtlPath) {
|
|
16811
|
+
let existing = {};
|
|
16812
|
+
try {
|
|
16813
|
+
const file = Bun.file(hooksPath);
|
|
16814
|
+
if (await file.exists()) {
|
|
16815
|
+
const parsed = await file.json();
|
|
16816
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
16817
|
+
existing = parsed;
|
|
16818
|
+
}
|
|
16819
|
+
}
|
|
16820
|
+
} catch {
|
|
16821
|
+
existing = {};
|
|
16822
|
+
}
|
|
16823
|
+
const existingHooks = isRecord5(existing.hooks) ? existing.hooks : {};
|
|
16824
|
+
const mergedHooks = { ...existingHooks };
|
|
16825
|
+
for (const [eventName, groups] of Object.entries(hookSettings)) {
|
|
16826
|
+
const eventGroups = existingHooks[eventName];
|
|
16827
|
+
const preservedGroups = Array.isArray(eventGroups) ? eventGroups.filter((group) => !isWebmuxHookGroup(group, agentCtlPath)) : [];
|
|
16828
|
+
mergedHooks[eventName] = [...preservedGroups, ...groups];
|
|
16829
|
+
}
|
|
16830
|
+
await Bun.write(hooksPath, JSON.stringify({ ...existing, hooks: mergedHooks }, null, 2) + `
|
|
16831
|
+
`);
|
|
16832
|
+
}
|
|
16833
|
+
async function resolveGitCommonDir(gitDir) {
|
|
16834
|
+
try {
|
|
16835
|
+
const commonDir = (await Bun.file(join9(gitDir, "commondir")).text()).trim();
|
|
16836
|
+
if (!commonDir)
|
|
16837
|
+
return gitDir;
|
|
16838
|
+
return commonDir.startsWith("/") ? commonDir : resolve6(gitDir, commonDir);
|
|
16839
|
+
} catch {
|
|
16840
|
+
return gitDir;
|
|
16841
|
+
}
|
|
16842
|
+
}
|
|
16843
|
+
async function ensureGeneratedCodexHooksIgnored(gitDir) {
|
|
16844
|
+
const commonDir = await resolveGitCommonDir(gitDir);
|
|
16845
|
+
const excludePath = join9(commonDir, "info", "exclude");
|
|
16846
|
+
let existing = "";
|
|
16847
|
+
try {
|
|
16848
|
+
existing = await Bun.file(excludePath).text();
|
|
16849
|
+
} catch {
|
|
16850
|
+
existing = "";
|
|
16851
|
+
}
|
|
16852
|
+
const lines = existing.split(/\r?\n/).map((line) => line.trim());
|
|
16853
|
+
if (lines.includes(GENERATED_CODEX_HOOKS_EXCLUDE))
|
|
16854
|
+
return;
|
|
16855
|
+
await mkdir3(dirname4(excludePath), { recursive: true });
|
|
16856
|
+
const separator = existing.length > 0 && !existing.endsWith(`
|
|
16857
|
+
`) ? `
|
|
16858
|
+
` : "";
|
|
16859
|
+
await Bun.write(excludePath, `${existing}${separator}${GENERATED_CODEX_HOOKS_EXCLUDE}
|
|
16860
|
+
`);
|
|
16861
|
+
}
|
|
16535
16862
|
async function ensureAgentRuntimeArtifacts(input) {
|
|
16536
16863
|
const storagePaths = getWorktreeStoragePaths(input.gitDir);
|
|
16537
16864
|
const artifacts = {
|
|
16538
16865
|
agentCtlPath: join9(storagePaths.webmuxDir, "webmux-agentctl"),
|
|
16539
|
-
claudeSettingsPath: join9(input.worktreePath, ".claude", "settings.local.json")
|
|
16866
|
+
claudeSettingsPath: join9(input.worktreePath, ".claude", "settings.local.json"),
|
|
16867
|
+
codexHooksPath: join9(input.worktreePath, ".codex", "hooks.json")
|
|
16540
16868
|
};
|
|
16541
16869
|
await mkdir3(dirname4(artifacts.claudeSettingsPath), { recursive: true });
|
|
16870
|
+
await mkdir3(dirname4(artifacts.codexHooksPath), { recursive: true });
|
|
16542
16871
|
await Bun.write(artifacts.agentCtlPath, buildAgentCtlScript());
|
|
16543
16872
|
await chmod2(artifacts.agentCtlPath, 493);
|
|
16544
16873
|
const hookSettings = buildClaudeHookSettings(artifacts);
|
|
@@ -16547,8 +16876,11 @@ async function ensureAgentRuntimeArtifacts(input) {
|
|
|
16547
16876
|
throw new Error("Invalid Claude hook settings");
|
|
16548
16877
|
}
|
|
16549
16878
|
await mergeClaudeSettings(artifacts.claudeSettingsPath, hooks);
|
|
16879
|
+
await ensureGeneratedCodexHooksIgnored(input.gitDir);
|
|
16880
|
+
await mergeCodexHooksFile(artifacts.codexHooksPath, buildCodexHookSettings(artifacts).hooks, artifacts.agentCtlPath);
|
|
16550
16881
|
return artifacts;
|
|
16551
16882
|
}
|
|
16883
|
+
var GENERATED_CODEX_HOOKS_EXCLUDE = ".codex/hooks.json";
|
|
16552
16884
|
var init_agent_runtime = __esm(() => {
|
|
16553
16885
|
init_fs();
|
|
16554
16886
|
});
|
|
@@ -16563,17 +16895,18 @@ function buildRuntimeBootstrap(runtimeEnvPath) {
|
|
|
16563
16895
|
function buildDockerRuntimeBootstrap(runtimeEnvPath) {
|
|
16564
16896
|
return `${buildRuntimeBootstrap(runtimeEnvPath)}; export PATH="$PATH:${DOCKER_PATH_FALLBACK}"`;
|
|
16565
16897
|
}
|
|
16566
|
-
function
|
|
16898
|
+
function buildBuiltInAgentInvocation(input) {
|
|
16567
16899
|
if (input.agent === "codex") {
|
|
16900
|
+
const hooksFlag = " --enable codex_hooks";
|
|
16568
16901
|
const yoloFlag2 = input.yolo ? " --yolo" : "";
|
|
16569
16902
|
if (input.launchMode === "resume") {
|
|
16570
|
-
return `codex${yoloFlag2} resume --last`;
|
|
16903
|
+
return `codex${hooksFlag}${yoloFlag2} resume --last`;
|
|
16571
16904
|
}
|
|
16572
16905
|
const promptSuffix2 = input.prompt ? ` -- ${quoteShell(input.prompt)}` : "";
|
|
16573
16906
|
if (input.systemPrompt) {
|
|
16574
|
-
return `codex${yoloFlag2} -c ${quoteShell(`developer_instructions=${input.systemPrompt}`)}${promptSuffix2}`;
|
|
16907
|
+
return `codex${hooksFlag}${yoloFlag2} -c ${quoteShell(`developer_instructions=${input.systemPrompt}`)}${promptSuffix2}`;
|
|
16575
16908
|
}
|
|
16576
|
-
return `codex${yoloFlag2}${promptSuffix2}`;
|
|
16909
|
+
return `codex${hooksFlag}${yoloFlag2}${promptSuffix2}`;
|
|
16577
16910
|
}
|
|
16578
16911
|
const yoloFlag = input.yolo ? " --dangerously-skip-permissions" : "";
|
|
16579
16912
|
if (input.launchMode === "resume") {
|
|
@@ -16585,6 +16918,47 @@ function buildAgentInvocation(input) {
|
|
|
16585
16918
|
}
|
|
16586
16919
|
return `claude${yoloFlag}${promptSuffix}`;
|
|
16587
16920
|
}
|
|
16921
|
+
function renderCustomCommandTemplate(template) {
|
|
16922
|
+
return template.replaceAll("${PROMPT}", `$${CUSTOM_AGENT_TEMPLATE_VARS.PROMPT}`).replaceAll("${SYSTEM_PROMPT}", `$${CUSTOM_AGENT_TEMPLATE_VARS.SYSTEM_PROMPT}`).replaceAll("${WORKTREE_PATH}", `$${CUSTOM_AGENT_TEMPLATE_VARS.WORKTREE_PATH}`).replaceAll("${REPO_PATH}", `$${CUSTOM_AGENT_TEMPLATE_VARS.REPO_PATH}`).replaceAll("${BRANCH}", `$${CUSTOM_AGENT_TEMPLATE_VARS.BRANCH}`).replaceAll("${PROFILE}", `$${CUSTOM_AGENT_TEMPLATE_VARS.PROFILE}`);
|
|
16923
|
+
}
|
|
16924
|
+
function buildCustomAgentExports(input) {
|
|
16925
|
+
const envEntries = [
|
|
16926
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.PROMPT, input.prompt ?? ""],
|
|
16927
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.SYSTEM_PROMPT, input.systemPrompt ?? ""],
|
|
16928
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.WORKTREE_PATH, input.worktreePath],
|
|
16929
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.REPO_PATH, input.repoRoot],
|
|
16930
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.BRANCH, input.branch],
|
|
16931
|
+
[CUSTOM_AGENT_TEMPLATE_VARS.PROFILE, input.profileName]
|
|
16932
|
+
];
|
|
16933
|
+
return envEntries.map(([key, value]) => `export ${key}=${quoteShell(value)}`).join("; ");
|
|
16934
|
+
}
|
|
16935
|
+
function buildCustomAgentInvocation(input) {
|
|
16936
|
+
const template = input.launchMode === "resume" && input.agent.implementation.config.resumeCommand ? input.agent.implementation.config.resumeCommand : input.agent.implementation.config.startCommand;
|
|
16937
|
+
const exports = buildCustomAgentExports(input);
|
|
16938
|
+
const renderedCommand = renderCustomCommandTemplate(template);
|
|
16939
|
+
return `${exports}; ${renderedCommand}`;
|
|
16940
|
+
}
|
|
16941
|
+
function buildAgentInvocation(input) {
|
|
16942
|
+
if (input.agent.kind === "builtin") {
|
|
16943
|
+
return buildBuiltInAgentInvocation({
|
|
16944
|
+
agent: input.agent.implementation.agent,
|
|
16945
|
+
yolo: input.yolo,
|
|
16946
|
+
systemPrompt: input.systemPrompt,
|
|
16947
|
+
prompt: input.prompt,
|
|
16948
|
+
launchMode: input.launchMode
|
|
16949
|
+
});
|
|
16950
|
+
}
|
|
16951
|
+
return buildCustomAgentInvocation({
|
|
16952
|
+
agent: input.agent,
|
|
16953
|
+
systemPrompt: input.systemPrompt,
|
|
16954
|
+
prompt: input.prompt,
|
|
16955
|
+
worktreePath: input.worktreePath,
|
|
16956
|
+
repoRoot: input.repoRoot,
|
|
16957
|
+
branch: input.branch,
|
|
16958
|
+
profileName: input.profileName,
|
|
16959
|
+
launchMode: input.launchMode
|
|
16960
|
+
});
|
|
16961
|
+
}
|
|
16588
16962
|
function buildAgentCommand(input, bootstrap = buildRuntimeBootstrap) {
|
|
16589
16963
|
return `${bootstrap(input.runtimeEnvPath)}; ${buildAgentInvocation(input)}`;
|
|
16590
16964
|
}
|
|
@@ -16603,10 +16977,112 @@ function buildDockerShellCommand(containerName2, worktreePath, runtimeEnvPath, s
|
|
|
16603
16977
|
function buildDockerAgentPaneCommand(input) {
|
|
16604
16978
|
return buildAgentCommand(input, buildDockerRuntimeBootstrap);
|
|
16605
16979
|
}
|
|
16606
|
-
var DOCKER_PATH_FALLBACK = "/root/.local/bin:/usr/local/bin:/root/.bun/bin:/root/.cargo/bin";
|
|
16980
|
+
var DOCKER_PATH_FALLBACK = "/root/.local/bin:/usr/local/bin:/root/.bun/bin:/root/.cargo/bin", CUSTOM_AGENT_TEMPLATE_VARS;
|
|
16981
|
+
var init_agent_service = __esm(() => {
|
|
16982
|
+
CUSTOM_AGENT_TEMPLATE_VARS = {
|
|
16983
|
+
PROMPT: "WEBMUX_AGENT_PROMPT",
|
|
16984
|
+
SYSTEM_PROMPT: "WEBMUX_AGENT_SYSTEM_PROMPT",
|
|
16985
|
+
WORKTREE_PATH: "WEBMUX_AGENT_WORKTREE_PATH",
|
|
16986
|
+
REPO_PATH: "WEBMUX_AGENT_REPO_PATH",
|
|
16987
|
+
BRANCH: "WEBMUX_AGENT_BRANCH",
|
|
16988
|
+
PROFILE: "WEBMUX_AGENT_PROFILE"
|
|
16989
|
+
};
|
|
16990
|
+
});
|
|
16991
|
+
|
|
16992
|
+
// backend/src/services/agent-registry.ts
|
|
16993
|
+
function cloneCapabilities(capabilities) {
|
|
16994
|
+
return { ...capabilities };
|
|
16995
|
+
}
|
|
16996
|
+
function cloneDefinition(definition) {
|
|
16997
|
+
if (definition.kind === "builtin") {
|
|
16998
|
+
return {
|
|
16999
|
+
...definition,
|
|
17000
|
+
capabilities: cloneCapabilities(definition.capabilities),
|
|
17001
|
+
implementation: { ...definition.implementation }
|
|
17002
|
+
};
|
|
17003
|
+
}
|
|
17004
|
+
return {
|
|
17005
|
+
...definition,
|
|
17006
|
+
capabilities: cloneCapabilities(definition.capabilities),
|
|
17007
|
+
implementation: {
|
|
17008
|
+
type: "custom",
|
|
17009
|
+
config: { ...definition.implementation.config }
|
|
17010
|
+
}
|
|
17011
|
+
};
|
|
17012
|
+
}
|
|
17013
|
+
function buildCustomAgentDefinition(id, config) {
|
|
17014
|
+
return {
|
|
17015
|
+
id,
|
|
17016
|
+
label: config.label,
|
|
17017
|
+
kind: "custom",
|
|
17018
|
+
capabilities: {
|
|
17019
|
+
terminal: true,
|
|
17020
|
+
inAppChat: false,
|
|
17021
|
+
conversationHistory: false,
|
|
17022
|
+
interrupt: false,
|
|
17023
|
+
resume: config.resumeCommand !== undefined
|
|
17024
|
+
},
|
|
17025
|
+
implementation: {
|
|
17026
|
+
type: "custom",
|
|
17027
|
+
config: { ...config }
|
|
17028
|
+
}
|
|
17029
|
+
};
|
|
17030
|
+
}
|
|
17031
|
+
function listAgentDefinitions(config) {
|
|
17032
|
+
const builtInIds = new Set(BUILTIN_AGENT_DEFINITIONS.map((agent) => agent.id));
|
|
17033
|
+
const customDefinitions = Object.entries(config.agents).filter(([id]) => !builtInIds.has(id)).sort(([leftId, left], [rightId, right]) => {
|
|
17034
|
+
const labelCompare = left.label.localeCompare(right.label);
|
|
17035
|
+
return labelCompare !== 0 ? labelCompare : leftId.localeCompare(rightId);
|
|
17036
|
+
}).map(([id, agent]) => buildCustomAgentDefinition(id, agent));
|
|
17037
|
+
return [
|
|
17038
|
+
...BUILTIN_AGENT_DEFINITIONS.map((definition) => cloneDefinition(definition)),
|
|
17039
|
+
...customDefinitions
|
|
17040
|
+
];
|
|
17041
|
+
}
|
|
17042
|
+
function getAgentDefinition(config, agentId) {
|
|
17043
|
+
const definition = listAgentDefinitions(config).find((agent) => agent.id === agentId);
|
|
17044
|
+
return definition ?? null;
|
|
17045
|
+
}
|
|
17046
|
+
var BUILTIN_AGENT_DEFINITIONS;
|
|
17047
|
+
var init_agent_registry = __esm(() => {
|
|
17048
|
+
BUILTIN_AGENT_DEFINITIONS = [
|
|
17049
|
+
{
|
|
17050
|
+
id: "claude",
|
|
17051
|
+
label: "Claude",
|
|
17052
|
+
kind: "builtin",
|
|
17053
|
+
capabilities: {
|
|
17054
|
+
terminal: true,
|
|
17055
|
+
inAppChat: true,
|
|
17056
|
+
conversationHistory: true,
|
|
17057
|
+
interrupt: true,
|
|
17058
|
+
resume: true
|
|
17059
|
+
},
|
|
17060
|
+
implementation: {
|
|
17061
|
+
type: "builtin",
|
|
17062
|
+
agent: "claude"
|
|
17063
|
+
}
|
|
17064
|
+
},
|
|
17065
|
+
{
|
|
17066
|
+
id: "codex",
|
|
17067
|
+
label: "Codex",
|
|
17068
|
+
kind: "builtin",
|
|
17069
|
+
capabilities: {
|
|
17070
|
+
terminal: true,
|
|
17071
|
+
inAppChat: true,
|
|
17072
|
+
conversationHistory: true,
|
|
17073
|
+
interrupt: true,
|
|
17074
|
+
resume: true
|
|
17075
|
+
},
|
|
17076
|
+
implementation: {
|
|
17077
|
+
type: "builtin",
|
|
17078
|
+
agent: "codex"
|
|
17079
|
+
}
|
|
17080
|
+
}
|
|
17081
|
+
];
|
|
17082
|
+
});
|
|
16607
17083
|
|
|
16608
17084
|
// backend/src/services/session-service.ts
|
|
16609
|
-
import { resolve as
|
|
17085
|
+
import { resolve as resolve7 } from "path";
|
|
16610
17086
|
function quoteShell2(value) {
|
|
16611
17087
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
16612
17088
|
}
|
|
@@ -16620,7 +17096,7 @@ function buildCommandPaneStartupCommand(template, ctx) {
|
|
|
16620
17096
|
if (!template.workingDir) {
|
|
16621
17097
|
return template.command;
|
|
16622
17098
|
}
|
|
16623
|
-
const workingDir =
|
|
17099
|
+
const workingDir = resolve7(resolvePaneCwd(template, ctx), template.workingDir);
|
|
16624
17100
|
return `cd -- ${quoteShell2(workingDir)} && ${template.command}`;
|
|
16625
17101
|
}
|
|
16626
17102
|
function resolvePaneStartupCommand(template, ctx) {
|
|
@@ -16850,7 +17326,7 @@ var init_worktree_service = __esm(() => {
|
|
|
16850
17326
|
|
|
16851
17327
|
// backend/src/services/lifecycle-service.ts
|
|
16852
17328
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
16853
|
-
import { dirname as dirname5, resolve as
|
|
17329
|
+
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
16854
17330
|
function toErrorMessage2(error) {
|
|
16855
17331
|
return error instanceof Error ? error.message : String(error);
|
|
16856
17332
|
}
|
|
@@ -16880,14 +17356,15 @@ function buildRuntimeControlBaseUrl(controlBaseUrl, runtime) {
|
|
|
16880
17356
|
function prefixAgentBranch(agent, branch) {
|
|
16881
17357
|
return `${agent}-${branch}`;
|
|
16882
17358
|
}
|
|
16883
|
-
function buildCreateWorktreeTargets(branch,
|
|
16884
|
-
if (
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
{ branch: prefixAgentBranch("codex", branch), agent: "codex" }
|
|
16888
|
-
];
|
|
17359
|
+
function buildCreateWorktreeTargets(branch, agentIds) {
|
|
17360
|
+
if (agentIds.length <= 1) {
|
|
17361
|
+
const agent = agentIds[0];
|
|
17362
|
+
return agent ? [{ branch, agent }] : [];
|
|
16889
17363
|
}
|
|
16890
|
-
return
|
|
17364
|
+
return agentIds.map((agent) => ({
|
|
17365
|
+
branch: prefixAgentBranch(agent, branch),
|
|
17366
|
+
agent
|
|
17367
|
+
}));
|
|
16891
17368
|
}
|
|
16892
17369
|
|
|
16893
17370
|
class LifecycleService {
|
|
@@ -16897,12 +17374,12 @@ class LifecycleService {
|
|
|
16897
17374
|
}
|
|
16898
17375
|
async createWorktrees(input) {
|
|
16899
17376
|
const mode = input.mode ?? "new";
|
|
16900
|
-
const
|
|
16901
|
-
if (
|
|
16902
|
-
throw new LifecycleError("Creating
|
|
17377
|
+
const agentIds = this.resolveSelectedAgents(input);
|
|
17378
|
+
if (agentIds.length > 1 && mode === "existing") {
|
|
17379
|
+
throw new LifecycleError("Creating multiple agents is only supported for new worktrees", 400);
|
|
16903
17380
|
}
|
|
16904
17381
|
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
16905
|
-
const targets = buildCreateWorktreeTargets(branch,
|
|
17382
|
+
const targets = buildCreateWorktreeTargets(branch, agentIds);
|
|
16906
17383
|
const createdBranches = [];
|
|
16907
17384
|
try {
|
|
16908
17385
|
for (const target of targets) {
|
|
@@ -16929,28 +17406,30 @@ class LifecycleService {
|
|
|
16929
17406
|
async createWorktree(input) {
|
|
16930
17407
|
const mode = input.mode ?? "new";
|
|
16931
17408
|
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
16932
|
-
const agent = this.
|
|
17409
|
+
const agent = this.resolveAgentDefinition(input.agent);
|
|
16933
17410
|
return await this.createResolvedWorktree({
|
|
16934
17411
|
...input,
|
|
16935
17412
|
mode,
|
|
16936
17413
|
branch,
|
|
16937
|
-
agent
|
|
17414
|
+
agent: agent.id
|
|
16938
17415
|
});
|
|
16939
17416
|
}
|
|
16940
17417
|
async openWorktree(branch) {
|
|
16941
17418
|
try {
|
|
16942
17419
|
const resolved = await this.resolveExistingWorktree(branch);
|
|
16943
|
-
const launchMode = resolved.meta ? "resume" : "fresh";
|
|
16944
17420
|
const initialized = resolved.meta ? await this.refreshManagedArtifacts(resolved) : await this.initializeUnmanagedWorktree(resolved);
|
|
16945
|
-
const { profile } = this.resolveProfile(initialized.meta.profile);
|
|
17421
|
+
const { profileName, profile } = this.resolveProfile(initialized.meta.profile);
|
|
17422
|
+
const agent = this.resolveAgentDefinition(initialized.meta.agent);
|
|
17423
|
+
const launchMode = resolved.meta && agent.capabilities.resume ? "resume" : "fresh";
|
|
16946
17424
|
await ensureAgentRuntimeArtifacts({
|
|
16947
17425
|
gitDir: initialized.paths.gitDir,
|
|
16948
17426
|
worktreePath: resolved.entry.path
|
|
16949
17427
|
});
|
|
16950
17428
|
await this.materializeRuntimeSession({
|
|
16951
17429
|
branch,
|
|
17430
|
+
profileName,
|
|
16952
17431
|
profile,
|
|
16953
|
-
agent
|
|
17432
|
+
agent,
|
|
16954
17433
|
initialized,
|
|
16955
17434
|
worktreePath: resolved.entry.path,
|
|
16956
17435
|
launchMode
|
|
@@ -17084,14 +17563,22 @@ class LifecycleService {
|
|
|
17084
17563
|
profile
|
|
17085
17564
|
};
|
|
17086
17565
|
}
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
|
|
17090
|
-
if (agent
|
|
17091
|
-
throw new LifecycleError(`Unknown agent: ${
|
|
17566
|
+
resolveAgentDefinition(agentId) {
|
|
17567
|
+
const resolvedAgentId = agentId ?? this.deps.config.workspace.defaultAgent;
|
|
17568
|
+
const agent = getAgentDefinition(this.deps.config, resolvedAgentId);
|
|
17569
|
+
if (!agent) {
|
|
17570
|
+
throw new LifecycleError(`Unknown agent: ${resolvedAgentId}`, 400);
|
|
17092
17571
|
}
|
|
17093
17572
|
return agent;
|
|
17094
17573
|
}
|
|
17574
|
+
resolveSelectedAgents(input) {
|
|
17575
|
+
const selectedAgents = input.agents && input.agents.length > 0 ? input.agents : [input.agent ?? this.deps.config.workspace.defaultAgent];
|
|
17576
|
+
const dedupedAgentIds = [...new Set(selectedAgents.map((agent) => agent.trim()).filter((agent) => agent.length > 0))];
|
|
17577
|
+
if (dedupedAgentIds.length === 0) {
|
|
17578
|
+
throw new LifecycleError("At least one agent must be selected", 400);
|
|
17579
|
+
}
|
|
17580
|
+
return dedupedAgentIds.map((agentId) => this.resolveAgentDefinition(agentId).id);
|
|
17581
|
+
}
|
|
17095
17582
|
async buildStartupEnvValues(envOverrides) {
|
|
17096
17583
|
const startupEnvValues = Object.fromEntries(Object.entries(this.deps.config.startupEnvs).map(([key, value]) => [key, stringifyStartupEnvValue(value)]));
|
|
17097
17584
|
for (const [key, value] of Object.entries(envOverrides ?? {})) {
|
|
@@ -17107,20 +17594,20 @@ class LifecycleService {
|
|
|
17107
17594
|
return allocateServicePorts(metas, this.deps.config.services);
|
|
17108
17595
|
}
|
|
17109
17596
|
resolveWorktreePath(branch) {
|
|
17110
|
-
return
|
|
17597
|
+
return resolve8(this.deps.projectRoot, this.deps.config.workspace.worktreeRoot, branch);
|
|
17111
17598
|
}
|
|
17112
17599
|
listLocalBranches() {
|
|
17113
|
-
return this.deps.git.listLocalBranches(
|
|
17600
|
+
return this.deps.git.listLocalBranches(resolve8(this.deps.projectRoot));
|
|
17114
17601
|
}
|
|
17115
17602
|
listRemoteBranches() {
|
|
17116
|
-
return this.deps.git.listRemoteBranches(
|
|
17603
|
+
return this.deps.git.listRemoteBranches(resolve8(this.deps.projectRoot));
|
|
17117
17604
|
}
|
|
17118
17605
|
listCheckedOutBranches() {
|
|
17119
|
-
return new Set(this.deps.git.listWorktrees(
|
|
17606
|
+
return new Set(this.deps.git.listWorktrees(resolve8(this.deps.projectRoot)).filter((entry) => !entry.bare && entry.branch !== null).map((entry) => entry.branch));
|
|
17120
17607
|
}
|
|
17121
17608
|
listProjectWorktrees() {
|
|
17122
|
-
const projectRoot2 =
|
|
17123
|
-
return this.deps.git.listWorktrees(projectRoot2).filter((entry) => !entry.bare &&
|
|
17609
|
+
const projectRoot2 = resolve8(this.deps.projectRoot);
|
|
17610
|
+
return this.deps.git.listWorktrees(projectRoot2).filter((entry) => !entry.bare && resolve8(entry.path) !== projectRoot2);
|
|
17124
17611
|
}
|
|
17125
17612
|
async readManagedMetas() {
|
|
17126
17613
|
const metas = await Promise.all(this.listProjectWorktrees().map(async (entry) => {
|
|
@@ -17213,6 +17700,7 @@ class LifecycleService {
|
|
|
17213
17700
|
});
|
|
17214
17701
|
ensureSessionLayout(this.deps.tmux, this.buildSessionLayout({
|
|
17215
17702
|
branch: input.branch,
|
|
17703
|
+
profileName: input.profileName,
|
|
17216
17704
|
profile: input.profile,
|
|
17217
17705
|
agent: input.agent,
|
|
17218
17706
|
initialized: input.initialized,
|
|
@@ -17225,6 +17713,7 @@ class LifecycleService {
|
|
|
17225
17713
|
}
|
|
17226
17714
|
ensureSessionLayout(this.deps.tmux, this.buildSessionLayout({
|
|
17227
17715
|
branch: input.branch,
|
|
17716
|
+
profileName: input.profileName,
|
|
17228
17717
|
profile: input.profile,
|
|
17229
17718
|
agent: input.agent,
|
|
17230
17719
|
initialized: input.initialized,
|
|
@@ -17243,6 +17732,10 @@ class LifecycleService {
|
|
|
17243
17732
|
agent: buildDockerAgentPaneCommand({
|
|
17244
17733
|
agent: input.agent,
|
|
17245
17734
|
runtimeEnvPath: input.initialized.paths.runtimeEnvPath,
|
|
17735
|
+
repoRoot: this.deps.projectRoot,
|
|
17736
|
+
worktreePath: input.worktreePath,
|
|
17737
|
+
branch: input.branch,
|
|
17738
|
+
profileName: input.profileName,
|
|
17246
17739
|
yolo: input.profile.yolo === true,
|
|
17247
17740
|
systemPrompt,
|
|
17248
17741
|
prompt: input.launchMode === "fresh" ? input.prompt : undefined,
|
|
@@ -17253,6 +17746,10 @@ class LifecycleService {
|
|
|
17253
17746
|
agent: buildAgentPaneCommand({
|
|
17254
17747
|
agent: input.agent,
|
|
17255
17748
|
runtimeEnvPath: input.initialized.paths.runtimeEnvPath,
|
|
17749
|
+
repoRoot: this.deps.projectRoot,
|
|
17750
|
+
worktreePath: input.worktreePath,
|
|
17751
|
+
branch: input.branch,
|
|
17752
|
+
profileName: input.profileName,
|
|
17256
17753
|
yolo: input.profile.yolo === true,
|
|
17257
17754
|
systemPrompt,
|
|
17258
17755
|
prompt: input.launchMode === "fresh" ? input.prompt : undefined,
|
|
@@ -17377,6 +17874,7 @@ class LifecycleService {
|
|
|
17377
17874
|
const baseBranch = input.mode === "new" ? requestedBaseBranch || this.deps.config.workspace.mainBranch : undefined;
|
|
17378
17875
|
const branchAvailability = this.resolveBranchAvailability(input.branch, input.mode);
|
|
17379
17876
|
const { profileName, profile } = this.resolveProfile(input.profile);
|
|
17877
|
+
const agent = this.resolveAgentDefinition(input.agent);
|
|
17380
17878
|
const worktreePath = this.resolveWorktreePath(input.branch);
|
|
17381
17879
|
const createProgressBase = {
|
|
17382
17880
|
branch: input.branch,
|
|
@@ -17401,7 +17899,7 @@ class LifecycleService {
|
|
|
17401
17899
|
...baseBranch ? { baseBranch } : {},
|
|
17402
17900
|
...branchAvailability.startPoint ? { startPoint: branchAvailability.startPoint } : {},
|
|
17403
17901
|
profile: profileName,
|
|
17404
|
-
agent:
|
|
17902
|
+
agent: agent.id,
|
|
17405
17903
|
runtime: profile.runtime,
|
|
17406
17904
|
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
17407
17905
|
allocatedPorts: await this.allocatePorts(),
|
|
@@ -17441,8 +17939,9 @@ class LifecycleService {
|
|
|
17441
17939
|
});
|
|
17442
17940
|
await this.materializeRuntimeSession({
|
|
17443
17941
|
branch: input.branch,
|
|
17942
|
+
profileName,
|
|
17444
17943
|
profile,
|
|
17445
|
-
agent
|
|
17944
|
+
agent,
|
|
17446
17945
|
initialized,
|
|
17447
17946
|
worktreePath,
|
|
17448
17947
|
prompt: input.prompt,
|
|
@@ -17483,6 +17982,8 @@ var init_lifecycle_service = __esm(() => {
|
|
|
17483
17982
|
init_config();
|
|
17484
17983
|
init_tmux();
|
|
17485
17984
|
init_policies();
|
|
17985
|
+
init_agent_service();
|
|
17986
|
+
init_agent_registry();
|
|
17486
17987
|
init_session_service();
|
|
17487
17988
|
init_worktree_service();
|
|
17488
17989
|
init_log();
|
|
@@ -17778,9 +18279,9 @@ async function mapWithConcurrency(items, limit, fn) {
|
|
|
17778
18279
|
}
|
|
17779
18280
|
|
|
17780
18281
|
// backend/src/services/reconciliation-service.ts
|
|
17781
|
-
import { basename as basename4, resolve as
|
|
18282
|
+
import { basename as basename4, resolve as resolve9 } from "path";
|
|
17782
18283
|
function makeUnmanagedWorktreeId(path) {
|
|
17783
|
-
return `unmanaged:${
|
|
18284
|
+
return `unmanaged:${resolve9(path)}`;
|
|
17784
18285
|
}
|
|
17785
18286
|
function isValidPort2(port) {
|
|
17786
18287
|
return port !== null && Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
@@ -17837,7 +18338,7 @@ class ReconciliationService {
|
|
|
17837
18338
|
if (!options.force && this.now() - this.lastReconciledAt < this.freshnessMs) {
|
|
17838
18339
|
return;
|
|
17839
18340
|
}
|
|
17840
|
-
const normalizedRepoRoot =
|
|
18341
|
+
const normalizedRepoRoot = resolve9(repoRoot);
|
|
17841
18342
|
const reconcilePromise = this.runReconcile(normalizedRepoRoot).then(() => {
|
|
17842
18343
|
this.lastReconciledAt = this.now();
|
|
17843
18344
|
});
|
|
@@ -17856,7 +18357,7 @@ class ReconciliationService {
|
|
|
17856
18357
|
windows = [];
|
|
17857
18358
|
}
|
|
17858
18359
|
const seenWorktreeIds = new Set;
|
|
17859
|
-
const candidateEntries = worktrees.filter((entry) => !entry.bare &&
|
|
18360
|
+
const candidateEntries = worktrees.filter((entry) => !entry.bare && resolve9(entry.path) !== normalizedRepoRoot);
|
|
17860
18361
|
const reconciledStates = await mapWithConcurrency(candidateEntries, this.concurrency, async (entry) => {
|
|
17861
18362
|
const gitDir = this.deps.git.resolveWorktreeGitDir(entry.path);
|
|
17862
18363
|
const meta = await readWorktreeMeta(gitDir);
|
|
@@ -18042,19 +18543,19 @@ __export(exports_worktree_commands, {
|
|
|
18042
18543
|
parseAddCommandArgs: () => parseAddCommandArgs,
|
|
18043
18544
|
getWorktreeCommandUsage: () => getWorktreeCommandUsage
|
|
18044
18545
|
});
|
|
18045
|
-
import { basename as basename5, resolve as
|
|
18546
|
+
import { basename as basename5, resolve as resolve10 } from "path";
|
|
18046
18547
|
function getWorktreeCommandUsage(command) {
|
|
18047
18548
|
switch (command) {
|
|
18048
18549
|
case "add":
|
|
18049
18550
|
return [
|
|
18050
18551
|
"Usage:",
|
|
18051
|
-
" webmux add [branch] [--existing] [--base <branch>] [--profile <name>] [--agent <
|
|
18552
|
+
" webmux add [branch] [--existing] [--base <branch>] [--profile <name>] [--agent <id>] [--prompt <text>] [--env KEY=VALUE] [--detach]",
|
|
18052
18553
|
"",
|
|
18053
18554
|
"Options:",
|
|
18054
18555
|
" --existing Use an existing local or remote branch instead of creating a new one",
|
|
18055
18556
|
" --base <branch> Base branch for a new worktree (defaults to config)",
|
|
18056
18557
|
" --profile <name> Worktree profile from .webmux.yaml",
|
|
18057
|
-
" --agent <
|
|
18558
|
+
" --agent <id> Agent id to launch (repeatable)",
|
|
18058
18559
|
" --prompt <text> Initial agent prompt",
|
|
18059
18560
|
" --env KEY=VALUE Runtime env override (repeatable)",
|
|
18060
18561
|
" -d, --detach Create worktree without switching to it",
|
|
@@ -18129,14 +18630,16 @@ function readOptionValue(args, index, flag) {
|
|
|
18129
18630
|
};
|
|
18130
18631
|
}
|
|
18131
18632
|
function parseAgent(value) {
|
|
18132
|
-
|
|
18133
|
-
|
|
18633
|
+
const trimmed = value.trim();
|
|
18634
|
+
if (!trimmed) {
|
|
18635
|
+
throw new CommandUsageError("Agent id cannot be empty");
|
|
18134
18636
|
}
|
|
18135
|
-
|
|
18637
|
+
return trimmed;
|
|
18136
18638
|
}
|
|
18137
18639
|
function parseAddCommandArgs(args) {
|
|
18138
18640
|
const input = {};
|
|
18139
18641
|
const envOverrides = {};
|
|
18642
|
+
const selectedAgents = [];
|
|
18140
18643
|
let detach = false;
|
|
18141
18644
|
for (let index = 0;index < args.length; index++) {
|
|
18142
18645
|
const arg = args[index];
|
|
@@ -18167,7 +18670,7 @@ function parseAddCommandArgs(args) {
|
|
|
18167
18670
|
}
|
|
18168
18671
|
if (arg === "--agent" || arg.startsWith("--agent=")) {
|
|
18169
18672
|
const { value, nextIndex } = readOptionValue(args, index, "--agent");
|
|
18170
|
-
|
|
18673
|
+
selectedAgents.push(parseAgent(value));
|
|
18171
18674
|
index = nextIndex;
|
|
18172
18675
|
continue;
|
|
18173
18676
|
}
|
|
@@ -18195,6 +18698,9 @@ function parseAddCommandArgs(args) {
|
|
|
18195
18698
|
}
|
|
18196
18699
|
input.branch = arg;
|
|
18197
18700
|
}
|
|
18701
|
+
if (selectedAgents.length > 0) {
|
|
18702
|
+
input.agents = selectedAgents;
|
|
18703
|
+
}
|
|
18198
18704
|
if (Object.keys(envOverrides).length > 0) {
|
|
18199
18705
|
input.envOverrides = envOverrides;
|
|
18200
18706
|
}
|
|
@@ -18318,8 +18824,8 @@ function parseListCommandArgs(args) {
|
|
|
18318
18824
|
return { mode, search };
|
|
18319
18825
|
}
|
|
18320
18826
|
function listProjectWorktrees(runtime) {
|
|
18321
|
-
const projectDir =
|
|
18322
|
-
return runtime.git.listWorktrees(projectDir).filter((entry) => !entry.bare &&
|
|
18827
|
+
const projectDir = resolve10(runtime.projectDir);
|
|
18828
|
+
return runtime.git.listWorktrees(projectDir).filter((entry) => !entry.bare && resolve10(entry.path) !== projectDir);
|
|
18323
18829
|
}
|
|
18324
18830
|
async function defaultConfirmPrune(worktreeCount) {
|
|
18325
18831
|
const response = await ot2({
|
|
@@ -18329,7 +18835,7 @@ async function defaultConfirmPrune(worktreeCount) {
|
|
|
18329
18835
|
return !q(response) && response;
|
|
18330
18836
|
}
|
|
18331
18837
|
function defaultSwitchToTmuxWindow(projectDir, branch) {
|
|
18332
|
-
const sessionName = buildProjectSessionName(
|
|
18838
|
+
const sessionName = buildProjectSessionName(resolve10(projectDir));
|
|
18333
18839
|
const windowName = buildWorktreeWindowName(branch);
|
|
18334
18840
|
const target = `${sessionName}:${windowName}`;
|
|
18335
18841
|
const selectResult = Bun.spawnSync(["tmux", "select-window", "-t", target], {
|
|
@@ -18361,7 +18867,7 @@ function matchesListSearch(row, query) {
|
|
|
18361
18867
|
return query.length === 0 || row.searchText.toLowerCase().includes(query.toLowerCase());
|
|
18362
18868
|
}
|
|
18363
18869
|
async function listWorktrees(runtime, stdout, options) {
|
|
18364
|
-
const projectDir =
|
|
18870
|
+
const projectDir = resolve10(runtime.projectDir);
|
|
18365
18871
|
const entries = listProjectWorktrees(runtime);
|
|
18366
18872
|
if (entries.length === 0) {
|
|
18367
18873
|
stdout("No worktrees found.");
|
|
@@ -18386,7 +18892,7 @@ async function listWorktrees(runtime, stdout, options) {
|
|
|
18386
18892
|
return {
|
|
18387
18893
|
branch,
|
|
18388
18894
|
isOpen,
|
|
18389
|
-
archived: archivedPaths.has(
|
|
18895
|
+
archived: archivedPaths.has(resolve10(entry.path)),
|
|
18390
18896
|
info,
|
|
18391
18897
|
searchText: [
|
|
18392
18898
|
branch,
|
|
@@ -18452,21 +18958,12 @@ async function runWorktreeCommand(context, deps2 = {}) {
|
|
|
18452
18958
|
if (!parsed.input.branch && parsed.input.prompt && runtime2.config.autoName) {
|
|
18453
18959
|
stdout("Generating branch name...");
|
|
18454
18960
|
}
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
switchToTmuxWindow(runtime2.projectDir, result.primaryBranch);
|
|
18462
|
-
}
|
|
18463
|
-
} else {
|
|
18464
|
-
const { agent, ...rest } = parsed.input;
|
|
18465
|
-
const result = await runtime2.lifecycleService.createWorktree({ ...rest, agent });
|
|
18466
|
-
stdout(`Created worktree ${result.branch}`);
|
|
18467
|
-
if (!parsed.detach) {
|
|
18468
|
-
switchToTmuxWindow(runtime2.projectDir, result.branch);
|
|
18469
|
-
}
|
|
18961
|
+
const result = await runtime2.lifecycleService.createWorktrees(parsed.input);
|
|
18962
|
+
for (const branch2 of result.branches) {
|
|
18963
|
+
stdout(`Created worktree ${branch2}`);
|
|
18964
|
+
}
|
|
18965
|
+
if (!parsed.detach) {
|
|
18966
|
+
switchToTmuxWindow(runtime2.projectDir, result.primaryBranch);
|
|
18470
18967
|
}
|
|
18471
18968
|
return 0;
|
|
18472
18969
|
}
|
|
@@ -18599,13 +19096,13 @@ var init_worktree_commands = __esm(() => {
|
|
|
18599
19096
|
});
|
|
18600
19097
|
|
|
18601
19098
|
// bin/src/webmux.ts
|
|
18602
|
-
import { resolve as
|
|
19099
|
+
import { resolve as resolve11, dirname as dirname6, join as join11 } from "path";
|
|
18603
19100
|
import { existsSync as existsSync5 } from "fs";
|
|
18604
19101
|
import { fileURLToPath } from "url";
|
|
18605
19102
|
// package.json
|
|
18606
19103
|
var package_default = {
|
|
18607
19104
|
name: "webmux",
|
|
18608
|
-
version: "0.
|
|
19105
|
+
version: "0.30.0",
|
|
18609
19106
|
description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
|
|
18610
19107
|
type: "module",
|
|
18611
19108
|
repository: {
|
|
@@ -18629,9 +19126,7 @@ var package_default = {
|
|
|
18629
19126
|
"frontend",
|
|
18630
19127
|
"packages/*"
|
|
18631
19128
|
],
|
|
18632
|
-
dependencies: {
|
|
18633
|
-
"@webmux/api-contract": "workspace:*"
|
|
18634
|
-
},
|
|
19129
|
+
dependencies: {},
|
|
18635
19130
|
scripts: {
|
|
18636
19131
|
dev: "bash dev.sh",
|
|
18637
19132
|
start: "bun bin/webmux.js",
|
|
@@ -18646,6 +19141,7 @@ var package_default = {
|
|
|
18646
19141
|
"frontend/dist/"
|
|
18647
19142
|
],
|
|
18648
19143
|
devDependencies: {
|
|
19144
|
+
"@webmux/api-contract": "workspace:*",
|
|
18649
19145
|
"@clack/prompts": "^1.1.0",
|
|
18650
19146
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
18651
19147
|
"@tailwindcss/vite": "^4.2.0",
|
|
@@ -18663,7 +19159,7 @@ var package_default = {
|
|
|
18663
19159
|
};
|
|
18664
19160
|
|
|
18665
19161
|
// bin/src/webmux.ts
|
|
18666
|
-
var PKG_ROOT =
|
|
19162
|
+
var PKG_ROOT = resolve11(dirname6(fileURLToPath(import.meta.url)), "..");
|
|
18667
19163
|
function usage2() {
|
|
18668
19164
|
console.log(`
|
|
18669
19165
|
webmux \u2014 Dev dashboard for managing Git worktrees
|
|
@@ -18878,8 +19374,8 @@ async function main(args = process.argv.slice(2)) {
|
|
|
18878
19374
|
const code = await proc.exited;
|
|
18879
19375
|
process.exit(code);
|
|
18880
19376
|
}
|
|
18881
|
-
await loadEnvFile(
|
|
18882
|
-
await loadEnvFile(
|
|
19377
|
+
await loadEnvFile(resolve11(process.cwd(), ".env.local"));
|
|
19378
|
+
await loadEnvFile(resolve11(process.cwd(), ".env"));
|
|
18883
19379
|
if (isWorktreeCommand(parsed.command)) {
|
|
18884
19380
|
const { runWorktreeCommand: runWorktreeCommand2 } = await Promise.resolve().then(() => (init_worktree_commands(), exports_worktree_commands));
|
|
18885
19381
|
const exitCode2 = await runWorktreeCommand2({
|
|
@@ -18894,7 +19390,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
18894
19390
|
usage2();
|
|
18895
19391
|
process.exit(0);
|
|
18896
19392
|
}
|
|
18897
|
-
if (!existsSync5(
|
|
19393
|
+
if (!existsSync5(resolve11(process.cwd(), ".webmux.yaml"))) {
|
|
18898
19394
|
console.error("No .webmux.yaml found in this directory.\nRun `webmux init` to set up your project.");
|
|
18899
19395
|
process.exit(1);
|
|
18900
19396
|
}
|