@iaforged/context-code 2.4.1 → 2.4.3
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/src/bridge/bridgeMain.js +1 -1
- package/dist/src/commands/fast/fast.js +1 -1
- package/dist/src/commands/memory/memory.js +1 -1
- package/dist/src/commands/model/model.js +1 -1
- package/dist/src/commands/review.js +1 -1
- package/dist/src/commands/ultraplan.js +1 -1
- package/dist/src/components/BypassPermissionsModeDialog.js +1 -1
- package/dist/src/components/ClaudeMdExternalIncludesDialog.js +1 -1
- package/dist/src/components/CostThresholdDialog.js +1 -1
- package/dist/src/components/FeedbackSurvey/TranscriptSharePrompt.js +1 -1
- package/dist/src/components/TrustDialog/TrustDialog.js +1 -1
- package/dist/src/components/hooks/SelectEventMode.js +1 -1
- package/dist/src/components/sandbox/SandboxOverridesTab.js +1 -1
- package/dist/src/components/sandbox/SandboxSettings.js +1 -1
- package/dist/src/constants/github-app.js +1 -1
- package/dist/src/constants/prompts.js +1 -1
- package/dist/src/keybindings/template.js +1 -1
- package/dist/src/skills/bundled/keybindings.js +1 -1
- package/dist/src/tools/AgentTool/agentMemory.js +1 -1
- package/dist/src/tools/AgentTool/built-in/claudeCodeGuideAgent.js +1 -1
- package/dist/src/tools/WebFetchTool/preapproved.js +1 -1
- package/dist/src/utils/claudemd.js +1 -1
- package/dist/src/utils/configConstants.js +1 -1
- package/dist/src/utils/cronTasks.js +1 -1
- package/dist/src/utils/cronTasksLock.js +1 -1
- package/dist/src/utils/doctorDiagnostic.js +1 -1
- package/dist/src/utils/envUtils.js +1 -1
- package/dist/src/utils/hooks/skillImprovement.js +1 -1
- package/dist/src/utils/markdownConfigLoader.js +1 -1
- package/dist/src/utils/nativeInstaller/installer.js +1 -1
- package/dist/src/utils/permissions/filesystem.js +1 -1
- package/dist/src/utils/plugins/addDirPluginSettings.js +1 -1
- package/dist/src/utils/preflightChecks.js +1 -1
- package/dist/src/utils/sandbox/sandbox-adapter.js +1 -1
- package/dist/src/utils/settings/settings.js +1 -1
- package/dist/src/utils/settings/validationTips.js +1 -1
- package/dist/src/utils/skills/skillChangeDetector.js +1 -1
- package/dist/src/utils/worktree.js +1 -1
- package/dist/src/whatsapp/config.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MACRO as e,feature as s}from"../recovery/bunBundleShim.js";import{randomUUID as t}from"crypto";import{hostname as o,tmpdir as r}from"os";import{basename as i,join as n,resolve as a}from"path";import{getRemoteSessionUrl as l}from"../constants/product.js";import{shutdownDatadog as d}from"../services/analytics/datadog.js";import{shutdown1PEventLogging as c}from"../services/analytics/firstPartyEventLogger.js";import{checkGate_CACHED_OR_BLOCKING as p}from"../services/analytics/growthbook.js";import{logEvent as u,logEventAsync as g}from"../services/analytics/index.js";import{isInBundledMode as m}from"../utils/bundledMode.js";import{logForDebugging as w}from"../utils/debug.js";import{logForDiagnosticsNoPII as f}from"../utils/diagLogs.js";import{isEnvTruthy as _,isInProtectedNamespace as h}from"../utils/envUtils.js";import{errorMessage as b}from"../utils/errors.js";import{truncateToWidth as k}from"../utils/format.js";import{logError as v}from"../utils/log.js";import{sleep as S}from"../utils/sleep.js";import{createAgentWorktree as $,removeAgentWorktree as y}from"../utils/worktree.js";import{BridgeFatalError as I,createBridgeApiClient as E,isExpiredErrorType as M,isSuppressible403 as R,validateBridgeId as C}from"./bridgeApi.js";import{formatDuration as x}from"./bridgeStatusUtil.js";import{createBridgeLogger as T}from"./bridgeUI.js";import{createCapacityWake as D}from"./capacityWake.js";import{describeAxiosError as A}from"./debugUtils.js";import{createTokenRefreshScheduler as U}from"./jwtUtils.js";import{getPollIntervalConfig as P}from"./pollConfig.js";import{toCompatSessionId as j,toInfraSessionId as B}from"./sessionIdCompat.js";import{createSessionSpawner as W,safeFilenameId as F}from"./sessionRunner.js";import{getTrustedDeviceToken as O}from"./trustedDevice.js";import{BRIDGE_LOGIN_ERROR as N,DEFAULT_SESSION_TIMEOUT_MS as G}from"./types.js";import{buildCCRv2SdkUrl as V,buildSdkUrl as H,decodeWorkSecret as L,registerWorker as z,sameSessionId as q}from"./workSecret.js";const K={connInitialMs:2e3,connCapMs:12e4,connGiveUpMs:6e5,generalInitialMs:500,generalCapMs:3e4,generalGiveUpMs:6e5};async function isMultiSessionSpawnEnabled(){return p("tengu_ccr_bridge_multi_session")}function pollSleepDetectionThresholdMs(e){return 2*e.connCapMs}function spawnScriptArgs(){return m()||!process.argv[1]?[]:[process.argv[1]]}function safeSpawn(e,s,t){try{return e.spawn(s,t)}catch(e){const s=b(e);return v(new Error(`Session spawn failed: ${s}`)),s}}export async function runBridgeLoop(e,t,o,i,a,d,c,p=K,g,m){const k=new AbortController;c.aborted?k.abort():c.addEventListener("abort",()=>k.abort(),{once:!0});const E=k.signal,T=new Map,B=new Map,W=new Map,O=new Map,N=new Map,Y=new Map,J=new Set,X=new Map,Q=new Set,Z=new Set,ee=D(E);async function heartbeatActiveWorkItems(){let e=!1,s=!1;const o=[];for(const[r]of T){const n=W.get(r),a=N.get(r);if(n&&a)try{await i.heartbeatWork(t,n,a),e=!0}catch(e){w(`[bridge:heartbeat] Failed for sessionId=${r} workId=${n}: ${b(e)}`),e instanceof I&&(u("tengu_bridge_heartbeat_error",{status:e.status,error_type:401===e.status||403===e.status?"auth_failed":"fatal"}),401===e.status||403===e.status?o.push(r):s=!0)}}for(const e of o){d.logVerbose(`Session ${e} token expired — re-queuing via bridge/reconnect`);try{await i.reconnectSession(t,e),w(`[bridge:heartbeat] Re-queued sessionId=${e} via bridge/reconnect`)}catch(s){d.logError(`Failed to refresh session ${e} token: ${b(s)}`),w(`[bridge:heartbeat] reconnectSession(${e}) failed: ${b(s)}`,{level:"error"})}}return s?"fatal":o.length>0?"auth_failed":e?"ok":"failed"}const se=new Set,te=m?U({getAccessToken:m,onRefresh:(e,s)=>{const o=T.get(e);o&&(se.has(e)?(d.logVerbose(`Refreshing session ${e} token via bridge/reconnect`),i.reconnectSession(t,e).catch(s=>{d.logError(`Failed to refresh session ${e} token: ${b(s)}`),w(`[bridge:token] reconnectSession(${e}) failed: ${b(s)}`,{level:"error"})})):o.updateAccessToken(s))},label:"bridge"}):null,oe=Date.now(),re=new Set;function trackCleanup(e){re.add(e),e.finally(()=>re.delete(e))}let ie=0,ne=0,ae=null,le=null,de=null,ce=null,pe=!1;if(w(`[bridge:work] Starting poll loop spawnMode=${e.spawnMode} maxSessions=${e.maxSessions} environmentId=${t}`),f("info","bridge_loop_started",{max_sessions:e.maxSessions,spawn_mode:e.spawnMode}),"ant"===process.env.USER_TYPE){let s;if(e.debugFile){const t=e.debugFile.lastIndexOf(".");s=t>0?`${e.debugFile.slice(0,t)}-*${e.debugFile.slice(t)}`:`${e.debugFile}-*`}else s=n(r(),"claude","bridge-session-*.log");d.setDebugLogPath(s)}function updateStatusDisplay(){d.updateSessionCount(T.size,e.maxSessions,e.spawnMode);for(const[e,s]of T){const t=s.currentActivity;t&&d.updateSessionActivity(O.get(e)??e,t)}if(0===T.size)return void d.updateIdleStatus();const[s,t]=[...T.entries()].pop(),o=B.get(s);if(!o)return;const r=t.currentActivity;if(!r||"result"===r.type||"error"===r.type)return void(e.maxSessions>1&&d.refreshDisplay());const i=x(Date.now()-o),n=t.activities.filter(e=>"tool_start"===e.type).slice(-5).map(e=>e.summary);d.updateSessionStatus(s,i,r,n)}function startStatusUpdates(){stopStatusUpdates(),updateStatusDisplay(),ce=setInterval(updateStatusDisplay,1e3)}function stopStatusUpdates(){ce&&(clearInterval(ce),ce=null)}function onSessionDone(s,o,r){return n=>{const a=W.get(s);T.delete(s),B.delete(s),W.delete(s),N.delete(s);const l=O.get(s)??s;O.delete(s),d.removeSession(l),Z.delete(l),se.delete(s);const c=Y.get(s);c&&(clearTimeout(c),Y.delete(s)),te?.cancel(s),ee.wake();const g=Q.delete(s),m=g&&"interrupted"===n?"failed":n,_=Date.now()-o;w(`[bridge:session] sessionId=${s} workId=${a??"unknown"} exited status=${m} duration=${x(_)}`),u("tengu_bridge_session_done",{status:m,duration_ms:_}),f("info","bridge_session_done",{status:m,duration_ms:_}),d.clearStatus(),stopStatusUpdates();const h=r.lastStderr.length>0?r.lastStderr.join("\n"):void 0;let S;switch(m){case"completed":d.logSessionComplete(s,_);break;case"failed":g||E.aborted||(S=h??"Process exited with error",d.logSessionFailed(s,S),v(new Error(`Bridge session failed: ${S}`)));break;case"interrupted":d.logVerbose(`Session ${s} interrupted`)}"interrupted"!==m&&a&&(trackCleanup(stopWorkWithRetry(i,t,a,d,p.stopWorkBaseDelayMs)),J.add(a));const $=X.get(s);if($&&(X.delete(s),trackCleanup(y($.worktreePath,$.worktreeBranch,$.gitRoot,$.hookBased).catch(e=>d.logVerbose(`Failed to remove worktree ${$.worktreePath}: ${b(e)}`)))),"interrupted"!==m&&!E.aborted){if("single-session"===e.spawnMode)return w(`[bridge:session] Session ${m}, aborting poll loop to tear down environment`),void k.abort();trackCleanup(i.archiveSession(l).catch(e=>d.logVerbose(`Failed to archive session ${s}: ${b(e)}`))),w(`[bridge:session] Session ${m}, returning to idle (multi-session mode)`)}E.aborted||startStatusUpdates()}}for(d.printBanner(e,t),d.updateSessionCount(0,e.maxSessions,e.spawnMode),g&&d.setAttached(g),g||startStatusUpdates();!E.aborted;){const s=P();try{const c=await i.pollForWork(t,o,E,s.reclaim_older_than_ms);if(null!==ae||null!==le){const e=Date.now()-(ae??le??Date.now());d.logReconnected(e),w(`[bridge:poll] Reconnected after ${x(e)}`),u("tengu_bridge_reconnected",{disconnected_ms:e})}if(ie=0,ne=0,ae=null,le=null,de=null,!c){if(T.size>=e.maxSessions){const t=s.multisession_poll_interval_ms_at_capacity;if(s.non_exclusive_heartbeat_interval_ms>0){u("tengu_bridge_heartbeat_mode_entered",{active_sessions:T.size,heartbeat_interval_ms:s.non_exclusive_heartbeat_interval_ms});const o=t>0?Date.now()+t:null;let r="ok",i=0;for(;!E.aborted&&T.size>=e.maxSessions&&(null===o||Date.now()<o);){const e=P();if(e.non_exclusive_heartbeat_interval_ms<=0)break;const s=ee.signal();if(r=await heartbeatActiveWorkItems(),"auth_failed"===r||"fatal"===r){s.cleanup();break}i++,await S(e.non_exclusive_heartbeat_interval_ms,s.signal),s.cleanup()}const n="auth_failed"===r||"fatal"===r?r:E.aborted?"shutdown":T.size<e.maxSessions?"capacity_changed":null!==o&&Date.now()>=o?"poll_due":"config_disabled";if(u("tengu_bridge_heartbeat_mode_exited",{reason:n,heartbeat_cycles:i,active_sessions:T.size}),"poll_due"===n&&w(`[bridge:poll] Heartbeat poll_due after ${i} cycles — falling through to pollForWork`),"auth_failed"===r||"fatal"===r){const e=ee.signal();await S(t>0?t:s.non_exclusive_heartbeat_interval_ms,e.signal),e.cleanup()}}else if(t>0){const e=ee.signal();await S(t,e.signal),e.cleanup()}}else{const e=T.size>0?s.multisession_poll_interval_ms_partial_capacity:s.multisession_poll_interval_ms_not_at_capacity;await S(e,E)}continue}const m=T.size>=e.maxSessions;if(J.has(c.id)){if(w(`[bridge:work] Skipping already-completed workId=${c.id}`),m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}else await S(1e3,E);continue}let k;try{k=L(c.secret)}catch(e){const o=b(e);if(d.logError(`Failed to decode work secret for workId=${c.id}: ${o}`),u("tengu_bridge_work_secret_failed",{}),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs)),m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}continue}const ackWork=async()=>{w(`[bridge:work] Acknowledging workId=${c.id}`);try{await i.acknowledgeWork(t,c.id,k.session_ingress_token)}catch(e){w(`[bridge:work] Acknowledge failed workId=${c.id}: ${b(e)}`)}},I=c.data.type;switch(c.data.type){case"healthcheck":await ackWork(),w("[bridge:work] Healthcheck received"),d.logVerbose("Healthcheck received");break;case"session":{const s=c.data.id;try{C(s,"session_id")}catch{await ackWork(),d.logError(`Invalid session_id received: ${s}`);break}const o=T.get(s);if(o){o.updateAccessToken(k.session_ingress_token),N.set(s,k.session_ingress_token),W.set(s,c.id),te?.schedule(s,k.session_ingress_token),w(`[bridge:work] Updated access token for existing sessionId=${s} workId=${c.id}`),await ackWork();break}if(T.size>=e.maxSessions){w(`[bridge:work] At capacity (${T.size}/${e.maxSessions}), cannot spawn new session for workId=${c.id}`);break}await ackWork();const m=Date.now();let I,M,R=!1;if(!0===k.use_code_sessions||_(process.env.CONTEXT_BRIDGE_USE_CCR_V2)||_(process.env.CLAUDE_BRIDGE_USE_CCR_V2)){I=V(e.apiBaseUrl,s);for(let e=1;e<=2;e++)try{M=await z(I,k.session_ingress_token),R=!0,w(`[bridge:session] CCR v2: registered worker sessionId=${s} epoch=${M} attempt=${e}`);break}catch(o){const r=b(o);if(e<2){if(w(`[bridge:session] CCR v2: registerWorker attempt ${e} failed, retrying: ${r}`),await S(2e3,E),E.aborted)break;continue}d.logError(`CCR v2 worker registration failed for session ${s}: ${r}`),v(new Error(`registerWorker failed: ${r}`)),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs))}if(!R)break}else I=H(e.sessionIngressUrl,s);const x=e.spawnMode;let D=e.dir,A=0;if("worktree"===x&&(void 0===g||!q(s,g))){const e=Date.now();try{const t=await $(`bridge-${F(s)}`);A=Date.now()-e,X.set(s,{worktreePath:t.worktreePath,worktreeBranch:t.worktreeBranch,gitRoot:t.gitRoot,hookBased:t.hookBased}),D=t.worktreePath,w(`[bridge:session] Created worktree for sessionId=${s} at ${t.worktreePath}`)}catch(e){const o=b(e);d.logError(`Failed to create worktree for session ${s}: ${o}`),v(new Error(`Worktree creation failed: ${o}`)),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs));break}}w(`[bridge:session] Spawning sessionId=${s} sdkUrl=${I}`);const U=j(s),P=safeSpawn(a,{sessionId:s,sdkUrl:I,accessToken:k.session_ingress_token,useCcrV2:R,workerEpoch:M,onFirstUserMessage:s=>{if(Z.has(U))return;Z.add(U);const t=deriveSessionTitle(s);d.setSessionTitle(U,t),w(`[bridge:title] derived title for ${U}: ${t}`),import("./createSession.js").then(({updateBridgeSessionTitle:s})=>s(U,t,{baseUrl:e.apiBaseUrl})).catch(e=>w(`[bridge:title] failed to update title for ${U}: ${e}`,{level:"error"}))}},D);if("string"==typeof P){d.logError(`Failed to spawn session ${s}: ${P}`);const e=X.get(s);e&&(X.delete(s),trackCleanup(y(e.worktreePath,e.worktreeBranch,e.gitRoot,e.hookBased).catch(s=>d.logVerbose(`Failed to remove worktree ${e.worktreePath}: ${b(s)}`)))),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs));break}const L=P,K=Date.now()-m;u("tengu_bridge_session_started",{active_sessions:T.size,spawn_mode:x,in_worktree:X.has(s),spawn_duration_ms:K,worktree_create_ms:A,inProtectedNamespace:h()}),f("info","bridge_session_started",{spawn_mode:x,in_worktree:X.has(s),spawn_duration_ms:K,worktree_create_ms:A}),T.set(s,L),W.set(s,c.id),N.set(s,k.session_ingress_token),O.set(s,U);const ee=Date.now();B.set(s,ee),d.logSessionStart(s,`Session ${s}`);const oe=F(s);let re;if(e.debugFile){const s=e.debugFile.lastIndexOf(".");re=s>0?`${e.debugFile.slice(0,s)}-${oe}${e.debugFile.slice(s)}`:`${e.debugFile}-${oe}`}else(e.verbose||"ant"===process.env.USER_TYPE)&&(re=n(r(),"claude",`bridge-session-${oe}.log`));re&&d.logVerbose(`Debug log: ${re}`),d.addSession(U,l(U,e.sessionIngressUrl)),startStatusUpdates(),d.setAttached(U),fetchSessionTitle(U,e.apiBaseUrl).then(e=>{e&&T.has(s)&&(Z.add(U),d.setSessionTitle(U,e),w(`[bridge:title] server title for ${U}: ${e}`))}).catch(e=>w(`[bridge:title] failed to fetch title for ${U}: ${e}`,{level:"error"}));const ie=e.sessionTimeoutMs??G;if(ie>0){const e=setTimeout(onSessionTimeout,ie,s,ie,d,Q,L);Y.set(s,e)}R&&se.add(s),te?.schedule(s,k.session_ingress_token),L.done.then(onSessionDone(s,ee,L));break}default:await ackWork(),w(`[bridge:work] Unknown work type: ${I}, skipping`)}if(m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}}catch(e){if(E.aborted)break;if(e instanceof I){pe=!0,M(e.errorType)?d.logStatus(e.message):R(e)?w(`[bridge:work] Suppressed 403 error: ${e.message}`):(d.logError(e.message),v(e)),u("tengu_bridge_fatal_error",{status:e.status,error_type:e.errorType}),f(M(e.errorType)?"info":"error","bridge_fatal_error",{status:e.status,error_type:e.errorType});break}const s=A(e);if(isConnectionError(e)||isServerError(e)){const e=Date.now();null!==de&&e-de>pollSleepDetectionThresholdMs(p)&&(w(`[bridge:work] Detected system sleep (${Math.round((e-de)/1e3)}s gap), resetting error budget`),f("info","bridge_poll_sleep_detected",{gapMs:e-de}),ae=null,ie=0,le=null,ne=0),de=e,ae||(ae=e);const t=e-ae;if(t>=p.connGiveUpMs){d.logError(`Server unreachable for ${Math.round(t/6e4)} minutes, giving up.`),u("tengu_bridge_poll_give_up",{error_type:"connection",elapsed_ms:t}),f("error","bridge_poll_give_up",{error_type:"connection",elapsed_ms:t}),pe=!0;break}le=null,ne=0,ie=ie?Math.min(2*ie,p.connCapMs):p.connInitialMs;const o=addJitter(ie);d.logVerbose(`Connection error, retrying in ${formatDelay(o)} (${Math.round(t/1e3)}s elapsed): ${s}`),d.updateReconnectingStatus(formatDelay(o),x(t)),P().non_exclusive_heartbeat_interval_ms>0&&await heartbeatActiveWorkItems(),await S(o,E)}else{const e=Date.now();null!==de&&e-de>pollSleepDetectionThresholdMs(p)&&(w(`[bridge:work] Detected system sleep (${Math.round((e-de)/1e3)}s gap), resetting error budget`),f("info","bridge_poll_sleep_detected",{gapMs:e-de}),ae=null,ie=0,le=null,ne=0),de=e,le||(le=e);const t=e-le;if(t>=p.generalGiveUpMs){d.logError(`Persistent errors for ${Math.round(t/6e4)} minutes, giving up.`),u("tengu_bridge_poll_give_up",{error_type:"general",elapsed_ms:t}),f("error","bridge_poll_give_up",{error_type:"general",elapsed_ms:t}),pe=!0;break}ae=null,ie=0,ne=ne?Math.min(2*ne,p.generalCapMs):p.generalInitialMs;const o=addJitter(ne);d.logVerbose(`Poll failed, retrying in ${formatDelay(o)} (${Math.round(t/1e3)}s elapsed): ${s}`),d.updateReconnectingStatus(formatDelay(o),x(t)),P().non_exclusive_heartbeat_interval_ms>0&&await heartbeatActiveWorkItems(),await S(o,E)}}}stopStatusUpdates(),d.clearStatus();const ue=Date.now()-oe;u("tengu_bridge_shutdown",{active_sessions:T.size,loop_duration_ms:ue}),f("info","bridge_shutdown",{active_sessions:T.size,loop_duration_ms:ue});const ge=new Set(T.keys());g&&ge.add(g);const me=new Map(O);if(T.size>0){w(`[bridge:shutdown] Shutting down ${T.size} active session(s)`),d.logStatus(`Shutting down ${T.size} active session(s)…`);const e=new Map(W);for(const[e,s]of T.entries())w(`[bridge:shutdown] Sending SIGTERM to sessionId=${e}`),s.kill();const s=new AbortController;await Promise.race([Promise.allSettled([...T.values()].map(e=>e.done)),S(p.shutdownGraceMs??3e4,s.signal)]),s.abort();for(const[e,s]of T.entries())w(`[bridge:shutdown] Force-killing stuck sessionId=${e}`),s.forceKill();for(const e of Y.values())clearTimeout(e);if(Y.clear(),te?.cancelAll(),X.size>0){const e=[...X.values()];X.clear(),w(`[bridge:shutdown] Cleaning up ${e.length} worktree(s)`),await Promise.allSettled(e.map(e=>y(e.worktreePath,e.worktreeBranch,e.gitRoot,e.hookBased)))}await Promise.allSettled([...e.entries()].map(([e,s])=>i.stopWork(t,s,!0).catch(t=>d.logVerbose(`Failed to stop work ${s} for session ${e}: ${b(t)}`))))}if(re.size>0&&await Promise.allSettled([...re]),s("KAIROS")&&"single-session"===e.spawnMode&&g&&!pe)return d.logStatus("Resume this session by running `context remote-control --continue`"),void w(`[bridge:shutdown] Skipping archive+deregister to allow resume of session ${g}`);ge.size>0&&(w(`[bridge:shutdown] Archiving ${ge.size} session(s)`),await Promise.allSettled([...ge].map(e=>i.archiveSession(me.get(e)??j(e)).catch(s=>d.logVerbose(`Failed to archive session ${e}: ${b(s)}`)))));try{await i.deregisterEnvironment(t),w("[bridge:shutdown] Environment deregistered, bridge offline"),d.logVerbose("Environment deregistered.")}catch(e){d.logVerbose(`Failed to deregister environment: ${b(e)}`)}const{clearBridgePointer:we}=await import("./bridgePointer.js");await we(e.dir),d.logVerbose("Environment offline.")}const Y=new Set(["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENETUNREACH","EHOSTUNREACH"]);export function isConnectionError(e){return!!(e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code&&Y.has(e.code))}export function isServerError(e){return!!e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code&&"ERR_BAD_RESPONSE"===e.code}function addJitter(e){return Math.max(0,e+.25*e*(2*Math.random()-1))}function formatDelay(e){return e>=1e3?`${(e/1e3).toFixed(1)}s`:`${Math.round(e)}ms`}async function stopWorkWithRetry(e,s,t,o,r=1e3){for(let i=1;i<=3;i++)try{return await e.stopWork(s,t,!1),void w(`[bridge:work] stopWork succeeded for workId=${t} on attempt ${i}/3`)}catch(e){if(e instanceof I)return R(e)?w(`[bridge:work] Suppressed stopWork 403 for ${t}: ${e.message}`):o.logError(`Failed to stop work ${t}: ${e.message}`),void f("error","bridge_stop_work_failed",{attempts:i,fatal:!0});const s=b(e);if(i<3){const e=addJitter(r*Math.pow(2,i-1));o.logVerbose(`Failed to stop work ${t} (attempt ${i}/3), retrying in ${formatDelay(e)}: ${s}`),await S(e)}else o.logError(`Failed to stop work ${t} after 3 attempts: ${s}`),f("error","bridge_stop_work_failed",{attempts:3})}}function onSessionTimeout(e,s,t,o,r){w(`[bridge:session] sessionId=${e} timed out after ${x(s)}`),u("tengu_bridge_session_timeout",{timeout_ms:s}),t.logSessionFailed(e,`Session timed out after ${x(s)}`),o.add(e),r.kill()}const J=["session","same-dir","worktree"];function parseSpawnValue(e){return"session"===e?"single-session":"same-dir"===e?"same-dir":"worktree"===e?"worktree":`--spawn requires one of: ${J.join(", ")} (got: ${e??"<missing>"})`}function parseCapacityValue(e){const s=void 0===e?NaN:parseInt(e,10);return isNaN(s)||s<1?`--capacity requires a positive integer (got: ${e??"<missing>"})`:s}export function parseArgs(e){let t,o,r,i,n,l,d,c,p=!1,u=!1,g=!1,m=!1;for(let w=0;w<e.length;w++){const f=e[w];if("--help"===f||"-h"===f)g=!0;else if("--verbose"===f||"-v"===f)p=!0;else if("--sandbox"===f)u=!0;else if("--no-sandbox"===f)u=!1;else if("--debug-file"===f&&w+1<e.length)t=a(e[++w]);else if(f.startsWith("--debug-file="))t=a(f.slice(13));else if("--session-timeout"===f&&w+1<e.length)o=1e3*parseInt(e[++w],10);else if(f.startsWith("--session-timeout="))o=1e3*parseInt(f.slice(18),10);else if("--permission-mode"===f&&w+1<e.length)r=e[++w];else if(f.startsWith("--permission-mode="))r=f.slice(18);else if("--name"===f&&w+1<e.length)i=e[++w];else if(f.startsWith("--name="))i=f.slice(7);else if(s("KAIROS")&&"--session-id"===f&&w+1<e.length){if(c=e[++w],!c)return makeError("--session-id requires a value")}else if(s("KAIROS")&&f.startsWith("--session-id=")){if(c=f.slice(13),!c)return makeError("--session-id requires a value")}else if(!s("KAIROS")||"--continue"!==f&&"-c"!==f)if("--spawn"===f||f.startsWith("--spawn=")){if(void 0!==n)return makeError("--spawn may only be specified once");const s=parseSpawnValue(f.startsWith("--spawn=")?f.slice(8):e[++w]);if("single-session"!==s&&"same-dir"!==s&&"worktree"!==s)return makeError(s);n=s}else if("--capacity"===f||f.startsWith("--capacity=")){if(void 0!==l)return makeError("--capacity may only be specified once");const s=parseCapacityValue(f.startsWith("--capacity=")?f.slice(11):e[++w]);if("number"!=typeof s)return makeError(s);l=s}else if("--create-session-in-dir"===f)d=!0;else{if("--no-create-session-in-dir"!==f)return makeError(`Unknown argument: ${f}\nRun 'context remote-control --help' for usage.`);d=!1}else m=!0}return"single-session"===n&&void 0!==l?makeError("--capacity cannot be used with --spawn=session (single-session mode has fixed capacity 1)."):!c&&!m||void 0===n&&void 0===l&&void 0===d?c&&m?makeError("--session-id and --continue cannot be used together."):{verbose:p,sandbox:u,debugFile:t,sessionTimeoutMs:o,permissionMode:r,name:i,spawnMode:n,capacity:l,createSessionInDir:d,sessionId:c,continueSession:m,help:g}:makeError("--session-id and --continue cannot be used with --spawn, --capacity, or --create-session-in-dir.");function makeError(e){return{verbose:p,sandbox:u,debugFile:t,sessionTimeoutMs:o,permissionMode:r,name:i,spawnMode:n,capacity:l,createSessionInDir:d,sessionId:c,continueSession:m,help:g,error:e}}}const X=80;function deriveSessionTitle(e){const s=e.replace(/\s+/g," ").trim();return k(s,X)}async function fetchSessionTitle(e,s){const{getBridgeSession:t}=await import("./createSession.js"),o=await t(e,{baseUrl:s});return o?.title||void 0}export async function bridgeMain(r){const n=parseArgs(r);if(n.help)return void await async function(){const{EXTERNAL_PERMISSION_MODES:e}=await import("../types/permissions.js"),t=e.join(", "),o=await isMultiSessionSpawnEnabled(),r=o?" --spawn <mode> Spawn mode: same-dir, worktree, session\n (default: same-dir)\n --capacity <N> Max concurrent sessions in worktree or\n same-dir mode (default: 32)\n --[no-]create-session-in-dir Pre-create a session in the current\n directory; in worktree mode this session\n stays in cwd while on-demand sessions get\n isolated worktrees (default: on)\n":"",i=o?"\n Remote Control runs as a persistent server that accepts multiple concurrent\n sessions in the current directory. One session is pre-created on start so\n you have somewhere to type immediately. Use --spawn=worktree to isolate\n each on-demand session in its own git worktree, or --spawn=session for\n the classic single-session mode (exits when that session ends). Press 'w'\n during runtime to toggle between same-dir and worktree.\n":"",n=o?" - Worktree mode requires a git repository or WorktreeCreate/WorktreeRemove hooks\n":"",a=`\nRemote Control - Connect your local environment to claude.ai/code\n\nUSAGE\n context remote-control [options]\nOPTIONS\n --name <name> Name for the session (shown in claude.ai/code)\n${s("KAIROS")?" -c, --continue Resume the last session in this directory\n --session-id <id> Resume a specific session by ID (cannot be\n used with spawn flags or --continue)\n":""} --permission-mode <mode> Permission mode for spawned sessions\n (${t})\n --debug-file <path> Write debug logs to file\n -v, --verbose Enable verbose output\n -h, --help Show this help\n${r}\nDESCRIPTION\n Remote Control allows you to control sessions on your local device from\n claude.ai/code (https://claude.ai/code). Run this command in the\n directory you want to work in, then connect from the Claude app or web.\n${i}\nNOTES\n - Debes iniciar sesión con una cuenta Context que tenga suscripción\n - Run \`context\` first in the directory to accept the workspace trust dialog\n${n}`;console.log(a)}();n.error&&(console.error(`Error: ${n.error}`),process.exit(1));const{verbose:l,sandbox:p,debugFile:m,sessionTimeoutMs:_,permissionMode:h,name:k,spawnMode:$,capacity:y,createSessionInDir:M,sessionId:R,continueSession:x}=n;let D,A=R;const U=void 0!==$||void 0!==y||void 0!==M;if(void 0!==h){const{PERMISSION_MODES:e}=await import("../types/permissions.js"),s=e;s.includes(h)||(console.error(`Error: Invalid permission mode '${h}'. Valid modes: ${s.join(", ")}`),process.exit(1))}const j=a("."),{enableConfigs:F,checkHasTrustDialogAccepted:G}=await import("../utils/config.js");F();const{initSinks:V}=await import("../utils/sinks.js");V();const H=await isMultiSessionSpawnEnabled();U&&!H&&(await g("tengu_bridge_multi_session_denied",{used_spawn:void 0!==$,used_capacity:void 0!==y,used_create_session_in_dir:void 0!==M}),await Promise.race([Promise.all([c(),d()]),S(500,void 0,{unref:!0})]).catch(()=>{}),console.error("Error: Multi-session Remote Control is not enabled for your account yet."),process.exit(1));const{setOriginalCwd:L,setCwdState:z}=await import("../bootstrap/state.js");L(j),z(j),G()||(console.error(`Error: Workspace not trusted. Please run \`context\` in ${j} first to review and accept the workspace trust dialog.`),process.exit(1));const{clearOAuthTokenCache:q,checkAndRefreshOAuthTokenIfNeeded:K}=await import("../utils/auth.js"),{getBridgeAccessToken:Y,getBridgeBaseUrl:J}=await import("./bridgeConfig.js");Y()||(console.error(N),process.exit(1));const{getGlobalConfig:X,saveGlobalConfig:Q,getCurrentProjectConfig:Z,saveCurrentProjectConfig:ee}=await import("../utils/config.js");if(!X().remoteDialogSeen){const e=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log("\nRemote Control lets you access this CLI session from the web (claude.ai/code)\nor the Claude app, so you can pick up where you left off on any device.\n\nYou can disconnect remote access anytime by running /remote-control again.\n");const s=await new Promise(s=>{e.question("Enable Remote Control? (y/n) ",s)});e.close(),Q(e=>e.remoteDialogSeen?e:{...e,remoteDialogSeen:!0}),"y"!==s.toLowerCase()&&"yes"!==s.toLowerCase()&&process.exit(0)}if(s("KAIROS")&&x){const{readBridgePointerAcrossWorktrees:e}=await import("./bridgePointer.js"),s=await e(j);s||(console.error("Error: No recent session found in this directory or its worktrees. Run `context remote-control` to start a new one."),process.exit(1));const{pointer:t,dir:o}=s,r=Math.round(t.ageMs/6e4),i=r<60?`${r}m`:`${Math.round(r/60)}h`,n=o!==j?` from worktree ${o}`:"";console.error(`Resuming session ${t.sessionId} (${i} ago)${n}…`),A=t.sessionId,D=o}const se=J();!se.startsWith("http://")||se.includes("localhost")||se.includes("127.0.0.1")||(console.error("Error: Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed."),process.exit(1));const te="ant"===process.env.USER_TYPE?process.env.CONTEXT_BRIDGE_SESSION_INGRESS_URL??process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL??se:se,{getBranch:oe,getRemoteUrl:re,findGitRoot:ie}=await import("../utils/git.js"),{hasWorktreeCreateHook:ne}=await import("../utils/hooks.js"),ae=ne()||null!==ie(j);let le,de,ce=H?Z().remoteControlSpawnMode:void 0;if("worktree"!==ce||ae||(console.error("Warning: Saved spawn mode is worktree but this directory is not a git repository. Falling back to same-dir."),ce=void 0,ee(e=>void 0===e.remoteControlSpawnMode?e:{...e,remoteControlSpawnMode:void 0})),H&&!ce&&ae&&void 0===$&&!A&&process.stdin.isTTY){const e=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log("\nClaude Remote Control is launching in spawn mode which lets you create new sessions in this project from Context Code on Web or your Mobile app. Learn more here: https://code.claude.com/docs/en/remote-control\n\nSpawn mode for this project:\n [1] same-dir — sessions share the current directory (default)\n [2] worktree — each session gets an isolated git worktree\n\nThis can be changed later or explicitly set with --spawn=same-dir or --spawn=worktree.\n");const s=await new Promise(s=>{e.question("Choose [1/2] (default: 1): ",s)});e.close();const t="2"===s.trim()?"worktree":"same-dir";ce=t,u("tengu_bridge_spawn_mode_chosen",{spawn_mode:t}),ee(e=>e.remoteControlSpawnMode===t?e:{...e,remoteControlSpawnMode:t})}A?(de="single-session",le="resume"):void 0!==$?(de=$,le="flag"):void 0!==ce?(de=ce,le="saved"):(de=H?"same-dir":"single-session",le="gate_default");const pe="single-session"===de?1:y??32,ue=M??!0;if(!A){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(j)}"worktree"!==de||ae||(console.error("Error: Worktree mode requires a git repository or WorktreeCreate hooks configured. Use --spawn=session for single-session mode."),process.exit(1));const ge=await oe(),me=await re(),we=o(),fe=t(),{handleOAuth401Error:_e}=await import("../utils/auth.js"),he=E({baseUrl:se,getAccessToken:Y,runnerVersion:e.VERSION,onDebug:w,onAuth401:_e,getTrustedDeviceToken:O});let be;if(s("KAIROS")&&A){try{C(A,"sessionId")}catch{console.error(`Error: Invalid session ID "${A}". Session IDs must not contain unsafe characters.`),process.exit(1)}await K(),q();const{getBridgeSession:e}=await import("./createSession.js"),s=await e(A,{baseUrl:se,getAccessToken:Y});if(!s){if(D){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(`Error: Session ${A} not found. It may have been archived or expired, or your login may have lapsed (run \`context /login\`).`),process.exit(1)}if(!s.environment_id){if(D){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(`Error: Session ${A} has no environment_id. It may never have been attached to a bridge.`),process.exit(1)}be=s.environment_id,w(`[bridge:init] Resuming session ${A} on environment ${be}`)}const ke={dir:j,machineName:we,branch:ge,gitRepoUrl:me,maxSessions:pe,spawnMode:de,verbose:l,sandbox:p,bridgeId:fe,workerType:"claude_code",environmentId:t(),reuseEnvironmentId:be,apiBaseUrl:se,sessionIngressUrl:te,debugFile:m,sessionTimeoutMs:_};let ve,Se,$e;w(`[bridge:init] bridgeId=${fe}${be?` reuseEnvironmentId=${be}`:""} dir=${j} branch=${ge} gitRepoUrl=${me} machine=${we}`),w(`[bridge:init] apiBaseUrl=${se} sessionIngressUrl=${te}`),w(`[bridge:init] sandbox=${p}${m?` debugFile=${m}`:""}`);try{const e=await he.registerBridgeEnvironment(ke);ve=e.environment_id,Se=e.environment_secret}catch(e){u("tengu_bridge_registration_failed",{status:e instanceof I?e.status:void 0}),console.error(e instanceof I&&404===e.status?"Remote Control environments are not available for your account.":`Error: ${b(e)}`),process.exit(1)}if(s("KAIROS")&&A)if(be&&ve!==be)v(new Error(`Bridge resume env mismatch: requested ${be}, backend returned ${ve}. Falling back to fresh session.`)),console.warn(`Warning: Could not resume session ${A} — its environment has expired. Creating a fresh session instead.`);else{const e=B(A),s=e===A?[A]:[A,e];let t,o=!1;for(const e of s)try{await he.reconnectSession(ve,e),w(`[bridge:init] Session ${e} re-queued via bridge/reconnect`),$e=A,o=!0;break}catch(s){t=s,w(`[bridge:init] reconnectSession(${e}) failed: ${b(s)}`)}if(!o){const e=t,s=e instanceof I;if(D&&s){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(s?`Error: ${b(e)}`:`Error: Failed to reconnect session ${A}: ${b(e)}\nThe session may still be resumable — try running the same command again.`),process.exit(1)}}w(`[bridge:init] Registered, server environmentId=${ve}`);const ye=P();u("tengu_bridge_started",{max_sessions:ke.maxSessions,has_debug_file:!!ke.debugFile,sandbox:ke.sandbox,verbose:ke.verbose,heartbeat_interval_ms:ye.non_exclusive_heartbeat_interval_ms,spawn_mode:ke.spawnMode,spawn_mode_source:le,multi_session_gate:H,pre_create_session:ue,worktree_available:ae}),f("info","bridge_started",{max_sessions:ke.maxSessions,sandbox:ke.sandbox,spawn_mode:ke.spawnMode});const Ie=W({execPath:process.execPath,scriptArgs:spawnScriptArgs(),env:process.env,verbose:l,sandbox:p,debugFile:m,permissionMode:h,onDebug:w,onActivity:(e,s)=>{w(`[bridge:activity] sessionId=${e} ${s.type} ${s.summary}`)},onPermissionRequest:(e,s,t)=>{w(`[bridge:perm] sessionId=${e} tool=${s.request.tool_name} request_id=${s.request_id} (not auto-approving)`)}}),Ee=T({verbose:l}),{parseGitHubRepository:Me}=await import("../utils/detectRepository.js"),Re=me?Me(me):null,Ce=Re?Re.split("/").pop():i(j);Ee.setRepoInfo(Ce,ge);const xe="single-session"!==de&&ae;xe&&Ee.setSpawnModeDisplay(de);const onStdinData=e=>{if(3!==e[0]&&4!==e[0])if(32!==e[0]){if(119===e[0]){if(!xe)return;const e="same-dir"===ke.spawnMode?"worktree":"same-dir";return ke.spawnMode=e,u("tengu_bridge_spawn_mode_toggled",{spawn_mode:e}),Ee.logStatus("worktree"===e?"Spawn mode: worktree (new sessions get isolated git worktrees)":"Spawn mode: same-dir (new sessions share the current directory)"),Ee.setSpawnModeDisplay(e),Ee.refreshDisplay(),void ee(s=>s.remoteControlSpawnMode===e?s:{...s,remoteControlSpawnMode:e})}}else Ee.toggleQr();else process.emit("SIGINT")};process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.on("data",onStdinData));const Te=new AbortController,onSigint=()=>{w("[bridge:shutdown] SIGINT received, shutting down"),Te.abort()},onSigterm=()=>{w("[bridge:shutdown] SIGTERM received, shutting down"),Te.abort()};process.on("SIGINT",onSigint),process.on("SIGTERM",onSigterm);let De=s("KAIROS")&&$e?$e:null;if(ue&&(!s("KAIROS")||!$e)){const{createBridgeSession:e}=await import("./createSession.js");try{De=await e({environmentId:ve,title:k,events:[],gitRepoUrl:me,branch:ge,signal:Te.signal,baseUrl:se,getAccessToken:Y,permissionMode:h}),De&&w(`[bridge:init] Created initial session ${De}`)}catch(e){w(`[bridge:init] Session creation failed (non-fatal): ${b(e)}`)}}let Ae=null;if(De&&"single-session"===de){const{writeBridgePointer:e}=await import("./bridgePointer.js"),s={sessionId:De,environmentId:ve,source:"standalone"};await e(ke.dir,s),Ae=setInterval(e,36e5,ke.dir,s),Ae.unref?.()}try{await runBridgeLoop(ke,ve,Se,he,Ie,Ee,Te.signal,void 0,De??void 0,async()=>(q(),await K(),Y()))}finally{null!==Ae&&clearInterval(Ae),process.off("SIGINT",onSigint),process.off("SIGTERM",onSigterm),process.stdin.off("data",onStdinData),process.stdin.isTTY&&process.stdin.setRawMode(!1),process.stdin.pause()}process.exit(0)}export class BridgeHeadlessPermanentError extends Error{constructor(e){super(e),this.name="BridgeHeadlessPermanentError"}}export async function runBridgeHeadless(s,r){const{dir:i,log:n}=s;process.chdir(i);const{setOriginalCwd:a,setCwdState:l}=await import("../bootstrap/state.js");a(i),l(i);const{enableConfigs:d,checkHasTrustDialogAccepted:c}=await import("../utils/config.js");d();const{initSinks:p}=await import("../utils/sinks.js");if(p(),!c())throw new BridgeHeadlessPermanentError(`Workspace not trusted: ${i}. Run \`context\` in that directory first to accept the trust dialog.`);if(!s.getAccessToken())throw new Error(N);const{getBridgeBaseUrl:u}=await import("./bridgeConfig.js"),g=u();if(g.startsWith("http://")&&!g.includes("localhost")&&!g.includes("127.0.0.1"))throw new BridgeHeadlessPermanentError("Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed.");const m="ant"===process.env.USER_TYPE?process.env.CONTEXT_BRIDGE_SESSION_INGRESS_URL??process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL??g:g,{getBranch:w,getRemoteUrl:f,findGitRoot:_}=await import("../utils/git.js"),{hasWorktreeCreateHook:h}=await import("../utils/hooks.js");if("worktree"===s.spawnMode){if(!(h()||null!==_(i)))throw new BridgeHeadlessPermanentError(`Worktree mode requires a git repository or WorktreeCreate hooks. Directory ${i} has neither.`)}const k=await w(),v=await f(),S=o(),$=t(),y={dir:i,machineName:S,branch:k,gitRepoUrl:v,maxSessions:s.capacity,spawnMode:s.spawnMode,verbose:!1,sandbox:s.sandbox,bridgeId:$,workerType:"claude_code",environmentId:t(),apiBaseUrl:g,sessionIngressUrl:m,sessionTimeoutMs:s.sessionTimeoutMs},I=E({baseUrl:g,getAccessToken:s.getAccessToken,runnerVersion:e.VERSION,onDebug:n,onAuth401:s.onAuth401,getTrustedDeviceToken:O});let M,R;try{const e=await I.registerBridgeEnvironment(y);M=e.environment_id,R=e.environment_secret}catch(e){throw new Error(`Bridge registration failed: ${b(e)}`)}const C=W({execPath:process.execPath,scriptArgs:spawnScriptArgs(),env:process.env,verbose:!1,sandbox:s.sandbox,permissionMode:s.permissionMode,onDebug:n}),x=function(e){const noop=()=>{};return{printBanner:(s,t)=>e(`registered environmentId=${t} dir=${s.dir} spawnMode=${s.spawnMode} capacity=${s.maxSessions}`),logSessionStart:(s,t)=>e(`session start ${s}`),logSessionComplete:(s,t)=>e(`session complete ${s} (${t}ms)`),logSessionFailed:(s,t)=>e(`session failed ${s}: ${t}`),logStatus:e,logVerbose:e,logError:s=>e(`error: ${s}`),logReconnected:s=>e(`reconnected after ${s}ms`),addSession:(s,t)=>e(`session attached ${s}`),removeSession:s=>e(`session detached ${s}`),updateIdleStatus:noop,updateReconnectingStatus:noop,updateSessionStatus:noop,updateSessionActivity:noop,updateSessionCount:noop,updateFailedStatus:noop,setSpawnModeDisplay:noop,setRepoInfo:noop,setDebugLogPath:noop,setAttached:noop,setSessionTitle:noop,clearStatus:noop,toggleQr:noop,refreshDisplay:noop}}(n);let T;if(x.printBanner(y,M),s.createSessionOnStart){const{createBridgeSession:e}=await import("./createSession.js");try{const t=await e({environmentId:M,title:s.name,events:[],gitRepoUrl:v,branch:k,signal:r,baseUrl:g,getAccessToken:s.getAccessToken,permissionMode:s.permissionMode});t&&(T=t,n(`created initial session ${t}`))}catch(e){n(`session pre-creation failed (non-fatal): ${b(e)}`)}}await runBridgeLoop(y,M,R,I,C,x,r,void 0,T,async()=>s.getAccessToken())}
|
|
1
|
+
import{MACRO as e,feature as s}from"../recovery/bunBundleShim.js";import{randomUUID as t}from"crypto";import{hostname as o,tmpdir as r}from"os";import{basename as i,join as n,resolve as a}from"path";import{getRemoteSessionUrl as l}from"../constants/product.js";import{shutdownDatadog as d}from"../services/analytics/datadog.js";import{shutdown1PEventLogging as c}from"../services/analytics/firstPartyEventLogger.js";import{checkGate_CACHED_OR_BLOCKING as p}from"../services/analytics/growthbook.js";import{logEvent as u,logEventAsync as g}from"../services/analytics/index.js";import{isInBundledMode as m}from"../utils/bundledMode.js";import{logForDebugging as w}from"../utils/debug.js";import{logForDiagnosticsNoPII as f}from"../utils/diagLogs.js";import{isEnvTruthy as _,isInProtectedNamespace as h}from"../utils/envUtils.js";import{errorMessage as b}from"../utils/errors.js";import{truncateToWidth as k}from"../utils/format.js";import{logError as v}from"../utils/log.js";import{sleep as S}from"../utils/sleep.js";import{createAgentWorktree as $,removeAgentWorktree as y}from"../utils/worktree.js";import{BridgeFatalError as I,createBridgeApiClient as E,isExpiredErrorType as M,isSuppressible403 as R,validateBridgeId as C}from"./bridgeApi.js";import{formatDuration as x}from"./bridgeStatusUtil.js";import{createBridgeLogger as T}from"./bridgeUI.js";import{createCapacityWake as D}from"./capacityWake.js";import{describeAxiosError as A}from"./debugUtils.js";import{createTokenRefreshScheduler as U}from"./jwtUtils.js";import{getPollIntervalConfig as P}from"./pollConfig.js";import{toCompatSessionId as j,toInfraSessionId as B}from"./sessionIdCompat.js";import{createSessionSpawner as W,safeFilenameId as F}from"./sessionRunner.js";import{getTrustedDeviceToken as O}from"./trustedDevice.js";import{BRIDGE_LOGIN_ERROR as N,DEFAULT_SESSION_TIMEOUT_MS as G}from"./types.js";import{buildCCRv2SdkUrl as V,buildSdkUrl as H,decodeWorkSecret as L,registerWorker as z,sameSessionId as q}from"./workSecret.js";const K={connInitialMs:2e3,connCapMs:12e4,connGiveUpMs:6e5,generalInitialMs:500,generalCapMs:3e4,generalGiveUpMs:6e5};async function isMultiSessionSpawnEnabled(){return p("tengu_ccr_bridge_multi_session")}function pollSleepDetectionThresholdMs(e){return 2*e.connCapMs}function spawnScriptArgs(){return m()||!process.argv[1]?[]:[process.argv[1]]}function safeSpawn(e,s,t){try{return e.spawn(s,t)}catch(e){const s=b(e);return v(new Error(`Session spawn failed: ${s}`)),s}}export async function runBridgeLoop(e,t,o,i,a,d,c,p=K,g,m){const k=new AbortController;c.aborted?k.abort():c.addEventListener("abort",()=>k.abort(),{once:!0});const E=k.signal,T=new Map,B=new Map,W=new Map,O=new Map,N=new Map,Y=new Map,J=new Set,X=new Map,Q=new Set,Z=new Set,ee=D(E);async function heartbeatActiveWorkItems(){let e=!1,s=!1;const o=[];for(const[r]of T){const n=W.get(r),a=N.get(r);if(n&&a)try{await i.heartbeatWork(t,n,a),e=!0}catch(e){w(`[bridge:heartbeat] Failed for sessionId=${r} workId=${n}: ${b(e)}`),e instanceof I&&(u("tengu_bridge_heartbeat_error",{status:e.status,error_type:401===e.status||403===e.status?"auth_failed":"fatal"}),401===e.status||403===e.status?o.push(r):s=!0)}}for(const e of o){d.logVerbose(`Session ${e} token expired — re-queuing via bridge/reconnect`);try{await i.reconnectSession(t,e),w(`[bridge:heartbeat] Re-queued sessionId=${e} via bridge/reconnect`)}catch(s){d.logError(`Failed to refresh session ${e} token: ${b(s)}`),w(`[bridge:heartbeat] reconnectSession(${e}) failed: ${b(s)}`,{level:"error"})}}return s?"fatal":o.length>0?"auth_failed":e?"ok":"failed"}const se=new Set,te=m?U({getAccessToken:m,onRefresh:(e,s)=>{const o=T.get(e);o&&(se.has(e)?(d.logVerbose(`Refreshing session ${e} token via bridge/reconnect`),i.reconnectSession(t,e).catch(s=>{d.logError(`Failed to refresh session ${e} token: ${b(s)}`),w(`[bridge:token] reconnectSession(${e}) failed: ${b(s)}`,{level:"error"})})):o.updateAccessToken(s))},label:"bridge"}):null,oe=Date.now(),re=new Set;function trackCleanup(e){re.add(e),e.finally(()=>re.delete(e))}let ie=0,ne=0,ae=null,le=null,de=null,ce=null,pe=!1;if(w(`[bridge:work] Starting poll loop spawnMode=${e.spawnMode} maxSessions=${e.maxSessions} environmentId=${t}`),f("info","bridge_loop_started",{max_sessions:e.maxSessions,spawn_mode:e.spawnMode}),"ant"===process.env.USER_TYPE){let s;if(e.debugFile){const t=e.debugFile.lastIndexOf(".");s=t>0?`${e.debugFile.slice(0,t)}-*${e.debugFile.slice(t)}`:`${e.debugFile}-*`}else s=n(r(),"claude","bridge-session-*.log");d.setDebugLogPath(s)}function updateStatusDisplay(){d.updateSessionCount(T.size,e.maxSessions,e.spawnMode);for(const[e,s]of T){const t=s.currentActivity;t&&d.updateSessionActivity(O.get(e)??e,t)}if(0===T.size)return void d.updateIdleStatus();const[s,t]=[...T.entries()].pop(),o=B.get(s);if(!o)return;const r=t.currentActivity;if(!r||"result"===r.type||"error"===r.type)return void(e.maxSessions>1&&d.refreshDisplay());const i=x(Date.now()-o),n=t.activities.filter(e=>"tool_start"===e.type).slice(-5).map(e=>e.summary);d.updateSessionStatus(s,i,r,n)}function startStatusUpdates(){stopStatusUpdates(),updateStatusDisplay(),ce=setInterval(updateStatusDisplay,1e3)}function stopStatusUpdates(){ce&&(clearInterval(ce),ce=null)}function onSessionDone(s,o,r){return n=>{const a=W.get(s);T.delete(s),B.delete(s),W.delete(s),N.delete(s);const l=O.get(s)??s;O.delete(s),d.removeSession(l),Z.delete(l),se.delete(s);const c=Y.get(s);c&&(clearTimeout(c),Y.delete(s)),te?.cancel(s),ee.wake();const g=Q.delete(s),m=g&&"interrupted"===n?"failed":n,_=Date.now()-o;w(`[bridge:session] sessionId=${s} workId=${a??"unknown"} exited status=${m} duration=${x(_)}`),u("tengu_bridge_session_done",{status:m,duration_ms:_}),f("info","bridge_session_done",{status:m,duration_ms:_}),d.clearStatus(),stopStatusUpdates();const h=r.lastStderr.length>0?r.lastStderr.join("\n"):void 0;let S;switch(m){case"completed":d.logSessionComplete(s,_);break;case"failed":g||E.aborted||(S=h??"Process exited with error",d.logSessionFailed(s,S),v(new Error(`Bridge session failed: ${S}`)));break;case"interrupted":d.logVerbose(`Session ${s} interrupted`)}"interrupted"!==m&&a&&(trackCleanup(stopWorkWithRetry(i,t,a,d,p.stopWorkBaseDelayMs)),J.add(a));const $=X.get(s);if($&&(X.delete(s),trackCleanup(y($.worktreePath,$.worktreeBranch,$.gitRoot,$.hookBased).catch(e=>d.logVerbose(`Failed to remove worktree ${$.worktreePath}: ${b(e)}`)))),"interrupted"!==m&&!E.aborted){if("single-session"===e.spawnMode)return w(`[bridge:session] Session ${m}, aborting poll loop to tear down environment`),void k.abort();trackCleanup(i.archiveSession(l).catch(e=>d.logVerbose(`Failed to archive session ${s}: ${b(e)}`))),w(`[bridge:session] Session ${m}, returning to idle (multi-session mode)`)}E.aborted||startStatusUpdates()}}for(d.printBanner(e,t),d.updateSessionCount(0,e.maxSessions,e.spawnMode),g&&d.setAttached(g),g||startStatusUpdates();!E.aborted;){const s=P();try{const c=await i.pollForWork(t,o,E,s.reclaim_older_than_ms);if(null!==ae||null!==le){const e=Date.now()-(ae??le??Date.now());d.logReconnected(e),w(`[bridge:poll] Reconnected after ${x(e)}`),u("tengu_bridge_reconnected",{disconnected_ms:e})}if(ie=0,ne=0,ae=null,le=null,de=null,!c){if(T.size>=e.maxSessions){const t=s.multisession_poll_interval_ms_at_capacity;if(s.non_exclusive_heartbeat_interval_ms>0){u("tengu_bridge_heartbeat_mode_entered",{active_sessions:T.size,heartbeat_interval_ms:s.non_exclusive_heartbeat_interval_ms});const o=t>0?Date.now()+t:null;let r="ok",i=0;for(;!E.aborted&&T.size>=e.maxSessions&&(null===o||Date.now()<o);){const e=P();if(e.non_exclusive_heartbeat_interval_ms<=0)break;const s=ee.signal();if(r=await heartbeatActiveWorkItems(),"auth_failed"===r||"fatal"===r){s.cleanup();break}i++,await S(e.non_exclusive_heartbeat_interval_ms,s.signal),s.cleanup()}const n="auth_failed"===r||"fatal"===r?r:E.aborted?"shutdown":T.size<e.maxSessions?"capacity_changed":null!==o&&Date.now()>=o?"poll_due":"config_disabled";if(u("tengu_bridge_heartbeat_mode_exited",{reason:n,heartbeat_cycles:i,active_sessions:T.size}),"poll_due"===n&&w(`[bridge:poll] Heartbeat poll_due after ${i} cycles — falling through to pollForWork`),"auth_failed"===r||"fatal"===r){const e=ee.signal();await S(t>0?t:s.non_exclusive_heartbeat_interval_ms,e.signal),e.cleanup()}}else if(t>0){const e=ee.signal();await S(t,e.signal),e.cleanup()}}else{const e=T.size>0?s.multisession_poll_interval_ms_partial_capacity:s.multisession_poll_interval_ms_not_at_capacity;await S(e,E)}continue}const m=T.size>=e.maxSessions;if(J.has(c.id)){if(w(`[bridge:work] Skipping already-completed workId=${c.id}`),m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}else await S(1e3,E);continue}let k;try{k=L(c.secret)}catch(e){const o=b(e);if(d.logError(`Failed to decode work secret for workId=${c.id}: ${o}`),u("tengu_bridge_work_secret_failed",{}),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs)),m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}continue}const ackWork=async()=>{w(`[bridge:work] Acknowledging workId=${c.id}`);try{await i.acknowledgeWork(t,c.id,k.session_ingress_token)}catch(e){w(`[bridge:work] Acknowledge failed workId=${c.id}: ${b(e)}`)}},I=c.data.type;switch(c.data.type){case"healthcheck":await ackWork(),w("[bridge:work] Healthcheck received"),d.logVerbose("Healthcheck received");break;case"session":{const s=c.data.id;try{C(s,"session_id")}catch{await ackWork(),d.logError(`Invalid session_id received: ${s}`);break}const o=T.get(s);if(o){o.updateAccessToken(k.session_ingress_token),N.set(s,k.session_ingress_token),W.set(s,c.id),te?.schedule(s,k.session_ingress_token),w(`[bridge:work] Updated access token for existing sessionId=${s} workId=${c.id}`),await ackWork();break}if(T.size>=e.maxSessions){w(`[bridge:work] At capacity (${T.size}/${e.maxSessions}), cannot spawn new session for workId=${c.id}`);break}await ackWork();const m=Date.now();let I,M,R=!1;if(!0===k.use_code_sessions||_(process.env.CONTEXT_BRIDGE_USE_CCR_V2)||_(process.env.CLAUDE_BRIDGE_USE_CCR_V2)){I=V(e.apiBaseUrl,s);for(let e=1;e<=2;e++)try{M=await z(I,k.session_ingress_token),R=!0,w(`[bridge:session] CCR v2: registered worker sessionId=${s} epoch=${M} attempt=${e}`);break}catch(o){const r=b(o);if(e<2){if(w(`[bridge:session] CCR v2: registerWorker attempt ${e} failed, retrying: ${r}`),await S(2e3,E),E.aborted)break;continue}d.logError(`CCR v2 worker registration failed for session ${s}: ${r}`),v(new Error(`registerWorker failed: ${r}`)),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs))}if(!R)break}else I=H(e.sessionIngressUrl,s);const x=e.spawnMode;let D=e.dir,A=0;if("worktree"===x&&(void 0===g||!q(s,g))){const e=Date.now();try{const t=await $(`bridge-${F(s)}`);A=Date.now()-e,X.set(s,{worktreePath:t.worktreePath,worktreeBranch:t.worktreeBranch,gitRoot:t.gitRoot,hookBased:t.hookBased}),D=t.worktreePath,w(`[bridge:session] Created worktree for sessionId=${s} at ${t.worktreePath}`)}catch(e){const o=b(e);d.logError(`Failed to create worktree for session ${s}: ${o}`),v(new Error(`Worktree creation failed: ${o}`)),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs));break}}w(`[bridge:session] Spawning sessionId=${s} sdkUrl=${I}`);const U=j(s),P=safeSpawn(a,{sessionId:s,sdkUrl:I,accessToken:k.session_ingress_token,useCcrV2:R,workerEpoch:M,onFirstUserMessage:s=>{if(Z.has(U))return;Z.add(U);const t=deriveSessionTitle(s);d.setSessionTitle(U,t),w(`[bridge:title] derived title for ${U}: ${t}`),import("./createSession.js").then(({updateBridgeSessionTitle:s})=>s(U,t,{baseUrl:e.apiBaseUrl})).catch(e=>w(`[bridge:title] failed to update title for ${U}: ${e}`,{level:"error"}))}},D);if("string"==typeof P){d.logError(`Failed to spawn session ${s}: ${P}`);const e=X.get(s);e&&(X.delete(s),trackCleanup(y(e.worktreePath,e.worktreeBranch,e.gitRoot,e.hookBased).catch(s=>d.logVerbose(`Failed to remove worktree ${e.worktreePath}: ${b(s)}`)))),J.add(c.id),trackCleanup(stopWorkWithRetry(i,t,c.id,d,p.stopWorkBaseDelayMs));break}const L=P,K=Date.now()-m;u("tengu_bridge_session_started",{active_sessions:T.size,spawn_mode:x,in_worktree:X.has(s),spawn_duration_ms:K,worktree_create_ms:A,inProtectedNamespace:h()}),f("info","bridge_session_started",{spawn_mode:x,in_worktree:X.has(s),spawn_duration_ms:K,worktree_create_ms:A}),T.set(s,L),W.set(s,c.id),N.set(s,k.session_ingress_token),O.set(s,U);const ee=Date.now();B.set(s,ee),d.logSessionStart(s,`Session ${s}`);const oe=F(s);let re;if(e.debugFile){const s=e.debugFile.lastIndexOf(".");re=s>0?`${e.debugFile.slice(0,s)}-${oe}${e.debugFile.slice(s)}`:`${e.debugFile}-${oe}`}else(e.verbose||"ant"===process.env.USER_TYPE)&&(re=n(r(),"claude",`bridge-session-${oe}.log`));re&&d.logVerbose(`Debug log: ${re}`),d.addSession(U,l(U,e.sessionIngressUrl)),startStatusUpdates(),d.setAttached(U),fetchSessionTitle(U,e.apiBaseUrl).then(e=>{e&&T.has(s)&&(Z.add(U),d.setSessionTitle(U,e),w(`[bridge:title] server title for ${U}: ${e}`))}).catch(e=>w(`[bridge:title] failed to fetch title for ${U}: ${e}`,{level:"error"}));const ie=e.sessionTimeoutMs??G;if(ie>0){const e=setTimeout(onSessionTimeout,ie,s,ie,d,Q,L);Y.set(s,e)}R&&se.add(s),te?.schedule(s,k.session_ingress_token),L.done.then(onSessionDone(s,ee,L));break}default:await ackWork(),w(`[bridge:work] Unknown work type: ${I}, skipping`)}if(m){const e=ee.signal();s.non_exclusive_heartbeat_interval_ms>0?(await heartbeatActiveWorkItems(),await S(s.non_exclusive_heartbeat_interval_ms,e.signal)):s.multisession_poll_interval_ms_at_capacity>0&&await S(s.multisession_poll_interval_ms_at_capacity,e.signal),e.cleanup()}}catch(e){if(E.aborted)break;if(e instanceof I){pe=!0,M(e.errorType)?d.logStatus(e.message):R(e)?w(`[bridge:work] Suppressed 403 error: ${e.message}`):(d.logError(e.message),v(e)),u("tengu_bridge_fatal_error",{status:e.status,error_type:e.errorType}),f(M(e.errorType)?"info":"error","bridge_fatal_error",{status:e.status,error_type:e.errorType});break}const s=A(e);if(isConnectionError(e)||isServerError(e)){const e=Date.now();null!==de&&e-de>pollSleepDetectionThresholdMs(p)&&(w(`[bridge:work] Detected system sleep (${Math.round((e-de)/1e3)}s gap), resetting error budget`),f("info","bridge_poll_sleep_detected",{gapMs:e-de}),ae=null,ie=0,le=null,ne=0),de=e,ae||(ae=e);const t=e-ae;if(t>=p.connGiveUpMs){d.logError(`Server unreachable for ${Math.round(t/6e4)} minutes, giving up.`),u("tengu_bridge_poll_give_up",{error_type:"connection",elapsed_ms:t}),f("error","bridge_poll_give_up",{error_type:"connection",elapsed_ms:t}),pe=!0;break}le=null,ne=0,ie=ie?Math.min(2*ie,p.connCapMs):p.connInitialMs;const o=addJitter(ie);d.logVerbose(`Connection error, retrying in ${formatDelay(o)} (${Math.round(t/1e3)}s elapsed): ${s}`),d.updateReconnectingStatus(formatDelay(o),x(t)),P().non_exclusive_heartbeat_interval_ms>0&&await heartbeatActiveWorkItems(),await S(o,E)}else{const e=Date.now();null!==de&&e-de>pollSleepDetectionThresholdMs(p)&&(w(`[bridge:work] Detected system sleep (${Math.round((e-de)/1e3)}s gap), resetting error budget`),f("info","bridge_poll_sleep_detected",{gapMs:e-de}),ae=null,ie=0,le=null,ne=0),de=e,le||(le=e);const t=e-le;if(t>=p.generalGiveUpMs){d.logError(`Persistent errors for ${Math.round(t/6e4)} minutes, giving up.`),u("tengu_bridge_poll_give_up",{error_type:"general",elapsed_ms:t}),f("error","bridge_poll_give_up",{error_type:"general",elapsed_ms:t}),pe=!0;break}ae=null,ie=0,ne=ne?Math.min(2*ne,p.generalCapMs):p.generalInitialMs;const o=addJitter(ne);d.logVerbose(`Poll failed, retrying in ${formatDelay(o)} (${Math.round(t/1e3)}s elapsed): ${s}`),d.updateReconnectingStatus(formatDelay(o),x(t)),P().non_exclusive_heartbeat_interval_ms>0&&await heartbeatActiveWorkItems(),await S(o,E)}}}stopStatusUpdates(),d.clearStatus();const ue=Date.now()-oe;u("tengu_bridge_shutdown",{active_sessions:T.size,loop_duration_ms:ue}),f("info","bridge_shutdown",{active_sessions:T.size,loop_duration_ms:ue});const ge=new Set(T.keys());g&&ge.add(g);const me=new Map(O);if(T.size>0){w(`[bridge:shutdown] Shutting down ${T.size} active session(s)`),d.logStatus(`Shutting down ${T.size} active session(s)…`);const e=new Map(W);for(const[e,s]of T.entries())w(`[bridge:shutdown] Sending SIGTERM to sessionId=${e}`),s.kill();const s=new AbortController;await Promise.race([Promise.allSettled([...T.values()].map(e=>e.done)),S(p.shutdownGraceMs??3e4,s.signal)]),s.abort();for(const[e,s]of T.entries())w(`[bridge:shutdown] Force-killing stuck sessionId=${e}`),s.forceKill();for(const e of Y.values())clearTimeout(e);if(Y.clear(),te?.cancelAll(),X.size>0){const e=[...X.values()];X.clear(),w(`[bridge:shutdown] Cleaning up ${e.length} worktree(s)`),await Promise.allSettled(e.map(e=>y(e.worktreePath,e.worktreeBranch,e.gitRoot,e.hookBased)))}await Promise.allSettled([...e.entries()].map(([e,s])=>i.stopWork(t,s,!0).catch(t=>d.logVerbose(`Failed to stop work ${s} for session ${e}: ${b(t)}`))))}if(re.size>0&&await Promise.allSettled([...re]),s("KAIROS")&&"single-session"===e.spawnMode&&g&&!pe)return d.logStatus("Resume this session by running `context remote-control --continue`"),void w(`[bridge:shutdown] Skipping archive+deregister to allow resume of session ${g}`);ge.size>0&&(w(`[bridge:shutdown] Archiving ${ge.size} session(s)`),await Promise.allSettled([...ge].map(e=>i.archiveSession(me.get(e)??j(e)).catch(s=>d.logVerbose(`Failed to archive session ${e}: ${b(s)}`)))));try{await i.deregisterEnvironment(t),w("[bridge:shutdown] Environment deregistered, bridge offline"),d.logVerbose("Environment deregistered.")}catch(e){d.logVerbose(`Failed to deregister environment: ${b(e)}`)}const{clearBridgePointer:we}=await import("./bridgePointer.js");await we(e.dir),d.logVerbose("Environment offline.")}const Y=new Set(["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENETUNREACH","EHOSTUNREACH"]);export function isConnectionError(e){return!!(e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code&&Y.has(e.code))}export function isServerError(e){return!!e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code&&"ERR_BAD_RESPONSE"===e.code}function addJitter(e){return Math.max(0,e+.25*e*(2*Math.random()-1))}function formatDelay(e){return e>=1e3?`${(e/1e3).toFixed(1)}s`:`${Math.round(e)}ms`}async function stopWorkWithRetry(e,s,t,o,r=1e3){for(let i=1;i<=3;i++)try{return await e.stopWork(s,t,!1),void w(`[bridge:work] stopWork succeeded for workId=${t} on attempt ${i}/3`)}catch(e){if(e instanceof I)return R(e)?w(`[bridge:work] Suppressed stopWork 403 for ${t}: ${e.message}`):o.logError(`Failed to stop work ${t}: ${e.message}`),void f("error","bridge_stop_work_failed",{attempts:i,fatal:!0});const s=b(e);if(i<3){const e=addJitter(r*Math.pow(2,i-1));o.logVerbose(`Failed to stop work ${t} (attempt ${i}/3), retrying in ${formatDelay(e)}: ${s}`),await S(e)}else o.logError(`Failed to stop work ${t} after 3 attempts: ${s}`),f("error","bridge_stop_work_failed",{attempts:3})}}function onSessionTimeout(e,s,t,o,r){w(`[bridge:session] sessionId=${e} timed out after ${x(s)}`),u("tengu_bridge_session_timeout",{timeout_ms:s}),t.logSessionFailed(e,`Session timed out after ${x(s)}`),o.add(e),r.kill()}const J=["session","same-dir","worktree"];function parseSpawnValue(e){return"session"===e?"single-session":"same-dir"===e?"same-dir":"worktree"===e?"worktree":`--spawn requires one of: ${J.join(", ")} (got: ${e??"<missing>"})`}function parseCapacityValue(e){const s=void 0===e?NaN:parseInt(e,10);return isNaN(s)||s<1?`--capacity requires a positive integer (got: ${e??"<missing>"})`:s}export function parseArgs(e){let t,o,r,i,n,l,d,c,p=!1,u=!1,g=!1,m=!1;for(let w=0;w<e.length;w++){const f=e[w];if("--help"===f||"-h"===f)g=!0;else if("--verbose"===f||"-v"===f)p=!0;else if("--sandbox"===f)u=!0;else if("--no-sandbox"===f)u=!1;else if("--debug-file"===f&&w+1<e.length)t=a(e[++w]);else if(f.startsWith("--debug-file="))t=a(f.slice(13));else if("--session-timeout"===f&&w+1<e.length)o=1e3*parseInt(e[++w],10);else if(f.startsWith("--session-timeout="))o=1e3*parseInt(f.slice(18),10);else if("--permission-mode"===f&&w+1<e.length)r=e[++w];else if(f.startsWith("--permission-mode="))r=f.slice(18);else if("--name"===f&&w+1<e.length)i=e[++w];else if(f.startsWith("--name="))i=f.slice(7);else if(s("KAIROS")&&"--session-id"===f&&w+1<e.length){if(c=e[++w],!c)return makeError("--session-id requires a value")}else if(s("KAIROS")&&f.startsWith("--session-id=")){if(c=f.slice(13),!c)return makeError("--session-id requires a value")}else if(!s("KAIROS")||"--continue"!==f&&"-c"!==f)if("--spawn"===f||f.startsWith("--spawn=")){if(void 0!==n)return makeError("--spawn may only be specified once");const s=parseSpawnValue(f.startsWith("--spawn=")?f.slice(8):e[++w]);if("single-session"!==s&&"same-dir"!==s&&"worktree"!==s)return makeError(s);n=s}else if("--capacity"===f||f.startsWith("--capacity=")){if(void 0!==l)return makeError("--capacity may only be specified once");const s=parseCapacityValue(f.startsWith("--capacity=")?f.slice(11):e[++w]);if("number"!=typeof s)return makeError(s);l=s}else if("--create-session-in-dir"===f)d=!0;else{if("--no-create-session-in-dir"!==f)return makeError(`Unknown argument: ${f}\nRun 'context remote-control --help' for usage.`);d=!1}else m=!0}return"single-session"===n&&void 0!==l?makeError("--capacity cannot be used with --spawn=session (single-session mode has fixed capacity 1)."):!c&&!m||void 0===n&&void 0===l&&void 0===d?c&&m?makeError("--session-id and --continue cannot be used together."):{verbose:p,sandbox:u,debugFile:t,sessionTimeoutMs:o,permissionMode:r,name:i,spawnMode:n,capacity:l,createSessionInDir:d,sessionId:c,continueSession:m,help:g}:makeError("--session-id and --continue cannot be used with --spawn, --capacity, or --create-session-in-dir.");function makeError(e){return{verbose:p,sandbox:u,debugFile:t,sessionTimeoutMs:o,permissionMode:r,name:i,spawnMode:n,capacity:l,createSessionInDir:d,sessionId:c,continueSession:m,help:g,error:e}}}const X=80;function deriveSessionTitle(e){const s=e.replace(/\s+/g," ").trim();return k(s,X)}async function fetchSessionTitle(e,s){const{getBridgeSession:t}=await import("./createSession.js"),o=await t(e,{baseUrl:s});return o?.title||void 0}export async function bridgeMain(r){const n=parseArgs(r);if(n.help)return void await async function(){const{EXTERNAL_PERMISSION_MODES:e}=await import("../types/permissions.js"),t=e.join(", "),o=await isMultiSessionSpawnEnabled(),r=o?" --spawn <mode> Spawn mode: same-dir, worktree, session\n (default: same-dir)\n --capacity <N> Max concurrent sessions in worktree or\n same-dir mode (default: 32)\n --[no-]create-session-in-dir Pre-create a session in the current\n directory; in worktree mode this session\n stays in cwd while on-demand sessions get\n isolated worktrees (default: on)\n":"",i=o?"\n Remote Control runs as a persistent server that accepts multiple concurrent\n sessions in the current directory. One session is pre-created on start so\n you have somewhere to type immediately. Use --spawn=worktree to isolate\n each on-demand session in its own git worktree, or --spawn=session for\n the classic single-session mode (exits when that session ends). Press 'w'\n during runtime to toggle between same-dir and worktree.\n":"",n=o?" - Worktree mode requires a git repository or WorktreeCreate/WorktreeRemove hooks\n":"",a=`\nRemote Control - Connect your local environment to claude.ai/code\n\nUSAGE\n context remote-control [options]\nOPTIONS\n --name <name> Name for the session (shown in claude.ai/code)\n${s("KAIROS")?" -c, --continue Resume the last session in this directory\n --session-id <id> Resume a specific session by ID (cannot be\n used with spawn flags or --continue)\n":""} --permission-mode <mode> Permission mode for spawned sessions\n (${t})\n --debug-file <path> Write debug logs to file\n -v, --verbose Enable verbose output\n -h, --help Show this help\n${r}\nDESCRIPTION\n Remote Control allows you to control sessions on your local device from\n claude.ai/code (https://claude.ai/code). Run this command in the\n directory you want to work in, then connect from the Claude app or web.\n${i}\nNOTES\n - Debes iniciar sesión con una cuenta Context que tenga suscripción\n - Run \`context\` first in the directory to accept the workspace trust dialog\n${n}`;console.log(a)}();n.error&&(console.error(`Error: ${n.error}`),process.exit(1));const{verbose:l,sandbox:p,debugFile:m,sessionTimeoutMs:_,permissionMode:h,name:k,spawnMode:$,capacity:y,createSessionInDir:M,sessionId:R,continueSession:x}=n;let D,A=R;const U=void 0!==$||void 0!==y||void 0!==M;if(void 0!==h){const{PERMISSION_MODES:e}=await import("../types/permissions.js"),s=e;s.includes(h)||(console.error(`Error: Invalid permission mode '${h}'. Valid modes: ${s.join(", ")}`),process.exit(1))}const j=a("."),{enableConfigs:F,checkHasTrustDialogAccepted:G}=await import("../utils/config.js");F();const{initSinks:V}=await import("../utils/sinks.js");V();const H=await isMultiSessionSpawnEnabled();U&&!H&&(await g("tengu_bridge_multi_session_denied",{used_spawn:void 0!==$,used_capacity:void 0!==y,used_create_session_in_dir:void 0!==M}),await Promise.race([Promise.all([c(),d()]),S(500,void 0,{unref:!0})]).catch(()=>{}),console.error("Error: Multi-session Remote Control is not enabled for your account yet."),process.exit(1));const{setOriginalCwd:L,setCwdState:z}=await import("../bootstrap/state.js");L(j),z(j),G()||(console.error(`Error: Workspace not trusted. Please run \`context\` in ${j} first to review and accept the workspace trust dialog.`),process.exit(1));const{clearOAuthTokenCache:q,checkAndRefreshOAuthTokenIfNeeded:K}=await import("../utils/auth.js"),{getBridgeAccessToken:Y,getBridgeBaseUrl:J}=await import("./bridgeConfig.js");Y()||(console.error(N),process.exit(1));const{getGlobalConfig:X,saveGlobalConfig:Q,getCurrentProjectConfig:Z,saveCurrentProjectConfig:ee}=await import("../utils/config.js");if(!X().remoteDialogSeen){const e=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log("\nRemote Control lets you access this CLI session from the web (claude.ai/code)\nor the Claude app, so you can pick up where you left off on any device.\n\nYou can disconnect remote access anytime by running /remote-control again.\n");const s=await new Promise(s=>{e.question("Enable Remote Control? (y/n) ",s)});e.close(),Q(e=>e.remoteDialogSeen?e:{...e,remoteDialogSeen:!0}),"y"!==s.toLowerCase()&&"yes"!==s.toLowerCase()&&process.exit(0)}if(s("KAIROS")&&x){const{readBridgePointerAcrossWorktrees:e}=await import("./bridgePointer.js"),s=await e(j);s||(console.error("Error: No recent session found in this directory or its worktrees. Run `context remote-control` to start a new one."),process.exit(1));const{pointer:t,dir:o}=s,r=Math.round(t.ageMs/6e4),i=r<60?`${r}m`:`${Math.round(r/60)}h`,n=o!==j?` from worktree ${o}`:"";console.error(`Resuming session ${t.sessionId} (${i} ago)${n}…`),A=t.sessionId,D=o}const se=J();!se.startsWith("http://")||se.includes("localhost")||se.includes("127.0.0.1")||(console.error("Error: Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed."),process.exit(1));const te="ant"===process.env.USER_TYPE?process.env.CONTEXT_BRIDGE_SESSION_INGRESS_URL??process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL??se:se,{getBranch:oe,getRemoteUrl:re,findGitRoot:ie}=await import("../utils/git.js"),{hasWorktreeCreateHook:ne}=await import("../utils/hooks.js"),ae=ne()||null!==ie(j);let le,de,ce=H?Z().remoteControlSpawnMode:void 0;if("worktree"!==ce||ae||(console.error("Warning: Saved spawn mode is worktree but this directory is not a git repository. Falling back to same-dir."),ce=void 0,ee(e=>void 0===e.remoteControlSpawnMode?e:{...e,remoteControlSpawnMode:void 0})),H&&!ce&&ae&&void 0===$&&!A&&process.stdin.isTTY){const e=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});console.log("\nClaude Remote Control is launching in spawn mode which lets you create new sessions in this project from Context Code on Web or your Mobile app. Learn more here: https://docs.iaforged.com/remote-control\n\nSpawn mode for this project:\n [1] same-dir — sessions share the current directory (default)\n [2] worktree — each session gets an isolated git worktree\n\nThis can be changed later or explicitly set with --spawn=same-dir or --spawn=worktree.\n");const s=await new Promise(s=>{e.question("Choose [1/2] (default: 1): ",s)});e.close();const t="2"===s.trim()?"worktree":"same-dir";ce=t,u("tengu_bridge_spawn_mode_chosen",{spawn_mode:t}),ee(e=>e.remoteControlSpawnMode===t?e:{...e,remoteControlSpawnMode:t})}A?(de="single-session",le="resume"):void 0!==$?(de=$,le="flag"):void 0!==ce?(de=ce,le="saved"):(de=H?"same-dir":"single-session",le="gate_default");const pe="single-session"===de?1:y??32,ue=M??!0;if(!A){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(j)}"worktree"!==de||ae||(console.error("Error: Worktree mode requires a git repository or WorktreeCreate hooks configured. Use --spawn=session for single-session mode."),process.exit(1));const ge=await oe(),me=await re(),we=o(),fe=t(),{handleOAuth401Error:_e}=await import("../utils/auth.js"),he=E({baseUrl:se,getAccessToken:Y,runnerVersion:e.VERSION,onDebug:w,onAuth401:_e,getTrustedDeviceToken:O});let be;if(s("KAIROS")&&A){try{C(A,"sessionId")}catch{console.error(`Error: Invalid session ID "${A}". Session IDs must not contain unsafe characters.`),process.exit(1)}await K(),q();const{getBridgeSession:e}=await import("./createSession.js"),s=await e(A,{baseUrl:se,getAccessToken:Y});if(!s){if(D){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(`Error: Session ${A} not found. It may have been archived or expired, or your login may have lapsed (run \`context /login\`).`),process.exit(1)}if(!s.environment_id){if(D){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(`Error: Session ${A} has no environment_id. It may never have been attached to a bridge.`),process.exit(1)}be=s.environment_id,w(`[bridge:init] Resuming session ${A} on environment ${be}`)}const ke={dir:j,machineName:we,branch:ge,gitRepoUrl:me,maxSessions:pe,spawnMode:de,verbose:l,sandbox:p,bridgeId:fe,workerType:"claude_code",environmentId:t(),reuseEnvironmentId:be,apiBaseUrl:se,sessionIngressUrl:te,debugFile:m,sessionTimeoutMs:_};let ve,Se,$e;w(`[bridge:init] bridgeId=${fe}${be?` reuseEnvironmentId=${be}`:""} dir=${j} branch=${ge} gitRepoUrl=${me} machine=${we}`),w(`[bridge:init] apiBaseUrl=${se} sessionIngressUrl=${te}`),w(`[bridge:init] sandbox=${p}${m?` debugFile=${m}`:""}`);try{const e=await he.registerBridgeEnvironment(ke);ve=e.environment_id,Se=e.environment_secret}catch(e){u("tengu_bridge_registration_failed",{status:e instanceof I?e.status:void 0}),console.error(e instanceof I&&404===e.status?"Remote Control environments are not available for your account.":`Error: ${b(e)}`),process.exit(1)}if(s("KAIROS")&&A)if(be&&ve!==be)v(new Error(`Bridge resume env mismatch: requested ${be}, backend returned ${ve}. Falling back to fresh session.`)),console.warn(`Warning: Could not resume session ${A} — its environment has expired. Creating a fresh session instead.`);else{const e=B(A),s=e===A?[A]:[A,e];let t,o=!1;for(const e of s)try{await he.reconnectSession(ve,e),w(`[bridge:init] Session ${e} re-queued via bridge/reconnect`),$e=A,o=!0;break}catch(s){t=s,w(`[bridge:init] reconnectSession(${e}) failed: ${b(s)}`)}if(!o){const e=t,s=e instanceof I;if(D&&s){const{clearBridgePointer:e}=await import("./bridgePointer.js");await e(D)}console.error(s?`Error: ${b(e)}`:`Error: Failed to reconnect session ${A}: ${b(e)}\nThe session may still be resumable — try running the same command again.`),process.exit(1)}}w(`[bridge:init] Registered, server environmentId=${ve}`);const ye=P();u("tengu_bridge_started",{max_sessions:ke.maxSessions,has_debug_file:!!ke.debugFile,sandbox:ke.sandbox,verbose:ke.verbose,heartbeat_interval_ms:ye.non_exclusive_heartbeat_interval_ms,spawn_mode:ke.spawnMode,spawn_mode_source:le,multi_session_gate:H,pre_create_session:ue,worktree_available:ae}),f("info","bridge_started",{max_sessions:ke.maxSessions,sandbox:ke.sandbox,spawn_mode:ke.spawnMode});const Ie=W({execPath:process.execPath,scriptArgs:spawnScriptArgs(),env:process.env,verbose:l,sandbox:p,debugFile:m,permissionMode:h,onDebug:w,onActivity:(e,s)=>{w(`[bridge:activity] sessionId=${e} ${s.type} ${s.summary}`)},onPermissionRequest:(e,s,t)=>{w(`[bridge:perm] sessionId=${e} tool=${s.request.tool_name} request_id=${s.request_id} (not auto-approving)`)}}),Ee=T({verbose:l}),{parseGitHubRepository:Me}=await import("../utils/detectRepository.js"),Re=me?Me(me):null,Ce=Re?Re.split("/").pop():i(j);Ee.setRepoInfo(Ce,ge);const xe="single-session"!==de&&ae;xe&&Ee.setSpawnModeDisplay(de);const onStdinData=e=>{if(3!==e[0]&&4!==e[0])if(32!==e[0]){if(119===e[0]){if(!xe)return;const e="same-dir"===ke.spawnMode?"worktree":"same-dir";return ke.spawnMode=e,u("tengu_bridge_spawn_mode_toggled",{spawn_mode:e}),Ee.logStatus("worktree"===e?"Spawn mode: worktree (new sessions get isolated git worktrees)":"Spawn mode: same-dir (new sessions share the current directory)"),Ee.setSpawnModeDisplay(e),Ee.refreshDisplay(),void ee(s=>s.remoteControlSpawnMode===e?s:{...s,remoteControlSpawnMode:e})}}else Ee.toggleQr();else process.emit("SIGINT")};process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.on("data",onStdinData));const Te=new AbortController,onSigint=()=>{w("[bridge:shutdown] SIGINT received, shutting down"),Te.abort()},onSigterm=()=>{w("[bridge:shutdown] SIGTERM received, shutting down"),Te.abort()};process.on("SIGINT",onSigint),process.on("SIGTERM",onSigterm);let De=s("KAIROS")&&$e?$e:null;if(ue&&(!s("KAIROS")||!$e)){const{createBridgeSession:e}=await import("./createSession.js");try{De=await e({environmentId:ve,title:k,events:[],gitRepoUrl:me,branch:ge,signal:Te.signal,baseUrl:se,getAccessToken:Y,permissionMode:h}),De&&w(`[bridge:init] Created initial session ${De}`)}catch(e){w(`[bridge:init] Session creation failed (non-fatal): ${b(e)}`)}}let Ae=null;if(De&&"single-session"===de){const{writeBridgePointer:e}=await import("./bridgePointer.js"),s={sessionId:De,environmentId:ve,source:"standalone"};await e(ke.dir,s),Ae=setInterval(e,36e5,ke.dir,s),Ae.unref?.()}try{await runBridgeLoop(ke,ve,Se,he,Ie,Ee,Te.signal,void 0,De??void 0,async()=>(q(),await K(),Y()))}finally{null!==Ae&&clearInterval(Ae),process.off("SIGINT",onSigint),process.off("SIGTERM",onSigterm),process.stdin.off("data",onStdinData),process.stdin.isTTY&&process.stdin.setRawMode(!1),process.stdin.pause()}process.exit(0)}export class BridgeHeadlessPermanentError extends Error{constructor(e){super(e),this.name="BridgeHeadlessPermanentError"}}export async function runBridgeHeadless(s,r){const{dir:i,log:n}=s;process.chdir(i);const{setOriginalCwd:a,setCwdState:l}=await import("../bootstrap/state.js");a(i),l(i);const{enableConfigs:d,checkHasTrustDialogAccepted:c}=await import("../utils/config.js");d();const{initSinks:p}=await import("../utils/sinks.js");if(p(),!c())throw new BridgeHeadlessPermanentError(`Workspace not trusted: ${i}. Run \`context\` in that directory first to accept the trust dialog.`);if(!s.getAccessToken())throw new Error(N);const{getBridgeBaseUrl:u}=await import("./bridgeConfig.js"),g=u();if(g.startsWith("http://")&&!g.includes("localhost")&&!g.includes("127.0.0.1"))throw new BridgeHeadlessPermanentError("Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed.");const m="ant"===process.env.USER_TYPE?process.env.CONTEXT_BRIDGE_SESSION_INGRESS_URL??process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL??g:g,{getBranch:w,getRemoteUrl:f,findGitRoot:_}=await import("../utils/git.js"),{hasWorktreeCreateHook:h}=await import("../utils/hooks.js");if("worktree"===s.spawnMode){if(!(h()||null!==_(i)))throw new BridgeHeadlessPermanentError(`Worktree mode requires a git repository or WorktreeCreate hooks. Directory ${i} has neither.`)}const k=await w(),v=await f(),S=o(),$=t(),y={dir:i,machineName:S,branch:k,gitRepoUrl:v,maxSessions:s.capacity,spawnMode:s.spawnMode,verbose:!1,sandbox:s.sandbox,bridgeId:$,workerType:"claude_code",environmentId:t(),apiBaseUrl:g,sessionIngressUrl:m,sessionTimeoutMs:s.sessionTimeoutMs},I=E({baseUrl:g,getAccessToken:s.getAccessToken,runnerVersion:e.VERSION,onDebug:n,onAuth401:s.onAuth401,getTrustedDeviceToken:O});let M,R;try{const e=await I.registerBridgeEnvironment(y);M=e.environment_id,R=e.environment_secret}catch(e){throw new Error(`Bridge registration failed: ${b(e)}`)}const C=W({execPath:process.execPath,scriptArgs:spawnScriptArgs(),env:process.env,verbose:!1,sandbox:s.sandbox,permissionMode:s.permissionMode,onDebug:n}),x=function(e){const noop=()=>{};return{printBanner:(s,t)=>e(`registered environmentId=${t} dir=${s.dir} spawnMode=${s.spawnMode} capacity=${s.maxSessions}`),logSessionStart:(s,t)=>e(`session start ${s}`),logSessionComplete:(s,t)=>e(`session complete ${s} (${t}ms)`),logSessionFailed:(s,t)=>e(`session failed ${s}: ${t}`),logStatus:e,logVerbose:e,logError:s=>e(`error: ${s}`),logReconnected:s=>e(`reconnected after ${s}ms`),addSession:(s,t)=>e(`session attached ${s}`),removeSession:s=>e(`session detached ${s}`),updateIdleStatus:noop,updateReconnectingStatus:noop,updateSessionStatus:noop,updateSessionActivity:noop,updateSessionCount:noop,updateFailedStatus:noop,setSpawnModeDisplay:noop,setRepoInfo:noop,setDebugLogPath:noop,setAttached:noop,setSessionTitle:noop,clearStatus:noop,toggleQr:noop,refreshDisplay:noop}}(n);let T;if(x.printBanner(y,M),s.createSessionOnStart){const{createBridgeSession:e}=await import("./createSession.js");try{const t=await e({environmentId:M,title:s.name,events:[],gitRepoUrl:v,branch:k,signal:r,baseUrl:g,getAccessToken:s.getAccessToken,permissionMode:s.permissionMode});t&&(T=t,n(`created initial session ${t}`))}catch(e){n(`session pre-creation failed (non-fatal): ${b(e)}`)}}await runBridgeLoop(y,M,R,I,C,x,r,void 0,T,async()=>s.getAccessToken())}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o,Fragment as t}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{useState as r}from"react";import{Dialog as a}from"../../components/design-system/Dialog.js";import{FastIcon as
|
|
1
|
+
import{jsx as e,jsxs as o,Fragment as t}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{useState as r}from"react";import{Dialog as a}from"../../components/design-system/Dialog.js";import{FastIcon as i,getFastIconString as s}from"../../components/FastIcon.js";import{Box as l,Link as c,Text as m}from"../../ink.js";import{useKeybindings as d}from"../../keybindings/useKeybinding.js";import{logEvent as p}from"../../services/analytics/index.js";import{useAppState as f,useSetAppState as u}from"../../state/AppState.js";import{clearFastModeCooldown as F,FAST_MODE_MODEL_DISPLAY as g,getFastModeModel as h,getFastModeRuntimeState as _,getFastModeUnavailableReason as y,isFastModeEnabled as M,isFastModeSupportedByModel as b,prefetchFastModeStatus as v}from"../../utils/fastMode.js";import{formatDuration as j}from"../../utils/format.js";import{formatModelPricing as x,getOpus46CostTier as w}from"../../utils/modelCost.js";import{updateSettingsForSource as S}from"../../utils/settings/settings.js";function applyFastMode(e,o){F(),S("userSettings",{fastMode:!!e||void 0}),o(e?e=>{const o=!b(e.mainLoopModel);return{...e,...o?{mainLoopModel:h(),mainLoopModelForSession:null}:{},fastMode:!0}}:e=>({...e,fastMode:!1}))}export function FastModePicker(F){const h=n(30),{onDone:y,unavailableReason:M}=F,v=f(_temp),S=f(_temp2),$=u(),[L,O]=r(S??!1);let k;h[0]===Symbol.for("react.memo_cache_sentinel")?(k=_(),h[0]=k):k=h[0];const C=k,D="cooldown"===C.status,N=null!==M;let A;h[1]===Symbol.for("react.memo_cache_sentinel")?(A=x(w(!0)),h[1]=A):A=h[1];const E=A;let K;h[2]!==L||h[3]!==N||h[4]!==v||h[5]!==y||h[6]!==$?(K=function(){if(!N)if(applyFastMode(L,$),p("tengu_fast_mode_toggled",{enabled:L,source:"picker"}),L){const e=s(L),o=b(v)?"":` · model set to ${g}`;y(`${e} Fast mode ON${o} · ${E}`)}else $(_temp3),y("Fast mode OFF")},h[2]=L,h[3]=N,h[4]=v,h[5]=y,h[6]=$,h[7]=K):K=h[7];const P=K;let R;h[8]!==S||h[9]!==N||h[10]!==y||h[11]!==$?(R=function(){if(N)return S&&applyFastMode(!1,$),void y("Fast mode OFF",{display:"system"});const e=S?`${s()} Kept Fast mode ON`:"Kept Fast mode OFF";y(e,{display:"system"})},h[8]=S,h[9]=N,h[10]=y,h[11]=$,h[12]=R):R=h[12];const T=R;let B;h[13]!==N?(B=function(){N||O(_temp4)},h[13]=N,h[14]=B):B=h[14];const G=B;let H,I,Y;h[15]!==P||h[16]!==G?(H={"confirm:yes":P,"confirm:nextField":G,"confirm:next":G,"confirm:previous":G,"confirm:cycleMode":G,"confirm:toggle":G},h[15]=P,h[16]=G,h[17]=H):H=h[17],h[18]===Symbol.for("react.memo_cache_sentinel")?(I={context:"Confirmation"},h[18]=I):I=h[18],d(H,I),h[19]===Symbol.for("react.memo_cache_sentinel")?(Y=o(m,{children:[e(i,{cooldown:D})," Fast mode (research preview)"]}),h[19]=Y):Y=h[19];const Z=Y;let q,z,J,Q;return h[20]!==N?(q=t=>t.pending?o(m,{children:["Presiona ",t.keyName," de nuevo para salir"]}):e(m,N?{children:"Esc para cancelar"}:{children:"Tab para alternar · Enter para confirmar · Esc para cancelar"}),h[20]=N,h[21]=q):q=h[21],h[22]!==L||h[23]!==M?(z=M?e(l,{marginLeft:2,children:e(m,{color:"error",children:M})}):o(t,{children:[e(l,{flexDirection:"column",gap:0,marginLeft:2,children:o(l,{flexDirection:"row",gap:2,children:[e(m,{bold:!0,children:"Fast mode"}),e(m,{color:L?"fastMode":void 0,bold:L,children:L?"ON ":"OFF"}),e(m,{dimColor:!0,children:E})]})}),D&&"cooldown"===C.status&&e(l,{marginLeft:2,children:o(m,{color:"warning",children:["overloaded"===C.reason?"Fast mode overloaded and is temporarily unavailable":"You've hit your fast limit"," · resets in ",j(C.resetAt-Date.now(),{hideTrailingZeros:!0})]})})]}),h[22]=L,h[23]=M,h[24]=z):z=h[24],h[25]===Symbol.for("react.memo_cache_sentinel")?(J=o(m,{dimColor:!0,children:["Learn more:"," ",e(c,{url:"https://docs.iaforged.com/fast-mode",children:"https://docs.iaforged.com/fast-mode"})]}),h[25]=J):J=h[25],h[26]!==T||h[27]!==z||h[28]!==q?(Q=o(a,{title:Z,subtitle:`High-speed mode for ${g}. Billed as extra usage at a premium rate. Separate rate limits apply.`,onCancel:T,color:"fastMode",inputGuide:q,children:[z,J]}),h[26]=T,h[27]=z,h[28]=q,h[29]=Q):Q=h[29],Q}function _temp4(e){return!e}function _temp3(e){return{...e,fastMode:!1}}function _temp2(e){return e.fastMode}function _temp(e){return e.mainLoopModel}export async function call(o,t,n){if(!M())return null;await v();const r=n?.trim().toLowerCase();if("on"===r||"off"===r){const e=await async function(e,o,t){const n=y();if(n)return`Fast mode unavailable: ${n}`;const{mainLoopModel:r}=o();if(applyFastMode(e,t),p("tengu_fast_mode_toggled",{enabled:e,source:"shortcut"}),e)return`${s(!0)} Fast mode ON${b(r)?"":` · model set to ${g}`} · ${x(w(!0))}`;return"Fast mode OFF"}("on"===r,t.getAppState,t.setAppState);return o(e),null}const a=y();return p("tengu_fast_mode_picker_shown",{unavailable_reason:a??""}),e(FastModePicker,{onDone:o,unavailableReason:a})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{mkdir as r,writeFile as t}from"fs/promises";import*as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{mkdir as r,writeFile as t}from"fs/promises";import*as m from"react";import{Dialog as n}from"../../components/design-system/Dialog.js";import{MemoryFileSelector as i}from"../../components/memory/MemoryFileSelector.js";import{getRelativeMemoryPath as s}from"../../components/memory/MemoryUpdateNotification.js";import{Box as a,Link as l,Text as c}from"../../ink.js";import{clearMemoryFileCaches as p,getMemoryFiles as d}from"../../utils/claudemd.js";import{getClaudeConfigHomeDir as f}from"../../utils/envUtils.js";import{getErrnoCode as y}from"../../utils/errors.js";import{logError as u}from"../../utils/log.js";import{editFileInEditor as j}from"../../utils/promptEditor.js";function MemoryCommand({onDone:p}){const handleCancel=()=>{p("Cancelled memory editing",{display:"system"})};return e(n,{title:"Memory",onCancel:handleCancel,color:"remember",children:o(a,{flexDirection:"column",children:[e(m.Suspense,{fallback:null,children:e(i,{onSelect:async e=>{try{e.includes(f())&&await r(f(),{recursive:!0});try{await t(e,"",{encoding:"utf8",flag:"wx"})}catch(e){if("EEXIST"!==y(e))throw e}await j(e);let o="default",m="";process.env.VISUAL?(o="$VISUAL",m=process.env.VISUAL):process.env.EDITOR&&(o="$EDITOR",m=process.env.EDITOR);const n="default"!==o?`Using ${o}="${m}".`:"",i=n?`> ${n} To change editor, set $EDITOR or $VISUAL environment variable.`:"> To use a different editor, set the $EDITOR or $VISUAL environment variable.";p(`Opened memory file at ${s(e)}\n\n${i}`,{display:"system"})}catch(e){u(e),p(`Error opening memory file: ${e}`)}},onCancel:handleCancel})}),e(a,{marginTop:1,children:o(c,{dimColor:!0,children:["Learn more: ",e(l,{url:"https://docs.iaforged.com/memory"})]})})]})})}export const call=async o=>(p(),await d(),e(MemoryCommand,{onDone:o}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e}from"react/jsx-runtime";import{c as o}from"react/compiler-runtime";import t from"chalk";import*as n from"react";import{ModelPicker as r}from"../../components/ModelPicker.js";import{COMMON_HELP_ARGS as
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{c as o}from"react/compiler-runtime";import t from"chalk";import*as n from"react";import{ModelPicker as r}from"../../components/ModelPicker.js";import{COMMON_HELP_ARGS as s,COMMON_INFO_ARGS as l}from"../../constants/xml.js";import{logEvent as i}from"../../services/analytics/index.js";import{useAppState as d,useSetAppState as m}from"../../state/AppState.js";import{isBilledAsExtraUsage as a}from"../../utils/extraUsage.js";import{clearFastModeCooldown as c,isFastModeAvailable as u,isFastModeEnabled as p,isFastModeSupportedByModel as f}from"../../utils/fastMode.js";import{MODEL_ALIASES as M}from"../../utils/model/aliases.js";import{checkOpus1mAccess as _,checkSonnet1mAccess as v}from"../../utils/model/check1mAccess.js";import{getDefaultMainLoopModelSetting as y,isOpus1mMergeEnabled as g,renderDefaultModelSetting as b}from"../../utils/model/model.js";import{isModelAllowed as j}from"../../utils/model/modelAllowlist.js";import{isProfiledProvider as x,setProviderProfileLastModel as L}from"../../utils/model/providerProfiles.js";import{setStoredLastModelForProvider as h}from"../../utils/model/providerProfilesDb.js";import{toProviderPreference as A}from"../../utils/model/providerSwitch.js";import{getAPIProvider as S}from"../../utils/model/providers.js";import{validateModel as $}from"../../utils/model/validateModel.js";function persistModelForActiveProvider(e){try{const o=A(S());x(o)&&L(o,e),h(o,e)}catch{}}export function ModelPickerWrapper(n){const s=o(17),{onDone:l}=n,M=d(_temp),_=d(_temp2),v=d(_temp3),y=m();let b;s[0]!==M||s[1]!==l?(b=function(){i("tengu_model_command_menu",{action:"cancel"});const e=renderModelLabel(M);l(`Modelo mantenido como ${t.bold(e)}`,{display:"system"})},s[0]=M,s[1]=l,s[2]=b):b=s[2];const j=b;let x;s[3]!==v||s[4]!==M||s[5]!==l||s[6]!==y?(x=function(e,o){i("tengu_model_command_menu",{action:e,from_model:M,to_model:e}),persistModelForActiveProvider(e),y(o=>({...o,mainLoopModel:e,mainLoopModelForSession:null}));let n,r=`Modelo configurado en ${t.bold(renderModelLabel(e))}`;void 0!==o&&(r+=` con un nivel de esfuerzo ${t.bold(o)}`),p()&&(c(),!f(e)&&v?(y(_temp4),n=!1):f(e)&&u()&&v&&(r+=" · Modo rápido ACTIVADO",n=!0)),a(e,!0===n,g())&&(r+=" · Facturado como uso extra"),!1===n&&(r+=" · Modo rápido DESACTIVADO"),l(r)},s[3]=v,s[4]=M,s[5]=l,s[6]=y,s[7]=x):x=s[7];const L=x;let h,A;return s[8]!==v||s[9]!==M?(h=p()&&v&&f(M)&&u(),s[8]=v,s[9]=M,s[10]=h):h=s[10],s[11]!==j||s[12]!==L||s[13]!==M||s[14]!==_||s[15]!==l||s[16]!==h?(A=e(r,{initial:M,sessionModel:_,onSelect:L,onCancel:j,isStandaloneCommand:!0,showFastModeNotice:h,onOpenProvider:()=>l("Abriendo selector de proveedor...",{nextInput:"/provider",submitNextInput:!0})}),s[11]=j,s[12]=L,s[13]=M,s[14]=_,s[15]=l,s[16]=h,s[17]=A):A=s[17],A}function _temp4(e){return{...e,fastMode:!1}}function _temp3(e){return e.fastMode}function _temp2(e){return e.mainLoopModelForSession}function _temp(e){return e.mainLoopModel}function SetModelAndClose({args:e,onDone:o}){const r=d(e=>e.fastMode),s=m(),l="default"===e?null:e;return n.useEffect(()=>{function setModel(e){persistModelForActiveProvider(e),s(o=>({...o,mainLoopModel:e,mainLoopModelForSession:null}));let n,l=`Set model to ${t.bold(renderModelLabel(e))}`;p()&&(c(),!f(e)&&r?(s(e=>({...e,fastMode:!1})),n=!1):f(e)&&r&&(l+=" · Fast mode ON",n=!0)),a(e,!0===n,g())&&(l+=" · Billed as extra usage"),!1===n&&(l+=" · Fast mode OFF"),o(l)}!async function(){if(!l||j(l))if(l&&function(e){const o=e.toLowerCase();return!_()&&!g()&&o.includes("opus")&&o.includes("[1m]")}(l))o("Opus 4.6 con 1M de contexto no está disponible para tu cuenta. Más información en: https://docs.iaforged.com/model-config#extended-context-with-1m",{display:"system"});else if(l&&function(e){const o=e.toLowerCase();return!v()&&(o.includes("sonnet[1m]")||o.includes("sonnet-4-6[1m]"))}(l))o("Sonnet 4.6 con 1M de contexto no está disponible para tu cuenta. Más información en: https://docs.iaforged.com/model-config#extended-context-with-1m",{display:"system"});else if(l)if(function(e){return M.includes(e.toLowerCase().trim())}(l))setModel(l);else try{const{valid:e,error:t}=await $(l);e?setModel(l):o(t||`Modelo '${l}' no encontrado`,{display:"system"})}catch(e){o(`Error al validar el modelo: ${e.message}`,{display:"system"})}else setModel(null);else o(`El modelo '${l}' no está disponible. Tu organización restringe la selección de modelos.`,{display:"system"})}()},[l,o,s]),null}function ShowModelAndClose(e){const{onDone:o}=e,n=d(_temp7),r=d(_temp8),s=d(_temp9),l=renderModelLabel(n),i=void 0!==s?` (effort: ${s})`:"";return o(r?`Current model: ${t.bold(renderModelLabel(r))} (session override from plan mode)\nBase model: ${l}${i}`:`Current model: ${l}${i}`),null}function _temp9(e){return e.effortValue}function _temp8(e){return e.mainLoopModelForSession}function _temp7(e){return e.mainLoopModel}export const call=async(o,t,n)=>(n=n?.trim()||"",l.includes(n)?(i("tengu_model_command_inline_help",{args:n}),e(ShowModelAndClose,{onDone:o})):s.includes(n)?void o("Run /model to open the model selection menu, or /model [modelName] to set the model.",{display:"system"}):n?(i("tengu_model_command_inline",{args:n}),e(SetModelAndClose,{args:n,onDone:o})):e(ModelPickerWrapper,{onDone:o}));function renderModelLabel(e){const o=b(e??y());return null===e?`${o} (default)`:o}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isUltrareviewEnabled as e}from"./review/ultrareviewEnabled.js";const LOCAL_REVIEW_PROMPT=e=>`\n Eres un experto revisor de código. Sigue estos pasos:\n\n 1. Si no se proporciona un número de PR en los argumentos, ejecuta \`gh pr list\` para mostrar las PR abiertas.\n 2. Si se proporciona un número de PR, ejecuta \`gh pr view <numero>\` para obtener los detalles de la PR.\n 3. Ejecuta \`gh pr diff <numero>\` para obtener el diff.\n 4. Analiza los cambios y proporciona una revisión de código exhaustiva que incluya:\n - Resumen de lo que hace la PR.\n - Análisis de la calidad y el estilo del código.\n - Sugerencias específicas de mejora.\n - Cualquier problema o riesgo potencial.\n\n Mantén tu revisión concisa pero exhaustiva. Encamina tu atención a:\n - Corrección del código.\n - Seguimiento de las convenciones del proyecto.\n - Implicaciones de rendimiento.\n - Cobertura de pruebas.\n - Consideraciones de seguridad.\n\n Formatea tu revisión con secciones claras y puntos de viñeta.\n\n Número de PR: ${e}\n `,n={type:"prompt",name:"review",aliases:["revisar"],description:"Revisar una pull request",progressMessage:"revisando pull request",contentLength:0,source:"builtin",getPromptForCommand:async e=>[{type:"text",text:LOCAL_REVIEW_PROMPT(e)}]},a={type:"local-jsx",name:"ultrareview",description:"~10–20 min · Encuentra y verifica errores en tu rama. Se ejecuta en Context Code en la web. Ver https://
|
|
1
|
+
import{isUltrareviewEnabled as e}from"./review/ultrareviewEnabled.js";const LOCAL_REVIEW_PROMPT=e=>`\n Eres un experto revisor de código. Sigue estos pasos:\n\n 1. Si no se proporciona un número de PR en los argumentos, ejecuta \`gh pr list\` para mostrar las PR abiertas.\n 2. Si se proporciona un número de PR, ejecuta \`gh pr view <numero>\` para obtener los detalles de la PR.\n 3. Ejecuta \`gh pr diff <numero>\` para obtener el diff.\n 4. Analiza los cambios y proporciona una revisión de código exhaustiva que incluya:\n - Resumen de lo que hace la PR.\n - Análisis de la calidad y el estilo del código.\n - Sugerencias específicas de mejora.\n - Cualquier problema o riesgo potencial.\n\n Mantén tu revisión concisa pero exhaustiva. Encamina tu atención a:\n - Corrección del código.\n - Seguimiento de las convenciones del proyecto.\n - Implicaciones de rendimiento.\n - Cobertura de pruebas.\n - Consideraciones de seguridad.\n\n Formatea tu revisión con secciones claras y puntos de viñeta.\n\n Número de PR: ${e}\n `,n={type:"prompt",name:"review",aliases:["revisar"],description:"Revisar una pull request",progressMessage:"revisando pull request",contentLength:0,source:"builtin",getPromptForCommand:async e=>[{type:"text",text:LOCAL_REVIEW_PROMPT(e)}]},a={type:"local-jsx",name:"ultrareview",description:"~10–20 min · Encuentra y verifica errores en tu rama. Se ejecuta en Context Code en la web. Ver https://docs.iaforged.com/claude-code-on-the-web",isEnabled:()=>e(),load:()=>import("./review/ultrareviewCommand.js")};export default n;export{a as ultrareview};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire as t}from"module";const n=t(import.meta.url);import{readFileSync as e}from"fs";import{REMOTE_CONTROL_DISCONNECTED_MSG as a}from"../bridge/types.js";import{DIAMOND_OPEN as o}from"../constants/figures.js";import{getRemoteSessionUrl as l}from"../constants/product.js";import{getFeatureValue_CACHED_MAY_BE_STALE as r}from"../services/analytics/growthbook.js";import{logEvent as s}from"../services/analytics/index.js";import{checkRemoteAgentEligibility as i,formatPreconditionError as u,RemoteAgentTask as p,registerRemoteAgentTask as c}from"../tasks/RemoteAgentTask/RemoteAgentTask.js";import{logForDebugging as d}from"../utils/debug.js";import{errorMessage as m}from"../utils/errors.js";import{logError as g}from"../utils/log.js";import{enqueuePendingNotification as f}from"../utils/messageQueueManager.js";import{ALL_MODEL_CONFIGS as h}from"../utils/model/configs.js";import{updateTaskState as _}from"../utils/task/framework.js";import{archiveRemoteSession as S,teleportToRemote as y}from"../utils/teleport.js";import{pollForApprovedExitPlanMode as v,UltraplanPollError as w}from"../utils/ultraplan/ccrSession.js";export const CCR_TERMS_URL="https://
|
|
1
|
+
import{createRequire as t}from"module";const n=t(import.meta.url);import{readFileSync as e}from"fs";import{REMOTE_CONTROL_DISCONNECTED_MSG as a}from"../bridge/types.js";import{DIAMOND_OPEN as o}from"../constants/figures.js";import{getRemoteSessionUrl as l}from"../constants/product.js";import{getFeatureValue_CACHED_MAY_BE_STALE as r}from"../services/analytics/growthbook.js";import{logEvent as s}from"../services/analytics/index.js";import{checkRemoteAgentEligibility as i,formatPreconditionError as u,RemoteAgentTask as p,registerRemoteAgentTask as c}from"../tasks/RemoteAgentTask/RemoteAgentTask.js";import{logForDebugging as d}from"../utils/debug.js";import{errorMessage as m}from"../utils/errors.js";import{logError as g}from"../utils/log.js";import{enqueuePendingNotification as f}from"../utils/messageQueueManager.js";import{ALL_MODEL_CONFIGS as h}from"../utils/model/configs.js";import{updateTaskState as _}from"../utils/task/framework.js";import{archiveRemoteSession as S,teleportToRemote as y}from"../utils/teleport.js";import{pollForApprovedExitPlanMode as v,UltraplanPollError as w}from"../utils/ultraplan/ccrSession.js";export const CCR_TERMS_URL="https://docs.iaforged.com/claude-code-on-the-web";const b=n("../utils/ultraplan/prompt.txt"),k=("string"==typeof b?b:b.default).trimEnd();export function buildUltraplanPrompt(t,n){const e=[];return n&&e.push("Here is a draft plan to refine:","",n,""),e.push(k),t&&e.push("",t),e.join("\n")}function buildAlreadyActiveMessage(t){return t?`ultraplan: already polling. Open ${t} to check status, or wait for the plan to land here.`:"ultraplan: already launching. Please wait for the session to start."}export async function stopUltraplan(t,n,e){await p.kill(t,e),e(t=>t.ultraplanSessionUrl||t.ultraplanPendingChoice||t.ultraplanLaunching?{...t,ultraplanSessionUrl:void 0,ultraplanPendingChoice:void 0,ultraplanLaunching:void 0}:t);const a=l(n,process.env.SESSION_INGRESS_URL);f({value:`Ultraplan stopped.\n\nSession: ${a}`,mode:"task-notification"}),f({value:"The user stopped the ultraplan session above. Do not respond to the stop notification — wait for their next message.",mode:"task-notification",isMeta:!0})}export async function launchUltraplan(t){const{blurb:n,seedPlan:e,getAppState:p,setAppState:b,signal:k,disconnectedBridge:U,onSessionReady:j}=t,{ultraplanSessionUrl:A,ultraplanLaunching:C}=p();return A||C?(s("tengu_ultraplan_create_failed",{reason:A?"already_polling":"already_launching"}),buildAlreadyActiveMessage(A)):n||e?(b(t=>t.ultraplanLaunching?t:{...t,ultraplanLaunching:!0}),async function(t){const{blurb:n,seedPlan:e,getAppState:a,setAppState:p,signal:b,onSessionReady:k}=t;let U;try{const t=r("tengu_ultraplan_model",h.opus46.firstParty),g=await i();if(!g.eligible){s("tengu_ultraplan_create_failed",{reason:"precondition",precondition_errors:g.errors.map(t=>t.type).join(",")});const t=g.errors.map(u).join("\n");return void f({value:`ultraplan: cannot launch remote session —\n${t}`,mode:"task-notification"})}const j=buildUltraplanPrompt(n,e);let A;const C=await y({initialMessage:j,description:n||"Refine local plan",model:t,permissionMode:"plan",ultraplan:!0,signal:b,useDefaultEnvironment:!0,onBundleFail:t=>{A=t}});if(!C)return s("tengu_ultraplan_create_failed",{reason:A?"bundle_fail":"teleport_null"}),void f({value:`ultraplan: session creation failed${A?` — ${A}`:""}. See --debug for details.`,mode:"task-notification"});U=C.id;const R=l(C.id,process.env.SESSION_INGRESS_URL);p(t=>({...t,ultraplanSessionUrl:R,ultraplanLaunching:void 0})),k?.(function(t){return`${o} ultraplan · Monitor progress in Context Code on the web ${t}\nYou can continue working — when the ${o} fills, press ↓ to view results`}(R)),s("tengu_ultraplan_launched",{has_seed_plan:Boolean(e),model:t});const{taskId:x}=c({remoteTaskType:"ultraplan",session:{id:C.id,title:n||"Ultraplan"},command:n,context:{abortController:new AbortController,getAppState:a,setAppState:p},isUltraplan:!0});!function(t,n,e,a,o){const l=Date.now();let r=!1;(async()=>{try{const{plan:r,rejectCount:i,executionTarget:u}=await v(n,18e5,n=>{"needs_input"===n&&s("tengu_ultraplan_awaiting_input",{}),_(t,o,t=>{if("running"!==t.status)return t;const e="running"===n?void 0:n;return t.ultraplanPhase===e?t:{...t,ultraplanPhase:e}})},()=>"running"!==a().tasks?.[t]?.status);if(s("tengu_ultraplan_approved",{duration_ms:Date.now()-l,plan_length:r.length,reject_count:i,execution_target:u}),"remote"===u){const n=a().tasks?.[t];if("running"!==n?.status)return;_(t,o,t=>"running"!==t.status?t:{...t,status:"completed",endTime:Date.now()}),o(t=>t.ultraplanSessionUrl===e?{...t,ultraplanSessionUrl:void 0}:t),f({value:[`Ultraplan approved — executing in Context Code on the web. Follow along at: ${e}`,"","Results will land as a pull request when the remote session finishes. There is nothing to do here."].join("\n"),mode:"task-notification"})}else o(e=>{const a=e.tasks?.[t];return a&&"running"===a.status?{...e,ultraplanPendingChoice:{plan:r,sessionId:n,taskId:t}}:e})}catch(i){const u=a().tasks?.[t];if("running"!==u?.status)return;r=!0,s("tengu_ultraplan_failed",{duration_ms:Date.now()-l,reason:i instanceof w?i.reason:"network_or_unknown",reject_count:i instanceof w?i.rejectCount:void 0}),f({value:`Ultraplan failed: ${m(i)}\n\nSession: ${e}`,mode:"task-notification"}),S(n).catch(t=>d(`ultraplan archive failed: ${String(t)}`)),o(t=>t.ultraplanSessionUrl===e?{...t,ultraplanSessionUrl:void 0}:t)}finally{r&&_(t,o,t=>"running"!==t.status?t:{...t,status:"failed",endTime:Date.now()})}})()}(x,C.id,R,a,p)}catch(t){g(t),s("tengu_ultraplan_create_failed",{reason:"unexpected_error"}),f({value:`ultraplan: unexpected error — ${m(t)}`,mode:"task-notification"}),U&&(S(U).catch(t=>d("ultraplan: failed to archive orphaned session",t)),p(t=>t.ultraplanSessionUrl?{...t,ultraplanSessionUrl:void 0}:t))}finally{p(t=>t.ultraplanLaunching?{...t,ultraplanLaunching:void 0}:t)}}({blurb:n,seedPlan:e,getAppState:p,setAppState:b,signal:k,onSessionReady:j}),function(t){return`${o} ultraplan\n${t?`${a} `:""}Starting Context Code on the web…`}(U)):['Usage: /ultraplan \\<prompt\\>, or include "ultraplan" anywhere',"in your prompt","","Advanced multi-agent plan mode with our most powerful model","(Opus). Runs in Context Code on the web. When the plan is ready,","you can execute it in the web session or send it back here.","Terminal stays free while the remote plans.","Requires /login.","",`Terms: ${CCR_TERMS_URL}`].join("\n")}const call=async(t,n,e)=>{const a=e.trim();if(!a){return t(await launchUltraplan({blurb:a,getAppState:n.getAppState,setAppState:n.setAppState,signal:n.abortController.signal}),{display:"system"}),null}const{ultraplanSessionUrl:o,ultraplanLaunching:l}=n.getAppState();return o||l?(s("tengu_ultraplan_create_failed",{reason:o?"already_polling":"already_launching"}),t(buildAlreadyActiveMessage(o),{display:"system"}),null):(n.setAppState(t=>({...t,ultraplanLaunchPending:{blurb:a}})),t(void 0,{display:"skip"}),null)};export default{type:"local-jsx",name:"ultraplan",description:`~10–30 min · Context Code en la web redacta un plan avanzado que puedes editar y aprobar. Ver ${CCR_TERMS_URL}`,argumentHint:"<prompt>",isEnabled:()=>!1,load:()=>Promise.resolve({call})};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as s}from"react/compiler-runtime";import t from"react";import{logEvent as i}from"../services/analytics/index.js";import{Box as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as s}from"react/compiler-runtime";import t from"react";import{logEvent as i}from"../services/analytics/index.js";import{Box as r,Link as n,Newline as a,Text as c}from"../ink.js";import{gracefulShutdownSync as m}from"../utils/gracefulShutdown.js";import{updateSettingsForSource as l}from"../utils/settings/settings.js";import{Select as d}from"./CustomSelect/index.js";import{Dialog as p}from"./design-system/Dialog.js";export function BypassPermissionsModeDialog(u){const f=s(7),{onAccept:_}=u;let g,h;f[0]===Symbol.for("react.memo_cache_sentinel")?(g=[],f[0]=g):g=f[0],t.useEffect(_temp,g),f[1]!==_?(h=function(e){e:switch(e){case"accept":i("tengu_bypass_permissions_mode_dialog_accept",{}),l("userSettings",{skipDangerousModePermissionPrompt:!0}),_();break e;case"decline":m(1)}},f[1]=_,f[2]=h):h=f[2];const j=h,b=_temp2;let y,x,C;return f[3]===Symbol.for("react.memo_cache_sentinel")?(y=o(r,{flexDirection:"column",gap:1,children:[o(c,{children:["En el modo de omisión de permisos (Bypass Permissions), Context Code no solicitará tu aprobación antes de ejecutar comandos potencialmente peligrosos.",e(a,{}),"Este modo solo debe utilizarse en un contenedor o máquina virtual (VM) aislada que tenga acceso restringido a Internet y pueda restaurarse fácilmente si sufre daños."]}),e(c,{children:"Al proceder, aceptas toda la responsabilidad por las acciones realizadas mientras se ejecuta en el modo de omisión de permisos."}),e(n,{url:"https://docs.iaforged.com/security"})]}),f[3]=y):y=f[3],f[4]===Symbol.for("react.memo_cache_sentinel")?(x=[{label:"No, salir",value:"decline"},{label:"Sí, acepto",value:"accept"}],f[4]=x):x=f[4],f[5]!==j?(C=o(p,{title:"ADVERTENCIA: Context Code se está ejecutando en modo de omisión de permisos",color:"error",onCancel:b,children:[y,e(d,{options:x,onChange:e=>j(e)})]}),f[5]=j,f[6]=C):C=f[6],C}function _temp2(){m(0)}function _temp(){i("tengu_bypass_permissions_mode_dialog_shown",{})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import o from"react";import{logEvent as r}from"../services/analytics/index.js";import{Box as l,Link as i,Text as s}from"../ink.js";import{saveCurrentProjectConfig as a}from"../utils/config.js";import{Select as c}from"./CustomSelect/index.js";import{Dialog as d}from"./design-system/Dialog.js";export function ClaudeMdExternalIncludesDialog(m){const u=n(18),{onDone:p,isStandaloneDialog:_,externalIncludes:h}=m;let f,x;u[0]===Symbol.for("react.memo_cache_sentinel")?(f=[],u[0]=f):f=u[0],o.useEffect(_temp,f),u[1]!==p?(x=e=>{"no"===e?(r("tengu_claude_md_external_includes_dialog_declined",{}),a(_temp2)):(r("tengu_claude_md_external_includes_dialog_accepted",{}),a(_temp3)),p()},u[1]=p,u[2]=x):x=u[2];const g=x;let C;u[3]!==g?(C=()=>{g("no")},u[3]=g,u[4]=C):C=u[4];const y=C,j=!_,w=!_;let E,I,S,b,D,v;return u[5]===Symbol.for("react.memo_cache_sentinel")?(E=e(s,{children:"This project's CLAUDE.md imports files outside the current working directory. Never allow this for third-party repositories."}),u[5]=E):E=u[5],u[6]!==h?(I=h&&h.length>0&&t(l,{flexDirection:"column",children:[e(s,{dimColor:!0,children:"External imports:"}),h.map(_temp4)]}),u[6]=h,u[7]=I):I=u[7],u[8]===Symbol.for("react.memo_cache_sentinel")?(S=t(s,{dimColor:!0,children:["Important: Only use Context Code with files you trust. Accessing untrusted files may pose security risks"," ",e(i,{url:"https://
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import o from"react";import{logEvent as r}from"../services/analytics/index.js";import{Box as l,Link as i,Text as s}from"../ink.js";import{saveCurrentProjectConfig as a}from"../utils/config.js";import{Select as c}from"./CustomSelect/index.js";import{Dialog as d}from"./design-system/Dialog.js";export function ClaudeMdExternalIncludesDialog(m){const u=n(18),{onDone:p,isStandaloneDialog:_,externalIncludes:h}=m;let f,x;u[0]===Symbol.for("react.memo_cache_sentinel")?(f=[],u[0]=f):f=u[0],o.useEffect(_temp,f),u[1]!==p?(x=e=>{"no"===e?(r("tengu_claude_md_external_includes_dialog_declined",{}),a(_temp2)):(r("tengu_claude_md_external_includes_dialog_accepted",{}),a(_temp3)),p()},u[1]=p,u[2]=x):x=u[2];const g=x;let C;u[3]!==g?(C=()=>{g("no")},u[3]=g,u[4]=C):C=u[4];const y=C,j=!_,w=!_;let E,I,S,b,D,v;return u[5]===Symbol.for("react.memo_cache_sentinel")?(E=e(s,{children:"This project's CLAUDE.md imports files outside the current working directory. Never allow this for third-party repositories."}),u[5]=E):E=u[5],u[6]!==h?(I=h&&h.length>0&&t(l,{flexDirection:"column",children:[e(s,{dimColor:!0,children:"External imports:"}),h.map(_temp4)]}),u[6]=h,u[7]=I):I=u[7],u[8]===Symbol.for("react.memo_cache_sentinel")?(S=t(s,{dimColor:!0,children:["Important: Only use Context Code with files you trust. Accessing untrusted files may pose security risks"," ",e(i,{url:"https://docs.iaforged.com/security"})," "]}),u[8]=S):S=u[8],u[9]===Symbol.for("react.memo_cache_sentinel")?(b=[{label:"Yes, allow external imports",value:"yes"},{label:"No, disable external imports",value:"no"}],u[9]=b):b=u[9],u[10]!==g?(D=e(c,{options:b,onChange:e=>g(e)}),u[10]=g,u[11]=D):D=u[11],u[12]!==y||u[13]!==D||u[14]!==j||u[15]!==w||u[16]!==I?(v=t(d,{title:"Allow external CLAUDE.md file imports?",color:"warning",onCancel:y,hideBorder:j,hideInputGuide:w,children:[E,I,S,D]}),u[12]=y,u[13]=D,u[14]=j,u[15]=w,u[16]=I,u[17]=v):v=u[17],v}function _temp4(e,n){return t(s,{dimColor:!0,children:[" ",e.path]},n)}function _temp3(e){return{...e,hasClaudeMdExternalIncludesApproved:!0,hasClaudeMdExternalIncludesWarningShown:!0}}function _temp2(e){return{...e,hasClaudeMdExternalIncludesApproved:!1,hasClaudeMdExternalIncludesWarningShown:!0}}function _temp(){r("tengu_claude_md_includes_dialog_shown",{})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as o,jsxs as e}from"react/jsx-runtime";import{c as t}from"react/compiler-runtime";import{Box as
|
|
1
|
+
import{jsx as o,jsxs as e}from"react/jsx-runtime";import{c as t}from"react/compiler-runtime";import{Box as r,Link as n,Text as s}from"../ink.js";import{Select as i}from"./CustomSelect/index.js";import{Dialog as a}from"./design-system/Dialog.js";export function CostThresholdDialog(c){const m=t(7),{onDone:l}=c;let d,f,h,p;return m[0]===Symbol.for("react.memo_cache_sentinel")?(d=e(r,{flexDirection:"column",children:[o(s,{children:"Más información sobre cómo monitorear tu gasto:"}),o(n,{url:"https://docs.iaforged.com/costs"})]}),m[0]=d):d=m[0],m[1]===Symbol.for("react.memo_cache_sentinel")?(f=[{value:"ok",label:"Entendido, ¡gracias!"}],m[1]=f):f=m[1],m[2]!==l?(h=o(i,{options:f,onChange:l}),m[2]=l,m[3]=h):h=m[3],m[4]!==l||m[5]!==h?(p=e(a,{title:"Has gastado $5 en la API de Anthropic en esta sesión.",onCancel:l,children:[d,h]}),m[4]=l,m[5]=h,m[6]=p):p=m[6],p}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsxs as e,jsx as n}from"react/jsx-runtime";import{c as o}from"react/compiler-runtime";import{BLACK_CIRCLE as r}from"../../constants/figures.js";import{Box as i,Text as
|
|
1
|
+
import{jsxs as e,jsx as n}from"react/jsx-runtime";import{c as o}from"react/compiler-runtime";import{BLACK_CIRCLE as r}from"../../constants/figures.js";import{Box as i,Text as t}from"../../ink.js";import{useDebouncedDigitInput as c}from"./useDebouncedDigitInput.js";const a=["1","2","3"],s={1:"yes",2:"no",3:"dont_ask_again"},isValidResponseInput=e=>a.includes(e);export function TranscriptSharePrompt(a){const l=o(11),{onSelect:m,inputValue:d,setInputValue:h}=a;let u,p,f,y,_,g,b;return l[0]!==m?(u=e=>m(s[e]),l[0]=m,l[1]=u):u=l[1],l[2]!==d||l[3]!==h||l[4]!==u?(p={inputValue:d,setInputValue:h,isValidDigit:isValidResponseInput,onDigit:u},l[2]=d,l[3]=h,l[4]=u,l[5]=p):p=l[5],c(p),l[6]===Symbol.for("react.memo_cache_sentinel")?(f=e(i,{children:[e(t,{color:"ansi:cyan",children:[r," "]}),n(t,{bold:!0,children:"Can Anthropic look at your session transcript to help us improve Context Code?"})]}),l[6]=f):f=l[6],l[7]===Symbol.for("react.memo_cache_sentinel")?(y=n(i,{marginLeft:2,children:n(t,{dimColor:!0,children:"Learn more: https://docs.iaforged.com/data-usage#session-quality-surveys"})}),l[7]=y):y=l[7],l[8]===Symbol.for("react.memo_cache_sentinel")?(_=n(i,{width:10,children:e(t,{children:[n(t,{color:"ansi:cyan",children:"1"}),": Yes"]})}),l[8]=_):_=l[8],l[9]===Symbol.for("react.memo_cache_sentinel")?(g=n(i,{width:10,children:e(t,{children:[n(t,{color:"ansi:cyan",children:"2"}),": No"]})}),l[9]=g):g=l[9],l[10]===Symbol.for("react.memo_cache_sentinel")?(b=e(i,{flexDirection:"column",marginTop:1,children:[f,y,e(i,{marginLeft:2,children:[_,g,n(i,{children:e(t,{children:[n(t,{color:"ansi:cyan",children:"3"}),": Don't ask again"]})})]})]}),l[10]=b):b=l[10],b}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{homedir as r}from"os";import t from"react";import{logEvent as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{homedir as r}from"os";import t from"react";import{logEvent as i}from"../../services/analytics/index.js";import{setSessionTrustAccepted as n}from"../../bootstrap/state.js";import{useExitOnCtrlCDWithKeybindings as s}from"../../hooks/useExitOnCtrlCDWithKeybindings.js";import{Box as a,Link as l,Text as c}from"../../ink.js";import{useKeybinding as m}from"../../keybindings/useKeybinding.js";import{getMcpConfigsByScope as d}from"../../services/mcp/config.js";import{BASH_TOOL_NAME as p}from"../../tools/BashTool/toolName.js";import{checkHasTrustDialogAccepted as h,saveCurrentProjectConfig as g}from"../../utils/config.js";import{getCwd as u}from"../../utils/cwd.js";import{getFsImplementation as f}from"../../utils/fsOperations.js";import{gracefulShutdownSync as C}from"../../utils/gracefulShutdown.js";import{Select as j}from"../CustomSelect/index.js";import{ContextCard as x}from"../ui/ContextCard.js";import y from"chalk";import{getApiKeyHelperSources as D,getAwsCommandsSources as b,getBashPermissionSources as v,getDangerousEnvVarsSources as w,getGcpCommandsSources as E,getHooksSources as H,getOtelHeadersHelperSources as S}from"./utils.js";export function TrustDialog({onDone:T,commands:k}){const{servers:_}=d("project"),A=Object.keys(_).length>0,B=H().length>0,O=v(),$=D().length>0,K=b().length>0,L=E().length>0,F=S().length>0,N=w().length>0,P=k?.some(e=>"prompt"===e.type&&"commands_DEPRECATED"===e.loadedFrom&&("projectSettings"===e.source||"localSettings"===e.source)&&e.allowedTools?.some(e=>e===p||e.startsWith(p+"(")))??!1,W=k?.some(e=>"prompt"===e.type&&("skills"===e.loadedFrom||"plugin"===e.loadedFrom)&&("projectSettings"===e.source||"localSettings"===e.source||"plugin"===e.source)&&e.allowedTools?.some(e=>e===p||e.startsWith(p+"(")))??!1,G=O.length>0||P||W,M=h();function onChange(e){if("exit"===e)return void C(1);const o=r()===u();i("tengu_trust_dialog_accept",{isHomeDir:o,hasMcpServers:A,hasHooks:B,hasBashExecution:G,hasApiKeyHelper:$,hasAwsCommands:K,hasGcpCommands:L,hasOtelHeadersHelper:F,hasDangerousEnvVars:N}),o?n(!0):g(e=>({...e,hasTrustDialogAccepted:!0})),T()}t.useEffect(()=>{const e=r()===u();i("tengu_trust_dialog_shown",{isHomeDir:e,hasMcpServers:A,hasHooks:B,hasBashExecution:G,hasApiKeyHelper:$,hasAwsCommands:K,hasGcpCommands:L,hasOtelHeadersHelper:F,hasDangerousEnvVars:N})},[A,B,G,$,K,L,F,N]);const V=s(()=>C(1));return m("confirm:no",()=>C(0),{context:"Confirmation"}),M?(setTimeout(T),null):e(x,{title:"Seguridad y Confianza",icon:"",children:o(a,{flexDirection:"column",gap:0,children:[o(a,{marginBottom:1,children:[e(c,{color:"cyan",bold:!0,children:" Carpeta: "}),e(c,{bold:!0,children:f().cwd()})]}),e(a,{flexDirection:"column",marginBottom:1,children:o(c,{children:[y.white("Para proteger tu seguridad, "),y.magenta.bold("Context"),y.white(" necesita confirmar si confías en este espacio de trabajo.")]})}),o(a,{paddingX:1,flexDirection:"column",gap:1,children:[o(a,{gap:1,children:[e(c,{color:"yellow",children:""}),e(c,{children:"¿Confías en los archivos de este directorio?"})]}),e(a,{gap:1,marginLeft:3,children:e(c,{dimColor:!0,children:"Al confirmar, permites que Context lea y ejecute comandos para ayudarte a programar."})})]}),e(a,{marginTop:1,paddingLeft:2,children:e(j,{options:[{label:` ${y.cyan("")} Sí, confío en este espacio`,value:"enable_all"},{label:` ${y.red("")} No, salir por ahora`,value:"exit"}],onChange:e=>onChange(e),onCancel:()=>onChange("exit")})}),o(a,{gap:1,marginTop:1,paddingLeft:1,children:[e(c,{dimColor:!0,children:""}),e(c,{dimColor:!0,children:e(l,{url:"https://docs.iaforged.com/security",children:"Documentación de seguridad"})})]}),e(a,{marginTop:1,paddingLeft:1,children:e(c,{dimColor:!0,children:V.pending?`Presiona ${y.bold(V.keyName)} de nuevo para salir`:`${y.cyan("Enter")} para confirmar · ${y.yellow("Esc")} para cancelar`})})]})})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsxs as o,jsx as e}from"react/jsx-runtime";import{c as t}from"react/compiler-runtime";import n from"figures";import{Box as s,Link as i,Text as r}from"../../ink.js";import{plural as c}from"../../utils/stringUtils.js";import{Select as l}from"../CustomSelect/select.js";import{Dialog as a}from"../design-system/Dialog.js";export function SelectEventMode(
|
|
1
|
+
import{jsxs as o,jsx as e}from"react/jsx-runtime";import{c as t}from"react/compiler-runtime";import n from"figures";import{Box as s,Link as i,Text as r}from"../../ink.js";import{plural as c}from"../../utils/stringUtils.js";import{Select as l}from"../CustomSelect/select.js";import{Dialog as a}from"../design-system/Dialog.js";export function SelectEventMode(m){const d=t(23),{hookEventMetadata:u,hooksByEvent:f,totalHooksCount:g,restrictedByPolicy:h,onSelectEvent:p,onCancel:j}=m;let x;d[0]!==g?(x=c(g,"hook"),d[0]=g,d[1]=x):x=d[1];const k=`${g} ${x} configured`;let C,y,b,v,D,E,S,H;return d[2]!==h?(C=h&&o(s,{flexDirection:"column",children:[o(r,{color:"suggestion",children:[n.info," Hooks restringidos por política"]}),e(r,{dimColor:!0,children:"Solo pueden ejecutarse hooks desde configuraciones gestionadas. Los hooks definidos por usuario en ~/.context/settings.json, .context/settings.json y .context/settings.local.json están bloqueados."})]}),d[2]=h,d[3]=C):C=d[3],d[4]===Symbol.for("react.memo_cache_sentinel")?(y=e(s,{flexDirection:"column",children:o(r,{dimColor:!0,children:[n.info," Este menú es de solo lectura. Para agregar o modificar hooks, edita settings.json directamente o pídeselo a Context."," ",e(i,{url:"https://docs.iaforged.com/hooks",children:"Más info"})]})}),d[4]=y):y=d[4],d[5]!==p?(b=o=>{p(o)},d[5]=p,d[6]=b):b=d[6],d[7]!==u?(v=Object.entries(u),d[7]=u,d[8]=v):v=d[8],d[9]!==f||d[10]!==v?(D=v.map(e=>{const[t,n]=e,s=f[t]||0;return{label:s>0?o(r,{children:[t," ",o(r,{color:"suggestion",children:["(",s,")"]})]}):t,value:t,description:n.summary}}),d[9]=f,d[10]=v,d[11]=D):D=d[11],d[12]!==j||d[13]!==b||d[14]!==D?(E=e(s,{flexDirection:"column",children:e(l,{onChange:b,onCancel:j,options:D})}),d[12]=j,d[13]=b,d[14]=D,d[15]=E):E=d[15],d[16]!==C||d[17]!==E?(S=o(s,{flexDirection:"column",gap:1,children:[C,y,E]}),d[16]=C,d[17]=E,d[18]=S):S=d[18],d[19]!==j||d[20]!==k||d[21]!==S?(H=e(a,{title:"Hooks",subtitle:k,onCancel:j,children:S}),d[19]=j,d[20]=k,d[21]=S,d[22]=H):H=d[22],H}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{Box as l,color as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{Box as l,color as r,Link as a,Text as d,useTheme as i}from"../../ink.js";import{SandboxManager as t}from"../../utils/sandbox/sandbox-adapter.js";import{Select as s}from"../CustomSelect/select.js";import{useTabHeaderFocus as c}from"../design-system/Tabs.js";export function SandboxOverridesTab(r){const a=n(5),{onComplete:i}=r,s=t.isSandboxingEnabled(),c=t.areSandboxSettingsLockedByPolicy(),m=t.areUnsandboxedCommandsAllowed();if(!s){let o;return a[0]===Symbol.for("react.memo_cache_sentinel")?(o=e(l,{flexDirection:"column",paddingY:1,children:e(d,{color:"subtle",children:"Sandbox no está habilitado. Habilítalo para configurar los ajustes de sobrescritura."})}),a[0]=o):o=a[0],o}if(c){let n,r;return a[1]===Symbol.for("react.memo_cache_sentinel")?(n=e(d,{color:"subtle",children:"Override settings are managed by a higher-priority configuration and cannot be changed locally."}),a[1]=n):n=a[1],a[2]===Symbol.for("react.memo_cache_sentinel")?(r=o(l,{flexDirection:"column",paddingY:1,children:[n,e(l,{marginTop:1,children:o(d,{dimColor:!0,children:["Current setting:"," ",m?"Allow unsandboxed fallback":"Strict sandbox mode"]})})]}),a[2]=r):r=a[2],r}let b;return a[3]!==i?(b=e(OverridesSelect,{onComplete:i,currentMode:m?"open":"closed"}),a[3]=i,a[4]=b):b=a[4],b}function OverridesSelect(m){const b=n(25),{onComplete:u,currentMode:x}=m,[h]=i(),{headerFocused:f,focusHeader:p}=c();let g;b[0]!==h?(g=r("success",h)("(current)"),b[0]=h,b[1]=g):g=b[1];const S=g,y="open"===x?`Allow unsandboxed fallback ${S}`:"Allow unsandboxed fallback";let C;b[2]!==y?(C={label:y,value:"open"},b[2]=y,b[3]=C):C=b[3];const _="closed"===x?`Strict sandbox mode ${S}`:"Strict sandbox mode";let k,v;b[4]!==_?(k={label:_,value:"closed"},b[4]=_,b[5]=k):k=b[5],b[6]!==C||b[7]!==k?(v=[C,k],b[6]=C,b[7]=k,b[8]=v):v=b[8];const w=v;let j;b[9]!==u?(j=async function(e){const o=e;await t.setSandboxSettings({allowUnsandboxedCommands:"open"===o});u("open"===o?"✓ Unsandboxed fallback allowed - commands can run outside sandbox when necessary":"✓ Strict sandbox mode - all commands must run in sandbox or be excluded via the `excludedCommands` option")},b[9]=u,b[10]=j):j=b[10];const A=j;let D,O,T,U,F,Y,B;return b[11]===Symbol.for("react.memo_cache_sentinel")?(D=e(l,{marginBottom:1,children:e(d,{bold:!0,children:"Configure Overrides:"})}),b[11]=D):D=b[11],b[12]!==u?(O=()=>u(void 0,{display:"skip"}),b[12]=u,b[13]=O):O=b[13],b[14]!==p||b[15]!==A||b[16]!==f||b[17]!==w||b[18]!==O?(T=e(s,{options:w,onChange:A,onCancel:O,onUpFromFirstItem:p,isDisabled:f}),b[14]=p,b[15]=A,b[16]=f,b[17]=w,b[18]=O,b[19]=T):T=b[19],b[20]===Symbol.for("react.memo_cache_sentinel")?(U=o(d,{dimColor:!0,children:[e(d,{bold:!0,dimColor:!0,children:"Allow unsandboxed fallback:"})," ","When a command fails due to sandbox restrictions, Claude can retry with dangerouslyDisableSandbox to run outside the sandbox (falling back to default permissions)."]}),b[20]=U):U=b[20],b[21]===Symbol.for("react.memo_cache_sentinel")?(F=o(d,{dimColor:!0,children:[e(d,{bold:!0,dimColor:!0,children:"Strict sandbox mode:"})," ","All bash commands invoked by the model must run in the sandbox unless they are explicitly listed in excludedCommands."]}),b[21]=F):F=b[21],b[22]===Symbol.for("react.memo_cache_sentinel")?(Y=o(l,{flexDirection:"column",marginTop:1,gap:1,children:[U,F,o(d,{dimColor:!0,children:["Learn more:"," ",e(a,{url:"https://docs.iaforged.com/sandboxing#configure-sandboxing",children:"docs.iaforged.com/sandboxing#configure-sandboxing"})]})]}),b[22]=Y):Y=b[22],b[23]!==T?(B=o(l,{flexDirection:"column",paddingY:1,children:[D,T,Y]}),b[23]=T,b[24]=B):B=b[24],B}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{Box as t,color as a,Link as l,Text as s,useTheme as i}from"../../ink.js";import{useKeybindings as r}from"../../keybindings/useKeybinding.js";import{SandboxManager as d}from"../../utils/sandbox/sandbox-adapter.js";import{getSettings_DEPRECATED as c}from"../../utils/settings/settings.js";import{Select as m}from"../CustomSelect/select.js";import{Pane as b}from"../design-system/Pane.js";import{Tab as h,Tabs as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import{c as n}from"react/compiler-runtime";import{Box as t,color as a,Link as l,Text as s,useTheme as i}from"../../ink.js";import{useKeybindings as r}from"../../keybindings/useKeybinding.js";import{SandboxManager as d}from"../../utils/sandbox/sandbox-adapter.js";import{getSettings_DEPRECATED as c}from"../../utils/settings/settings.js";import{Select as m}from"../CustomSelect/select.js";import{Pane as b}from"../design-system/Pane.js";import{Tab as h,Tabs as p,useTabHeaderFocus as u}from"../design-system/Tabs.js";import{SandboxConfigTab as x}from"./SandboxConfigTab.js";import{SandboxDependenciesTab as S}from"./SandboxDependenciesTab.js";import{SandboxOverridesTab as g}from"./SandboxOverridesTab.js";export function SandboxSettings(o){const t=n(34),{onComplete:l,depCheck:s}=o,[m]=i(),u=d.isSandboxingEnabled(),f=d.isAutoAllowBashIfSandboxedEnabled(),w=s.warnings.length>0;let C;t[0]===Symbol.for("react.memo_cache_sentinel")?(C=c(),t[0]=C):C=t[0];const k=C,y=k.sandbox?.network?.allowAllUnixSockets,j=w&&!y,T=u?f?"auto-allow":"regular":"disabled";let _;t[1]!==m?(_=a("success",m)("(current)"),t[1]=m,t[2]=_):_=t[2];const B=_,v="auto-allow"===T?`Sandbox BashTool, with auto-allow ${B}`:"Sandbox BashTool, with auto-allow";let A;t[3]!==v?(A={label:v,value:"auto-allow"},t[3]=v,t[4]=A):A=t[4];const D="regular"===T?`Sandbox BashTool, with regular permissions ${B}`:"Sandbox BashTool, with regular permissions";let I;t[5]!==D?(I={label:D,value:"regular"},t[5]=D,t[6]=I):I=t[6];const M="disabled"===T?`No Sandbox ${B}`:"No Sandbox";let E,F;t[7]!==M?(E={label:M,value:"disabled"},t[7]=M,t[8]=E):E=t[8],t[9]!==A||t[10]!==I||t[11]!==E?(F=[A,I,E],t[9]=A,t[10]=I,t[11]=E,t[12]=F):F=t[12];const $=F;let N;t[13]!==l?(N=async function(e){e:switch(e){case"auto-allow":await d.setSandboxSettings({enabled:!0,autoAllowBashIfSandboxed:!0}),l("✓ Sandbox enabled with auto-allow for bash commands");break e;case"regular":await d.setSandboxSettings({enabled:!0,autoAllowBashIfSandboxed:!1}),l("✓ Sandbox enabled with regular bash permissions");break e;case"disabled":await d.setSandboxSettings({enabled:!1,autoAllowBashIfSandboxed:!1}),l("○ Sandbox disabled")}},t[13]=l,t[14]=N):N=t[14];const O=N;let U,W,H;t[15]!==l?(U={"confirm:no":()=>l(void 0,{display:"skip"})},t[15]=l,t[16]=U):U=t[16],t[17]===Symbol.for("react.memo_cache_sentinel")?(W={context:"Settings"},t[17]=W):W=t[17],r(U,W),t[18]!==O||t[19]!==l||t[20]!==$||t[21]!==j?(H=e(h,{title:"Mode",children:e(SandboxModeTab,{showSocketWarning:j,options:$,onSelect:O,onComplete:l})},"mode"),t[18]=O,t[19]=l,t[20]=$,t[21]=j,t[22]=H):H=t[22];const K=H;let L;t[23]!==l?(L=e(h,{title:"Overrides",children:e(g,{onComplete:l})},"overrides"),t[23]=l,t[24]=L):L=t[24];const P=L;let Y;t[25]===Symbol.for("react.memo_cache_sentinel")?(Y=e(h,{title:"Config",children:e(x,{})},"config"),t[25]=Y):Y=t[25];const q=Y,z=s.errors.length>0;let G;t[26]!==s||t[27]!==z||t[28]!==w||t[29]!==K||t[30]!==P?(G=z?[e(h,{title:"Dependencies",children:e(S,{depCheck:s})},"dependencies")]:[K,...w?[e(h,{title:"Dependencies",children:e(S,{depCheck:s})},"dependencies")]:[],P,q],t[26]=s,t[27]=z,t[28]=w,t[29]=K,t[30]=P,t[31]=G):G=t[31];const J=G;let Q;return t[32]!==J?(Q=e(b,{color:"permission",children:e(p,{title:"Sandbox:",color:"permission",defaultTab:"Mode",children:J})}),t[32]=J,t[33]=Q):Q=t[33],Q}function SandboxModeTab(a){const i=n(16),{showSocketWarning:r,options:d,onSelect:c,onComplete:b}=a,{headerFocused:h,focusHeader:p}=u();let x,S,g,f,w,C,k;return i[0]!==r?(x=r&&e(t,{marginBottom:1,children:e(s,{color:"warning",children:"Cannot block unix domain sockets (see Dependencies tab)"})}),i[0]=r,i[1]=x):x=i[1],i[2]===Symbol.for("react.memo_cache_sentinel")?(S=e(t,{marginBottom:1,children:e(s,{bold:!0,children:"Configure Mode:"})}),i[2]=S):S=i[2],i[3]!==b?(g=()=>b(void 0,{display:"skip"}),i[3]=b,i[4]=g):g=i[4],i[5]!==p||i[6]!==h||i[7]!==c||i[8]!==d||i[9]!==g?(f=e(m,{options:d,onChange:c,onCancel:g,onUpFromFirstItem:p,isDisabled:h}),i[5]=p,i[6]=h,i[7]=c,i[8]=d,i[9]=g,i[10]=f):f=i[10],i[11]===Symbol.for("react.memo_cache_sentinel")?(w=o(s,{dimColor:!0,children:[e(s,{bold:!0,dimColor:!0,children:"Auto-allow mode:"})," ","Commands will try to run in the sandbox automatically, and attempts to run outside of the sandbox fallback to regular permissions. Explicit ask/deny rules are always respected."]}),i[11]=w):w=i[11],i[12]===Symbol.for("react.memo_cache_sentinel")?(C=o(t,{flexDirection:"column",marginTop:1,gap:1,children:[w,o(s,{dimColor:!0,children:["Learn more:"," ",e(l,{url:"https://docs.iaforged.com/sandboxing",children:"docs.iaforged.com/sandboxing"})]})]}),i[12]=C):C=i[12],i[13]!==x||i[14]!==f?(k=o(t,{flexDirection:"column",paddingY:1,children:[x,S,f,C]}),i[13]=x,i[14]=f,i[15]=k):k=i[15],k}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PR_TITLE="Add Context Code GitHub Workflow";export const GITHUB_ACTION_SETUP_DOCS_URL="https://github.com/anthropics/claude-code-action/blob/main/docs/setup.md";export const WORKFLOW_CONTENT="name: Context Code\n\non:\n issue_comment:\n types: [created]\n pull_request_review_comment:\n types: [created]\n issues:\n types: [opened, assigned]\n pull_request_review:\n types: [submitted]\n\njobs:\n claude:\n if: |\n (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||\n (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||\n (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||\n (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))\n runs-on: ubuntu-latest\n permissions:\n contents: read\n pull-requests: read\n issues: read\n id-token: write\n actions: read # Required for Claude to read CI results on PRs\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n with:\n fetch-depth: 1\n\n - name: Run Context Code\n id: claude\n uses: anthropics/claude-code-action@v1\n with:\n anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n\n # This is an optional setting that allows Claude to read CI results on PRs\n additional_permissions: |\n actions: read\n\n # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.\n # prompt: 'Update the pull request description to include a summary of changes.'\n\n # Optional: Add claude_args to customize behavior and configuration\n # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md\n # or https://
|
|
1
|
+
export const PR_TITLE="Add Context Code GitHub Workflow";export const GITHUB_ACTION_SETUP_DOCS_URL="https://github.com/anthropics/claude-code-action/blob/main/docs/setup.md";export const WORKFLOW_CONTENT="name: Context Code\n\non:\n issue_comment:\n types: [created]\n pull_request_review_comment:\n types: [created]\n issues:\n types: [opened, assigned]\n pull_request_review:\n types: [submitted]\n\njobs:\n claude:\n if: |\n (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||\n (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||\n (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||\n (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))\n runs-on: ubuntu-latest\n permissions:\n contents: read\n pull-requests: read\n issues: read\n id-token: write\n actions: read # Required for Claude to read CI results on PRs\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n with:\n fetch-depth: 1\n\n - name: Run Context Code\n id: claude\n uses: anthropics/claude-code-action@v1\n with:\n anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n\n # This is an optional setting that allows Claude to read CI results on PRs\n additional_permissions: |\n actions: read\n\n # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.\n # prompt: 'Update the pull request description to include a summary of changes.'\n\n # Optional: Add claude_args to customize behavior and configuration\n # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md\n # or https://docs.iaforged.com/cli-reference for available options\n # claude_args: '--allowed-tools Bash(gh pr:*)'\n\n";export const PR_BODY="## 🤖 Installing Context Code GitHub App\n\nThis PR adds a GitHub Actions workflow that enables Context Code integration in our repository.\n\n### What is Context Code?\n\n[Context Code](https://claude.com/claude-code) is an AI coding agent that can help with:\n- Bug fixes and improvements \n- Documentation updates\n- Implementing new features\n- Code reviews and suggestions\n- Writing tests\n- And more!\n\n### How it works\n\nOnce this PR is merged, we'll be able to interact with Claude by mentioning @claude in a pull request or issue comment.\nOnce the workflow is triggered, Claude will analyze the comment and surrounding context, and execute on the request in a GitHub action.\n\n### Important Notes\n\n- **This workflow won't take effect until this PR is merged**\n- **@claude mentions won't work until after the merge is complete**\n- The workflow runs automatically whenever Claude is mentioned in PR or issue comments\n- Claude gets access to the entire PR or issue context including files, diffs, and previous comments\n\n### Security\n\n- Our Anthropic API key is securely stored as a GitHub Actions secret\n- Only users with write access to the repository can trigger the workflow\n- All Claude runs are stored in the GitHub Actions run history\n- Claude's default tools are limited to reading/writing files and interacting with our repo by creating comments, branches, and commits.\n- We can add more allowed tools by adding them to the workflow file like:\n\n```\nallowed_tools: Bash(npm install),Bash(npm run build),Bash(npm run lint),Bash(npm run test)\n```\n\nThere's more information in the [Context Code action repo](https://github.com/anthropics/claude-code-action).\n\nAfter merging this PR, let's try mentioning @claude in a comment on any PR to get started!";export const CODE_REVIEW_PLUGIN_WORKFLOW_CONTENT="name: Context Code Review\n\non:\n pull_request:\n types: [opened, synchronize, ready_for_review, reopened]\n # Optional: Only run on specific file changes\n # paths:\n # - \"src/**/*.ts\"\n # - \"src/**/*.tsx\"\n # - \"src/**/*.js\"\n # - \"src/**/*.jsx\"\n\njobs:\n claude-review:\n # Optional: Filter by PR author\n # if: |\n # github.event.pull_request.user.login == 'external-contributor' ||\n # github.event.pull_request.user.login == 'new-developer' ||\n # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'\n\n runs-on: ubuntu-latest\n permissions:\n contents: read\n pull-requests: read\n issues: read\n id-token: write\n\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n with:\n fetch-depth: 1\n\n - name: Run Context Code Review\n id: claude-review\n uses: anthropics/claude-code-action@v1\n with:\n anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'\n plugins: 'code-review@claude-code-plugins'\n prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'\n # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md\n # or https://docs.iaforged.com/cli-reference for available options\n\n";
|