






















@@ -3,16 +3,30 @@ import { normalizeOptionalString } from "../shared/string-coerce.js";
33import type { ExecElevatedDefaults } from "./bash-tools.exec-types.js";
4455const EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX = "exec-approval-followup:";
6-const EXEC_APPROVAL_FOLLOWUP_ELEVATED_TOKEN_MARKER = ":elevated:";
7-const EXEC_APPROVAL_FOLLOWUP_ELEVATED_TTL_MS = 5 * 60 * 1000;
6+const EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_NONCE_MARKER = ":nonce:";
7+const EXEC_APPROVAL_FOLLOWUP_RUNTIME_HANDOFF_TTL_MS = 5 * 60 * 1000;
889-type ExecApprovalFollowupElevatedEntry = {
9+export type ExecApprovalFollowupRuntimeHandoff = {
10+kind: "exec-approval-followup";
11+approvalId: string;
1012sessionKey: string;
13+idempotencyKey: string;
1114bashElevated: ExecElevatedDefaults;
15+};
16+17+export type ExecApprovalFollowupRuntimeHandoffRegistration = {
18+handoffId: string;
19+idempotencyKey: string;
20+};
21+22+type ExecApprovalFollowupRuntimeHandoffEntry = ExecApprovalFollowupRuntimeHandoff & {
1223expiresAtMs: number;
1324};
142515-const execApprovalFollowupElevatedDefaults = new Map<string, ExecApprovalFollowupElevatedEntry>();
26+const execApprovalFollowupRuntimeHandoffs = new Map<
27+string,
28+ExecApprovalFollowupRuntimeHandoffEntry
29+>();
16301731function cloneExecElevatedDefaults(value: ExecElevatedDefaults): ExecElevatedDefaults {
1832return {
@@ -28,96 +42,109 @@ function cloneExecElevatedDefaults(value: ExecElevatedDefaults): ExecElevatedDef
2842};
2943}
304431-function pruneExpiredExecApprovalFollowupElevatedDefaults(nowMs: number): void {
32-for (const [token, entry] of execApprovalFollowupElevatedDefaults) {
45+function cloneExecApprovalFollowupRuntimeHandoff(
46+value: ExecApprovalFollowupRuntimeHandoff,
47+): ExecApprovalFollowupRuntimeHandoff {
48+return {
49+kind: value.kind,
50+approvalId: value.approvalId,
51+sessionKey: value.sessionKey,
52+idempotencyKey: value.idempotencyKey,
53+bashElevated: cloneExecElevatedDefaults(value.bashElevated),
54+};
55+}
56+57+function pruneExpiredExecApprovalFollowupRuntimeHandoffs(nowMs: number): void {
58+for (const [handoffId, entry] of execApprovalFollowupRuntimeHandoffs) {
3359if (entry.expiresAtMs <= nowMs) {
34-execApprovalFollowupElevatedDefaults.delete(token);
60+execApprovalFollowupRuntimeHandoffs.delete(handoffId);
3561}
3662}
3763}
38643965export function buildExecApprovalFollowupIdempotencyKey(params: {
4066approvalId: string;
41-execApprovalFollowupToken?: string;
67+nonce?: string;
4268}): string {
4369const base = `${EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX}${params.approvalId}`;
44-return params.execApprovalFollowupToken
45- ? `${base}${EXEC_APPROVAL_FOLLOWUP_ELEVATED_TOKEN_MARKER}${params.execApprovalFollowupToken}`
46- : base;
70+const nonce = normalizeOptionalString(params.nonce);
71+return nonce ? `${base}${EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_NONCE_MARKER}${nonce}` : base;
4772}
487349-function parseExecApprovalFollowupToken(idempotencyKey: string): string | undefined {
50-if (!idempotencyKey.startsWith(EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX)) {
74+export function parseExecApprovalFollowupApprovalId(idempotencyKey: string): string | undefined {
75+const normalized = normalizeOptionalString(idempotencyKey);
76+if (!normalized?.startsWith(EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX)) {
5177return undefined;
5278}
53-const tokenMarker = idempotencyKey.lastIndexOf(EXEC_APPROVAL_FOLLOWUP_ELEVATED_TOKEN_MARKER);
54-if (tokenMarker < EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX.length) {
55-return undefined;
56-}
57-return normalizeOptionalString(
58-idempotencyKey.slice(tokenMarker + EXEC_APPROVAL_FOLLOWUP_ELEVATED_TOKEN_MARKER.length),
59-);
79+const body = normalized.slice(EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_PREFIX.length);
80+const nonceMarker = body.lastIndexOf(EXEC_APPROVAL_FOLLOWUP_IDEMPOTENCY_NONCE_MARKER);
81+return normalizeOptionalString(nonceMarker >= 0 ? body.slice(0, nonceMarker) : body);
6082}
618362-export function registerExecApprovalFollowupElevatedDefaults(params: {
84+export function registerExecApprovalFollowupRuntimeHandoff(params: {
85+approvalId: string;
6386sessionKey: string;
6487bashElevated?: ExecElevatedDefaults;
6588nowMs?: number;
66-}): string | undefined {
89+}): ExecApprovalFollowupRuntimeHandoffRegistration | undefined {
90+const approvalId = normalizeOptionalString(params.approvalId);
6791const sessionKey = normalizeOptionalString(params.sessionKey);
68-if (!params.bashElevated || !sessionKey) {
92+if (!approvalId || !sessionKey || !params.bashElevated) {
6993return undefined;
7094}
7195const nowMs = params.nowMs ?? Date.now();
72-pruneExpiredExecApprovalFollowupElevatedDefaults(nowMs);
73-const token = randomUUID();
74-execApprovalFollowupElevatedDefaults.set(token, {
96+pruneExpiredExecApprovalFollowupRuntimeHandoffs(nowMs);
97+const handoffId = randomUUID();
98+const idempotencyKey = buildExecApprovalFollowupIdempotencyKey({
99+ approvalId,
100+nonce: randomUUID(),
101+});
102+execApprovalFollowupRuntimeHandoffs.set(handoffId, {
103+kind: "exec-approval-followup",
104+ approvalId,
75105 sessionKey,
106+ idempotencyKey,
76107bashElevated: cloneExecElevatedDefaults(params.bashElevated),
77-expiresAtMs: nowMs + EXEC_APPROVAL_FOLLOWUP_ELEVATED_TTL_MS,
108+expiresAtMs: nowMs + EXEC_APPROVAL_FOLLOWUP_RUNTIME_HANDOFF_TTL_MS,
78109});
79-return token;
110+return { handoffId, idempotencyKey };
80111}
8111282-export function consumeExecApprovalFollowupElevatedDefaults(params: {
83-token?: string;
113+export function consumeExecApprovalFollowupRuntimeHandoff(params: {
114+handoffId?: string;
115+approvalId?: string;
116+idempotencyKey?: string;
84117sessionKey?: string;
85118nowMs?: number;
86-}): ExecElevatedDefaults | undefined {
87-const token = normalizeOptionalString(params.token);
88-if (!token) {
119+}): ExecApprovalFollowupRuntimeHandoff | undefined {
120+const handoffId = normalizeOptionalString(params.handoffId);
121+const approvalId = normalizeOptionalString(params.approvalId);
122+const idempotencyKey = normalizeOptionalString(params.idempotencyKey);
123+if (!handoffId || !approvalId || !idempotencyKey) {
89124return undefined;
90125}
91126const nowMs = params.nowMs ?? Date.now();
92-pruneExpiredExecApprovalFollowupElevatedDefaults(nowMs);
93-const entry = execApprovalFollowupElevatedDefaults.get(token);
127+pruneExpiredExecApprovalFollowupRuntimeHandoffs(nowMs);
128+const entry = execApprovalFollowupRuntimeHandoffs.get(handoffId);
94129if (!entry) {
95130return undefined;
96131}
97132if (entry.expiresAtMs <= nowMs) {
98-execApprovalFollowupElevatedDefaults.delete(token);
133+execApprovalFollowupRuntimeHandoffs.delete(handoffId);
99134return undefined;
100135}
101136const sessionKey = normalizeOptionalString(params.sessionKey);
102-if (entry.sessionKey !== sessionKey) {
137+if (
138+entry.approvalId !== approvalId ||
139+entry.idempotencyKey !== idempotencyKey ||
140+entry.sessionKey !== sessionKey
141+) {
103142return undefined;
104143}
105-execApprovalFollowupElevatedDefaults.delete(token);
106-return cloneExecElevatedDefaults(entry.bashElevated);
107-}
108-109-export function consumeExecApprovalFollowupElevatedDefaultsFromIdempotencyKey(params: {
110-idempotencyKey: string;
111-sessionKey?: string;
112-nowMs?: number;
113-}): ExecElevatedDefaults | undefined {
114-return consumeExecApprovalFollowupElevatedDefaults({
115-token: parseExecApprovalFollowupToken(params.idempotencyKey),
116-sessionKey: params.sessionKey,
117-nowMs: params.nowMs,
118-});
144+execApprovalFollowupRuntimeHandoffs.delete(handoffId);
145+return cloneExecApprovalFollowupRuntimeHandoff(entry);
119146}
120147121-export function resetExecApprovalFollowupElevatedDefaultsForTests(): void {
122-execApprovalFollowupElevatedDefaults.clear();
148+export function resetExecApprovalFollowupRuntimeHandoffsForTests(): void {
149+execApprovalFollowupRuntimeHandoffs.clear();
123150}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。