@proletariat/cli 0.3.47 → 0.3.49
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/commands/caffeinate/index.d.ts +10 -0
- package/dist/commands/caffeinate/index.js +64 -0
- package/dist/commands/caffeinate/start.d.ts +14 -0
- package/dist/commands/caffeinate/start.js +86 -0
- package/dist/commands/caffeinate/status.d.ts +10 -0
- package/dist/commands/caffeinate/status.js +55 -0
- package/dist/commands/caffeinate/stop.d.ts +10 -0
- package/dist/commands/caffeinate/stop.js +47 -0
- package/dist/commands/claude/index.js +21 -21
- package/dist/commands/claude/open.js +1 -1
- package/dist/commands/commit.js +10 -8
- package/dist/commands/config/index.js +4 -5
- package/dist/commands/execution/config.d.ts +2 -2
- package/dist/commands/execution/config.js +18 -18
- package/dist/commands/execution/list.js +2 -2
- package/dist/commands/execution/view.js +2 -2
- package/dist/commands/init.js +9 -1
- package/dist/commands/orchestrator/attach.js +64 -14
- package/dist/commands/orchestrator/start.d.ts +5 -5
- package/dist/commands/orchestrator/start.js +45 -35
- package/dist/commands/orchestrator/status.js +64 -23
- package/dist/commands/orchestrator/stop.js +44 -12
- package/dist/commands/qa/index.js +12 -12
- package/dist/commands/session/attach.js +23 -0
- package/dist/commands/session/poke.js +1 -1
- package/dist/commands/staff/add.js +1 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/linear.d.ts +24 -0
- package/dist/commands/work/linear.js +218 -0
- package/dist/commands/work/revise.js +8 -8
- package/dist/commands/work/spawn.js +29 -20
- package/dist/commands/work/start.js +22 -12
- package/dist/commands/work/watch.js +3 -3
- package/dist/hooks/init.js +8 -0
- package/dist/lib/agents/index.js +2 -2
- package/dist/lib/caffeinate.d.ts +64 -0
- package/dist/lib/caffeinate.js +146 -0
- package/dist/lib/database/drizzle-schema.d.ts +7 -7
- package/dist/lib/database/drizzle-schema.js +1 -1
- package/dist/lib/execution/codex-adapter.d.ts +96 -0
- package/dist/lib/execution/codex-adapter.js +148 -0
- package/dist/lib/execution/config.d.ts +6 -6
- package/dist/lib/execution/config.js +17 -10
- package/dist/lib/execution/devcontainer.d.ts +3 -3
- package/dist/lib/execution/devcontainer.js +3 -3
- package/dist/lib/execution/index.d.ts +1 -0
- package/dist/lib/execution/index.js +1 -0
- package/dist/lib/execution/runners.d.ts +2 -2
- package/dist/lib/execution/runners.js +69 -26
- package/dist/lib/execution/spawner.js +3 -3
- package/dist/lib/execution/storage.d.ts +2 -2
- package/dist/lib/execution/storage.js +3 -3
- package/dist/lib/execution/types.d.ts +2 -2
- package/dist/lib/execution/types.js +1 -1
- package/dist/lib/external-issues/index.d.ts +1 -1
- package/dist/lib/external-issues/index.js +1 -1
- package/dist/lib/external-issues/linear.d.ts +43 -0
- package/dist/lib/external-issues/linear.js +261 -0
- package/dist/lib/external-issues/types.d.ts +67 -0
- package/dist/lib/external-issues/types.js +41 -0
- package/dist/lib/init/index.d.ts +4 -0
- package/dist/lib/init/index.js +11 -1
- package/dist/lib/machine-config.d.ts +1 -0
- package/dist/lib/machine-config.js +6 -3
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -1
- package/dist/lib/pmo/storage/actions.js +3 -3
- package/dist/lib/pmo/storage/base.js +116 -6
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/tickets.js +2 -2
- package/dist/lib/pmo/storage/types.d.ts +2 -1
- package/dist/lib/repos/index.js +1 -1
- package/oclif.manifest.json +3052 -2721
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Devcontainer Template Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates .devcontainer/ configuration for agent
|
|
5
|
-
* Uses a custom Dockerfile with network firewall for security
|
|
4
|
+
* Generates .devcontainer/ configuration for agent isolated execution.
|
|
5
|
+
* Uses a custom Dockerfile with network firewall for security isolation.
|
|
6
6
|
*/
|
|
7
7
|
import { ExecutionConfig, ExecutorType } from './types.js';
|
|
8
8
|
export type MountMode = 'worktree' | 'clone';
|
|
@@ -48,7 +48,7 @@ export interface DevcontainerJson {
|
|
|
48
48
|
/**
|
|
49
49
|
* Generate default devcontainer.json content
|
|
50
50
|
*
|
|
51
|
-
* Uses a custom Dockerfile with firewall for network
|
|
51
|
+
* Uses a custom Dockerfile with firewall for network isolation.
|
|
52
52
|
* Mounts the entire agent workspace directory so all contents (repos, prompt files, etc.)
|
|
53
53
|
* are accessible inside the container at /workspace.
|
|
54
54
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Devcontainer Template Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates .devcontainer/ configuration for agent
|
|
5
|
-
* Uses a custom Dockerfile with network firewall for security
|
|
4
|
+
* Generates .devcontainer/ configuration for agent isolated execution.
|
|
5
|
+
* Uses a custom Dockerfile with network firewall for security isolation.
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'node:fs';
|
|
8
8
|
import * as path from 'node:path';
|
|
@@ -11,7 +11,7 @@ import { parseChannel } from '../workspace-config.js';
|
|
|
11
11
|
/**
|
|
12
12
|
* Generate default devcontainer.json content
|
|
13
13
|
*
|
|
14
|
-
* Uses a custom Dockerfile with firewall for network
|
|
14
|
+
* Uses a custom Dockerfile with firewall for network isolation.
|
|
15
15
|
* Mounts the entire agent workspace directory so all contents (repos, prompt files, etc.)
|
|
16
16
|
* are accessible inside the container at /workspace.
|
|
17
17
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implementations for each execution environment (devcontainer, host, docker, vm).
|
|
5
5
|
*/
|
|
6
|
-
import { ExecutionEnvironment, DisplayMode, OutputMode, SessionManager, ExecutorType, ExecutionContext, ExecutionConfig } from './types.js';
|
|
6
|
+
import { ExecutionEnvironment, DisplayMode, OutputMode, PermissionMode, SessionManager, ExecutorType, ExecutionContext, ExecutionConfig } from './types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Build a unified name for tmux sessions, window names, and tab titles.
|
|
9
9
|
* Format: "{ticketId}-{action}-{agentName}"
|
|
@@ -162,7 +162,7 @@ export declare function getContainerId(containerName: string): string | null;
|
|
|
162
162
|
* Uses docker exec for direct container access.
|
|
163
163
|
* Uses a prompt file to avoid shell escaping issues.
|
|
164
164
|
*/
|
|
165
|
-
export declare function buildDevcontainerCommand(context: ExecutionContext, executor: ExecutorType, promptFile: string, containerId?: string, outputMode?: OutputMode,
|
|
165
|
+
export declare function buildDevcontainerCommand(context: ExecutionContext, executor: ExecutorType, promptFile: string, containerId?: string, outputMode?: OutputMode, permissionMode?: PermissionMode, displayMode?: DisplayMode): string;
|
|
166
166
|
/**
|
|
167
167
|
* Run command inside a Docker container.
|
|
168
168
|
* Uses raw Docker commands for filesystem isolation - no devcontainer CLI required.
|
|
@@ -11,6 +11,7 @@ import * as os from 'node:os';
|
|
|
11
11
|
import { DEFAULT_EXECUTION_CONFIG, } from './types.js';
|
|
12
12
|
import { getSetTitleCommands } from '../terminal.js';
|
|
13
13
|
import { readDevcontainerJson } from './devcontainer.js';
|
|
14
|
+
import { getCodexCommand, resolveCodexExecutionContext, validateCodexMode } from './codex-adapter.js';
|
|
14
15
|
// =============================================================================
|
|
15
16
|
// Terminal Title Helpers
|
|
16
17
|
// =============================================================================
|
|
@@ -167,11 +168,16 @@ export function getExecutorCommand(executor, prompt, skipPermissions = true) {
|
|
|
167
168
|
}
|
|
168
169
|
// Manual mode - will prompt for each action (still interactive, no -p)
|
|
169
170
|
return { cmd: 'claude', args: [prompt] };
|
|
170
|
-
case 'codex':
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
case 'codex': {
|
|
172
|
+
// Delegate to Codex adapter for deterministic mode mapping.
|
|
173
|
+
// getExecutorCommand is called without display/output context, so we use
|
|
174
|
+
// 'interactive' as default context (safe for validation — all permission modes
|
|
175
|
+
// are valid with interactive). Runners that need stricter validation should
|
|
176
|
+
// call the adapter directly with the actual execution context.
|
|
177
|
+
const codexPermission = skipPermissions ? 'danger' : 'safe';
|
|
178
|
+
const codexResult = getCodexCommand(prompt, codexPermission, 'interactive');
|
|
179
|
+
return { cmd: codexResult.cmd, args: codexResult.args };
|
|
180
|
+
}
|
|
175
181
|
case 'aider':
|
|
176
182
|
return { cmd: 'aider', args: ['--message', prompt] };
|
|
177
183
|
case 'custom':
|
|
@@ -380,8 +386,17 @@ export async function runHost(context, executor, config, displayMode = 'terminal
|
|
|
380
386
|
const sessionName = buildTmuxWindowName(context);
|
|
381
387
|
const windowTitle = buildWindowTitle(context);
|
|
382
388
|
const prompt = buildPrompt(context);
|
|
383
|
-
// Terminal - use
|
|
384
|
-
const skipPermissions =
|
|
389
|
+
// Terminal - use permission mode setting
|
|
390
|
+
const skipPermissions = config.permissionMode === 'danger';
|
|
391
|
+
// Validate Codex mode combination before proceeding
|
|
392
|
+
if (executor === 'codex') {
|
|
393
|
+
const codexPermission = config.permissionMode;
|
|
394
|
+
const codexContext = resolveCodexExecutionContext(displayMode, config.outputMode);
|
|
395
|
+
const modeError = validateCodexMode(codexPermission, codexContext);
|
|
396
|
+
if (modeError) {
|
|
397
|
+
return { success: false, error: modeError.message };
|
|
398
|
+
}
|
|
399
|
+
}
|
|
385
400
|
const { cmd, args } = getExecutorCommand(executor, prompt, skipPermissions);
|
|
386
401
|
// Write command to temp script to avoid shell escaping issues
|
|
387
402
|
// Use HQ .proletariat/scripts if available, otherwise fallback to home dir
|
|
@@ -919,7 +934,7 @@ function createDockerContainer(context, containerName, imageName, config, execut
|
|
|
919
934
|
`--memory=${config.devcontainer.memory}`,
|
|
920
935
|
`--cpus=${config.devcontainer.cpus}`,
|
|
921
936
|
];
|
|
922
|
-
// Security flags - these provide the
|
|
937
|
+
// Security flags - these provide the isolation
|
|
923
938
|
const securityFlags = [
|
|
924
939
|
'--cap-add=NET_ADMIN', // For firewall setup
|
|
925
940
|
'--cap-add=NET_RAW', // For firewall setup
|
|
@@ -951,10 +966,10 @@ function createDockerContainer(context, containerName, imageName, config, execut
|
|
|
951
966
|
* Run the post-start setup commands in a container.
|
|
952
967
|
* This includes firewall initialization, prlt setup, and Claude settings.
|
|
953
968
|
* @param containerId - Docker container ID
|
|
954
|
-
* @param
|
|
969
|
+
* @param permissionMode - Permission mode: 'safe' requires approval, 'danger' skips checks
|
|
955
970
|
* @param executor - Which executor is being used (determines Claude-specific setup)
|
|
956
971
|
*/
|
|
957
|
-
function runContainerSetup(containerId,
|
|
972
|
+
function runContainerSetup(containerId, permissionMode = 'safe', executor = 'claude-code') {
|
|
958
973
|
try {
|
|
959
974
|
// Run firewall init (requires sudo since we're running as node user)
|
|
960
975
|
execSync(`docker exec ${containerId} sudo /usr/local/bin/init-firewall.sh`, { stdio: 'pipe' });
|
|
@@ -995,9 +1010,9 @@ function runContainerSetup(containerId, sandboxed = true, executor = 'claude-cod
|
|
|
995
1010
|
console.debug('[runners:docker] Failed to parse host .claude.json, using empty settings');
|
|
996
1011
|
}
|
|
997
1012
|
}
|
|
998
|
-
// Only set bypassPermissionsModeAccepted when user chose danger mode
|
|
1013
|
+
// Only set bypassPermissionsModeAccepted when user chose danger mode
|
|
999
1014
|
// This doesn't modify the host file - only the container copy
|
|
1000
|
-
if (
|
|
1015
|
+
if (permissionMode === 'danger') {
|
|
1001
1016
|
settings.bypassPermissionsModeAccepted = true;
|
|
1002
1017
|
}
|
|
1003
1018
|
// Skip first-run onboarding (theme picker, tips, etc.) for automated agents
|
|
@@ -1032,7 +1047,7 @@ function runContainerSetup(containerId, sandboxed = true, executor = 'claude-cod
|
|
|
1032
1047
|
const settingsJson = JSON.stringify(settings);
|
|
1033
1048
|
// Write to container at /home/node/.claude.json using stdin piping
|
|
1034
1049
|
execSync(`docker exec -i ${containerId} bash -c 'cat > /home/node/.claude.json'`, { input: settingsJson, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
1035
|
-
console.debug(`[runners:docker] Copied .claude.json settings to container (bypassPermissionsModeAccepted=${
|
|
1050
|
+
console.debug(`[runners:docker] Copied .claude.json settings to container (bypassPermissionsModeAccepted=${permissionMode === 'danger'})`);
|
|
1036
1051
|
// Write ~/.claude/settings.json to skip the dangerous mode permission prompt (TKT-1134)
|
|
1037
1052
|
// This prevents Claude Code from prompting about permission mode on first run
|
|
1038
1053
|
const claudeSettings = JSON.stringify({ skipDangerousModePermissionPrompt: true });
|
|
@@ -1138,10 +1153,10 @@ function ensureDockerContainer(context, config, executor = 'claude-code') {
|
|
|
1138
1153
|
return null;
|
|
1139
1154
|
}
|
|
1140
1155
|
// Run post-start setup (firewall, prlt, Claude settings)
|
|
1141
|
-
// Pass
|
|
1156
|
+
// Pass permission mode to determine whether to set bypassPermissionsModeAccepted
|
|
1142
1157
|
// Pass executor to skip Claude-specific setup for non-Claude executors
|
|
1143
|
-
console.debug(`[runners:docker] Running container setup (
|
|
1144
|
-
if (!runContainerSetup(containerId, config.
|
|
1158
|
+
console.debug(`[runners:docker] Running container setup (permissionMode=${config.permissionMode}, executor=${executor})`);
|
|
1159
|
+
if (!runContainerSetup(containerId, config.permissionMode, executor)) {
|
|
1145
1160
|
console.debug(`[runners:docker] Setup failed, but continuing...`);
|
|
1146
1161
|
// Don't fail completely - setup might partially work
|
|
1147
1162
|
}
|
|
@@ -1225,30 +1240,42 @@ function writePromptFile(context) {
|
|
|
1225
1240
|
* Uses docker exec for direct container access.
|
|
1226
1241
|
* Uses a prompt file to avoid shell escaping issues.
|
|
1227
1242
|
*/
|
|
1228
|
-
export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive',
|
|
1243
|
+
export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive', permissionMode = 'safe', displayMode = 'terminal') {
|
|
1229
1244
|
// Calculate the relative path from agentDir to worktreePath for cd
|
|
1230
1245
|
const relativePath = path.relative(context.agentDir, context.worktreePath);
|
|
1231
1246
|
const cdCmd = relativePath ? `cd /workspace/${relativePath} && ` : '';
|
|
1232
1247
|
// Build executor command using the centralized getExecutorCommand()
|
|
1233
1248
|
// This ensures all runners use consistent executor invocation
|
|
1234
1249
|
let executorCmd;
|
|
1250
|
+
const skipPermissions = permissionMode === 'danger';
|
|
1235
1251
|
if (isClaudeExecutor(executor)) {
|
|
1236
|
-
// Claude-specific flags based on output mode and
|
|
1252
|
+
// Claude-specific flags based on output mode and permission mode
|
|
1237
1253
|
// - interactive: No -p flag, shows streaming UI (watch Claude work in real-time)
|
|
1238
1254
|
// - print: Uses -p flag, outputs final result only (better for logs/automation)
|
|
1239
1255
|
const printFlag = outputMode === 'print' ? '-p ' : '';
|
|
1240
|
-
// sandboxed=true means safe mode (no --dangerously-skip-permissions)
|
|
1241
|
-
// sandboxed=false means danger mode (use --dangerously-skip-permissions)
|
|
1242
1256
|
// --permission-mode bypassPermissions: skips the "trust this folder" dialog
|
|
1243
1257
|
const bypassTrustFlag = '--permission-mode bypassPermissions ';
|
|
1244
|
-
const permissionsFlag =
|
|
1258
|
+
const permissionsFlag = skipPermissions ? '--dangerously-skip-permissions ' : '';
|
|
1245
1259
|
// --effort high: skips the effort level prompt for automated agents (TKT-1134)
|
|
1246
1260
|
const effortFlag = '--effort high ';
|
|
1247
1261
|
executorCmd = `claude ${bypassTrustFlag}${permissionsFlag}${effortFlag}${printFlag}"$(cat ${promptFile})"`;
|
|
1248
1262
|
}
|
|
1263
|
+
else if (executor === 'codex') {
|
|
1264
|
+
// Use Codex adapter for mode validation and deterministic command building.
|
|
1265
|
+
// Validates that the permission/display combination is supported before building.
|
|
1266
|
+
const codexPermission = permissionMode;
|
|
1267
|
+
const codexContext = resolveCodexExecutionContext(displayMode, outputMode);
|
|
1268
|
+
const modeError = validateCodexMode(codexPermission, codexContext);
|
|
1269
|
+
if (modeError) {
|
|
1270
|
+
throw modeError;
|
|
1271
|
+
}
|
|
1272
|
+
const codexResult = getCodexCommand('PLACEHOLDER', codexPermission, codexContext);
|
|
1273
|
+
const argsStr = codexResult.args.map(a => a === 'PLACEHOLDER' ? `"$(cat ${promptFile})"` : a).join(' ');
|
|
1274
|
+
executorCmd = `${codexResult.cmd} ${argsStr}`;
|
|
1275
|
+
}
|
|
1249
1276
|
else {
|
|
1250
|
-
// Non-Claude executors: use getExecutorCommand() to get correct command and args
|
|
1251
|
-
const { cmd, args } = getExecutorCommand(executor, `PLACEHOLDER`,
|
|
1277
|
+
// Non-Claude, non-Codex executors: use getExecutorCommand() to get correct command and args
|
|
1278
|
+
const { cmd, args } = getExecutorCommand(executor, `PLACEHOLDER`, skipPermissions);
|
|
1252
1279
|
// Replace the placeholder prompt with a file read for shell safety
|
|
1253
1280
|
const argsStr = args.map(a => a === 'PLACEHOLDER' ? `"$(cat ${promptFile})"` : a).join(' ');
|
|
1254
1281
|
executorCmd = `${cmd} ${argsStr}`;
|
|
@@ -1337,7 +1364,7 @@ export async function runDevcontainer(context, executor, config, displayMode = '
|
|
|
1337
1364
|
}
|
|
1338
1365
|
// Build the docker exec command (just runs claude directly)
|
|
1339
1366
|
// tmux session setup is handled by runDevcontainerInTmux, not buildDevcontainerCommand
|
|
1340
|
-
const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.
|
|
1367
|
+
const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.permissionMode, displayMode);
|
|
1341
1368
|
// Execute based on display mode
|
|
1342
1369
|
// When sessionManager is 'tmux', always use tmux inside container for session persistence
|
|
1343
1370
|
// (allows reattach via `prlt session attach` even for background mode)
|
|
@@ -1987,9 +2014,17 @@ export async function runDocker(context, executor, config) {
|
|
|
1987
2014
|
if (config.docker.cpus) {
|
|
1988
2015
|
dockerCmd += ` --cpus ${config.docker.cpus}`;
|
|
1989
2016
|
}
|
|
2017
|
+
// Validate Codex mode: Docker runner is always non-tty (detached with -d)
|
|
2018
|
+
if (executor === 'codex') {
|
|
2019
|
+
const codexPermission = config.permissionMode;
|
|
2020
|
+
const modeError = validateCodexMode(codexPermission, 'non-tty');
|
|
2021
|
+
if (modeError) {
|
|
2022
|
+
return { success: false, error: modeError.message };
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1990
2025
|
// Build executor command using getExecutorCommand() for correct invocation
|
|
1991
2026
|
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
1992
|
-
const { cmd, args } = getExecutorCommand(executor, escapedPrompt,
|
|
2027
|
+
const { cmd, args } = getExecutorCommand(executor, escapedPrompt, config.permissionMode === 'danger');
|
|
1993
2028
|
// For Claude Code in Docker, use --print for non-interactive output
|
|
1994
2029
|
// Non-Claude executors use their native command format from getExecutorCommand()
|
|
1995
2030
|
dockerCmd += ` ${config.docker.image}`;
|
|
@@ -2049,9 +2084,17 @@ export async function runVm(context, executor, config, host) {
|
|
|
2049
2084
|
const gitPullCmd = `cd ${remoteWorkspace} && git fetch && git checkout ${context.branch}`;
|
|
2050
2085
|
execSync(`ssh ${sshOpts} ${user}@${targetHost} "${gitPullCmd}"`, { stdio: 'pipe' });
|
|
2051
2086
|
}
|
|
2087
|
+
// Validate Codex mode: VM runner is always non-tty (SSH + nohup)
|
|
2088
|
+
if (executor === 'codex') {
|
|
2089
|
+
const codexPermission = config.permissionMode;
|
|
2090
|
+
const modeError = validateCodexMode(codexPermission, 'non-tty');
|
|
2091
|
+
if (modeError) {
|
|
2092
|
+
return { success: false, error: modeError.message };
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2052
2095
|
// Execute on remote using executor-appropriate command
|
|
2053
2096
|
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
2054
|
-
const { cmd: executorCmd, args: executorArgs } = getExecutorCommand(executor, escapedPrompt,
|
|
2097
|
+
const { cmd: executorCmd, args: executorArgs } = getExecutorCommand(executor, escapedPrompt, config.permissionMode === 'danger');
|
|
2055
2098
|
// Build the remote command based on executor type
|
|
2056
2099
|
let remoteCmd;
|
|
2057
2100
|
if (isClaudeExecutor(executor)) {
|
|
@@ -282,7 +282,7 @@ export async function spawnAgentForTicket(ticket, agentName, storage, executionS
|
|
|
282
282
|
environment = 'host';
|
|
283
283
|
}
|
|
284
284
|
const displayMode = options.displayMode || 'terminal';
|
|
285
|
-
const
|
|
285
|
+
const permissionMode = (options.skipPermissions ?? false) ? 'danger' : 'safe';
|
|
286
286
|
// Executor preflight check (TKT-1082): verify binary is available before proceeding
|
|
287
287
|
// For host environment, check immediately. For devcontainer, check happens after container start.
|
|
288
288
|
if (environment === 'host') {
|
|
@@ -457,12 +457,12 @@ export async function spawnAgentForTicket(ticket, agentName, storage, executionS
|
|
|
457
457
|
executor,
|
|
458
458
|
environment,
|
|
459
459
|
displayMode,
|
|
460
|
-
|
|
460
|
+
permissionMode,
|
|
461
461
|
branch,
|
|
462
462
|
});
|
|
463
463
|
// Load execution config (use passed config or load from db)
|
|
464
464
|
const executionConfig = options.executionConfig || loadExecutionConfig(db);
|
|
465
|
-
executionConfig.
|
|
465
|
+
executionConfig.permissionMode = permissionMode;
|
|
466
466
|
// Run execution
|
|
467
467
|
// Default to tmux for session persistence (enables peek/poke/attach)
|
|
468
468
|
const sessionManager = options.sessionManager || 'tmux';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Database operations for agent_work table.
|
|
5
5
|
*/
|
|
6
6
|
import Database from 'better-sqlite3';
|
|
7
|
-
import { AgentWork, ExecutionStatus, ExecutorType, ExecutionEnvironment, DisplayMode } from './types.js';
|
|
7
|
+
import { AgentWork, ExecutionStatus, ExecutorType, ExecutionEnvironment, DisplayMode, PermissionMode } from './types.js';
|
|
8
8
|
export declare class ExecutionStorage {
|
|
9
9
|
private db;
|
|
10
10
|
constructor(db: Database.Database);
|
|
@@ -18,7 +18,7 @@ export declare class ExecutionStorage {
|
|
|
18
18
|
executor: ExecutorType;
|
|
19
19
|
environment: ExecutionEnvironment;
|
|
20
20
|
displayMode: DisplayMode;
|
|
21
|
-
|
|
21
|
+
permissionMode: PermissionMode;
|
|
22
22
|
branch?: string;
|
|
23
23
|
pid?: string;
|
|
24
24
|
containerId?: string;
|
|
@@ -18,7 +18,7 @@ function rowToAgentWork(row) {
|
|
|
18
18
|
executor: row.executor,
|
|
19
19
|
environment: (row.environment || 'host'),
|
|
20
20
|
displayMode: (row.display_mode || 'terminal'),
|
|
21
|
-
|
|
21
|
+
permissionMode: (row.permission_mode || 'safe'),
|
|
22
22
|
status: row.status,
|
|
23
23
|
branch: row.branch || undefined,
|
|
24
24
|
pid: row.pid || undefined,
|
|
@@ -51,10 +51,10 @@ export class ExecutionStorage {
|
|
|
51
51
|
const id = `WORK-${randomUUID().substring(0, 8).toUpperCase()}`;
|
|
52
52
|
this.db.prepare(`
|
|
53
53
|
INSERT INTO ${T.agent_work} (
|
|
54
|
-
id, ticket_id, agent_name, executor, environment, display_mode,
|
|
54
|
+
id, ticket_id, agent_name, executor, environment, display_mode, permission_mode,
|
|
55
55
|
status, branch, pid, container_id, session_id, host, log_path, started_at
|
|
56
56
|
) VALUES (?, ?, ?, ?, ?, ?, ?, 'starting', ?, ?, ?, ?, ?, ?, ?)
|
|
57
|
-
`).run(id, params.ticketId, params.agentName, params.executor, params.environment, params.displayMode, params.
|
|
57
|
+
`).run(id, params.ticketId, params.agentName, params.executor, params.environment, params.displayMode, params.permissionMode, params.branch || null, params.pid || null, params.containerId || null, params.sessionId || null, params.host || null, params.logPath || null, now);
|
|
58
58
|
return this.getExecution(id);
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -46,7 +46,7 @@ export interface AgentWork {
|
|
|
46
46
|
environment: ExecutionEnvironment;
|
|
47
47
|
displayMode: DisplayMode;
|
|
48
48
|
sessionManager?: SessionManager;
|
|
49
|
-
|
|
49
|
+
permissionMode: PermissionMode;
|
|
50
50
|
status: ExecutionStatus;
|
|
51
51
|
branch?: string;
|
|
52
52
|
pid?: string;
|
|
@@ -120,7 +120,7 @@ export interface ExecutionConfig {
|
|
|
120
120
|
autoExecute: boolean;
|
|
121
121
|
shell: Shell;
|
|
122
122
|
outputMode: OutputMode;
|
|
123
|
-
|
|
123
|
+
permissionMode: PermissionMode;
|
|
124
124
|
authMethod?: AuthMethod;
|
|
125
125
|
createPrDefault?: boolean;
|
|
126
126
|
tmux: {
|
|
@@ -131,7 +131,7 @@ export const DEFAULT_EXECUTION_CONFIG = {
|
|
|
131
131
|
autoExecute: false,
|
|
132
132
|
shell: 'zsh', // macOS default
|
|
133
133
|
outputMode: 'interactive', // Show streaming UI by default
|
|
134
|
-
|
|
134
|
+
permissionMode: 'safe', // Require approval for dangerous operations by default
|
|
135
135
|
tmux: {
|
|
136
136
|
session: 'proletariat',
|
|
137
137
|
layout: 'window',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shared contract for normalizing external issues (Linear, Jira) into
|
|
5
5
|
* a canonical IssueEnvelope format with deterministic spawn context mapping.
|
|
6
6
|
*/
|
|
7
|
-
export { type IssueSource, type IssueEnvelope, type IssueSpawnContext, type IssueValidationError, type IssueValidationErrorCode, type IssueValidationResult, type ExternalIssueAdapter, type ExternalIssueErrorCode, ISSUE_SOURCES, ExternalIssueError, } from './types.js';
|
|
7
|
+
export { type IssueSource, type IssueEnvelope, type IssueSpawnContext, type IssueValidationError, type IssueValidationErrorCode, type IssueValidationResult, type ExternalIssueAdapter, type ExternalIssueErrorCode, type ExternalIssueAdapterErrorCode, type NormalizedIssueEnvelope, type IssueSourceMetadata, ISSUE_SOURCES, ExternalIssueError, ExternalIssueAdapterError, toNormalizedEnvelope, } from './types.js';
|
|
8
8
|
export { validateIssueEnvelope, validateOrThrow, } from './validation.js';
|
|
9
9
|
export { mapToSpawnContext, } from './mapper.js';
|
|
10
10
|
export { LinearIssueAdapter, JiraIssueAdapter, } from './adapters.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a canonical IssueEnvelope format with deterministic spawn context mapping.
|
|
6
6
|
*/
|
|
7
7
|
// Types and interfaces
|
|
8
|
-
export { ISSUE_SOURCES, ExternalIssueError, } from './types.js';
|
|
8
|
+
export { ISSUE_SOURCES, ExternalIssueError, ExternalIssueAdapterError, toNormalizedEnvelope, } from './types.js';
|
|
9
9
|
// Validation
|
|
10
10
|
export { validateIssueEnvelope, validateOrThrow, } from './validation.js';
|
|
11
11
|
// Mapper
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type IssueEnvelope, type NormalizedIssueEnvelope } from './types.js';
|
|
2
|
+
export interface LinearAdapterConfig {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
team?: string;
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a raw Linear API issue node into a canonical IssueEnvelope.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeLinearIssue(rawIssue: unknown): IssueEnvelope;
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a raw Linear issue into a PMO-ready NormalizedIssueEnvelope.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeLinearIssueToEnvelope(rawIssue: unknown): NormalizedIssueEnvelope;
|
|
15
|
+
/**
|
|
16
|
+
* Build a PMO ticket description from a NormalizedIssueEnvelope.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildLinearTicketDescription(envelope: NormalizedIssueEnvelope): string;
|
|
19
|
+
/**
|
|
20
|
+
* Build ticket metadata from a NormalizedIssueEnvelope for traceability.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildLinearMetadata(envelope: NormalizedIssueEnvelope): Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Build a spawn context message from a NormalizedIssueEnvelope.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildLinearSpawnContextMessage(envelope: NormalizedIssueEnvelope, additionalMessage?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Build a CLI command string for selecting a specific Linear issue.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildLinearIssueChoiceCommand(issueIdentifier: string, projectId?: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a single Linear issue by identifier (for example, ENG-123) and normalize it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getLinearIssueByIdentifier(configInput: LinearAdapterConfig, identifier: string, options?: {
|
|
35
|
+
fetchImpl?: typeof fetch;
|
|
36
|
+
}): Promise<NormalizedIssueEnvelope | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Fetch and normalize Linear issues into NormalizedIssueEnvelopes.
|
|
39
|
+
*/
|
|
40
|
+
export declare function listLinearIssues(configInput: LinearAdapterConfig, options?: {
|
|
41
|
+
limit?: number;
|
|
42
|
+
fetchImpl?: typeof fetch;
|
|
43
|
+
}): Promise<NormalizedIssueEnvelope[]>;
|