mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 05:59:43 +02:00
fix(pi): close four mode-list gaps in the pi run mode
Review follow-ups on #282. All four are the same failure shape: a list that enumerates run modes, missed by the sweep that added 'pi'. 1. Cron ignored pi's project-trust clamp. The PR widened CronJobBaseSchema's agentType to accept 'pi' but not the matching clamp beside gemini's, so a non-granted multi-user owner's cron pi job spawned bare `pi` (pi's own defaultProjectTrust, an interactive prompt they can answer "yes" to, which loads and EXECUTES repo-local .pi/extensions TypeScript) while the same user's UI/API launch was forced to --no-approve. The clamp is now a pure exported helper, clampCronExternalCliConfigs(), so both it and gemini's previously untested materialization are pinned. 2. POST /api/sessions/:id/interactive auto-enabled the Ralph tracker for pi: its denylist covered opencode/codex/gemini/antigravity only. The tracker is never fed for an external CLI (_processExpensiveParsers returns early), so a pi session reported ralphEnabled and Ralph UI state no sibling backend shows. 3. REMOTE_CLI_BIN had no pi entry, so buildRemoteCliVersionProbeCommand() returned null and Session.cliVersion stayed blank for every remote-SSH pi session, even though the PR wired the remote launch command and the per-mode override schema field. 4. The desktop home rail's badge map had no pi entry, and its lookup falls back to '', which is what claude renders. A pi session read as Claude there while the tab strip and phone overview badged it correctly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@ import { validateSessionFilePath } from '../web/route-helpers.js';
|
||||
import { computeNextRunAt, dueKeyFor } from './cron-time.js';
|
||||
import type { SessionPort, EventPort, ConfigPort, InfraPort } from '../web/ports/index.js';
|
||||
import type { CronJob, CronJobRun, CronJobRunStatus, TriggerType } from '../types/cron.js';
|
||||
import type { GeminiConfig } from '../types/session.js';
|
||||
import type { GeminiConfig, PiConfig, SessionMode } from '../types/session.js';
|
||||
import type { CronJobInput } from './cron-input.js';
|
||||
|
||||
/** The subset of the route context the cron depends on. */
|
||||
@@ -35,6 +35,32 @@ export type CronDeps = SessionPort & EventPort & ConfigPort & InfraPort;
|
||||
|
||||
const delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
/**
|
||||
* Section 6.3 clamp for a cron-launched external CLI, mirroring
|
||||
* `clampExternalCliBypassForOwner()` in session-routes.ts.
|
||||
*
|
||||
* A cron job carries NO per-CLI config, so what a non-granted owner actually gets is
|
||||
* each CLI's SPAWN DEFAULT, and for two of them that default is itself unsafe:
|
||||
* - gemini: `buildGeminiCommand(undefined)` emits `--approval-mode yolo` (classifier-free),
|
||||
* so `auto_edit` is materialized.
|
||||
* - pi: pi's own `defaultProjectTrust` is an interactive prompt the session user can simply
|
||||
* answer "yes" to, which then loads and EXECUTES repo-local `.pi/extensions` TypeScript,
|
||||
* so `approveProjectTrust: false` (`--no-approve`) is materialized. Omitting `--approve`
|
||||
* is NOT a clamp.
|
||||
* Codex and antigravity need nothing here: their absent config already spawns safe.
|
||||
* Granted/admin/single-user get undefined for both, i.e. upstream defaults untouched.
|
||||
*/
|
||||
export function clampCronExternalCliConfigs(
|
||||
mode: SessionMode,
|
||||
ownerGranted: boolean
|
||||
): { geminiConfig: GeminiConfig | undefined; piConfig: PiConfig | undefined } {
|
||||
if (ownerGranted) return { geminiConfig: undefined, piConfig: undefined };
|
||||
return {
|
||||
geminiConfig: mode === 'gemini' ? { approvalMode: 'auto_edit' } : undefined,
|
||||
piConfig: mode === 'pi' ? { approveProjectTrust: false } : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** Hard ceiling on a prompt-file read (defends against unbounded-read DoS). */
|
||||
const MAX_PROMPT_FILE_BYTES = 1024 * 1024;
|
||||
|
||||
@@ -371,13 +397,10 @@ export class CronService {
|
||||
const claudeModeConfig = await this.deps.getClaudeModeConfig();
|
||||
const effectiveClaudeMode = await resolveClaudeModeForUsername(claudeModeConfig.claudeMode, job.owner);
|
||||
const model = mode !== 'shell' ? modelConfig?.defaultModel || undefined : undefined;
|
||||
// Section 6.3: cron carries no per-CLI config, so buildGeminiCommand(undefined)
|
||||
// would default a non-granted owner to `--approval-mode yolo` (classifier-free) —
|
||||
// materialize auto_edit for a non-granted gemini owner, mirroring the route clamp
|
||||
// (#15). Granted/admin/single-user leave it undefined → yolo parity. Codex's absent
|
||||
// config already defaults to the safe sandbox, so no clamp is needed there.
|
||||
const geminiConfig: GeminiConfig | undefined =
|
||||
mode === 'gemini' && !ownerGranted ? { approvalMode: 'auto_edit' } : undefined;
|
||||
// Section 6.3: materialize the safe default for a non-granted owner (see
|
||||
// clampCronExternalCliConfigs — cron sends no per-CLI config, so the CLI's own
|
||||
// spawn default is what would otherwise apply).
|
||||
const { geminiConfig, piConfig } = clampCronExternalCliConfigs(mode, ownerGranted);
|
||||
session = new Session({
|
||||
workingDir: job.workingDir,
|
||||
mode,
|
||||
@@ -389,6 +412,7 @@ export class CronService {
|
||||
claudeMode: effectiveClaudeMode,
|
||||
allowedTools: claudeModeConfig.allowedTools,
|
||||
geminiConfig,
|
||||
piConfig,
|
||||
owner: job.owner,
|
||||
});
|
||||
this.deps.addSession(session);
|
||||
|
||||
@@ -268,6 +268,7 @@ const REMOTE_CLI_BIN: Partial<Record<SessionMode, string>> = {
|
||||
codex: 'codex',
|
||||
gemini: 'gemini',
|
||||
antigravity: 'agy',
|
||||
pi: 'pi',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,6 +69,7 @@ const HOME_SESSIONS_MODE_BADGE = {
|
||||
codex: 'cx',
|
||||
gemini: 'gm',
|
||||
antigravity: 'ag',
|
||||
pi: 'pi',
|
||||
};
|
||||
|
||||
Object.assign(CodemanApp.prototype, {
|
||||
|
||||
@@ -1114,12 +1114,16 @@ export function registerSessionRoutes(
|
||||
|
||||
try {
|
||||
// Auto-detect completion phrase from CLAUDE.md BEFORE starting (only if globally enabled and not explicitly disabled by user)
|
||||
// Ralph tracker is not supported for opencode / codex / gemini / antigravity sessions
|
||||
// Ralph tracker is not supported for opencode / codex / gemini / antigravity / pi sessions.
|
||||
// Keep this list in step with isExternalCliMode(): _processExpensiveParsers() returns early
|
||||
// for those modes, so a tracker enabled here would never be fed, and the session would
|
||||
// still report ralphEnabled + Ralph UI state that no other external CLI shows.
|
||||
if (
|
||||
session.mode !== 'opencode' &&
|
||||
session.mode !== 'codex' &&
|
||||
session.mode !== 'gemini' &&
|
||||
session.mode !== 'antigravity' &&
|
||||
session.mode !== 'pi' &&
|
||||
ctx.store.getConfig().ralphEnabled &&
|
||||
!session.ralphTracker.autoEnableDisabled
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user