chore: test deploy snapshot
This commit is contained in:
@@ -29,6 +29,7 @@ const CHAT_SESSION_ID_KEY = 'main-chat-panel:session-id';
|
||||
const CHAT_LAST_EVENT_ID_STORAGE_PREFIX = 'main-chat-panel:last-event-id:';
|
||||
const CHAT_NOTIFY_OFFLINE_STORAGE_PREFIX = 'main-chat-panel:notify-offline:';
|
||||
const CHAT_SESSION_LAST_TYPE_STORAGE_PREFIX = 'main-chat-panel:last-chat-type:';
|
||||
const CHAT_SHARE_ACCESS_PIN_STORAGE_KEY = 'main-chat-panel:share-access-pins';
|
||||
const CHAT_INTRO_MESSAGE =
|
||||
'요청은 기본적으로 순차 처리됩니다. 급한 요청만 즉시 실행을 사용하세요. 여러 Codex를 추가한 즉시 실행은 병렬로 처리됩니다.';
|
||||
const CHAT_ACTIVITY_MESSAGE_PREFIX = '[[activity-log]]';
|
||||
@@ -69,6 +70,83 @@ function normalizeOptionalText(value: string | null | undefined) {
|
||||
return normalized || null;
|
||||
}
|
||||
|
||||
function canUseLocalStorage() {
|
||||
return typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
|
||||
}
|
||||
|
||||
function readStoredChatShareAccessPins() {
|
||||
if (!canUseLocalStorage()) {
|
||||
return {} as Record<string, { pin: string; expiresAtMs: number | null }>;
|
||||
}
|
||||
|
||||
try {
|
||||
const rawValue = window.localStorage.getItem(CHAT_SHARE_ACCESS_PIN_STORAGE_KEY);
|
||||
if (!rawValue) {
|
||||
return {} as Record<string, { pin: string; expiresAtMs: number | null }>;
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(rawValue) as Record<string, { pin?: unknown; expiresAtMs?: unknown }>;
|
||||
const nowMs = Date.now();
|
||||
const nextEntries = Object.entries(parsed).flatMap(([token, value]) => {
|
||||
const normalizedToken = normalizeRequiredText(token);
|
||||
const normalizedPin = normalizeRequiredText(typeof value?.pin === 'string' ? value.pin : '');
|
||||
const expiresAtMs = Number.isFinite(value?.expiresAtMs) ? Number(value.expiresAtMs) : null;
|
||||
|
||||
if (!normalizedToken || !normalizedPin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (expiresAtMs != null && expiresAtMs <= nowMs) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [[normalizedToken, { pin: normalizedPin, expiresAtMs }] as const];
|
||||
});
|
||||
|
||||
return Object.fromEntries(nextEntries);
|
||||
} catch {
|
||||
return {} as Record<string, { pin: string; expiresAtMs: number | null }>;
|
||||
}
|
||||
}
|
||||
|
||||
function writeStoredChatShareAccessPins(entries: Record<string, { pin: string; expiresAtMs: number | null }>) {
|
||||
if (!canUseLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const normalizedEntries = Object.entries(entries).flatMap(([token, value]) => {
|
||||
const normalizedToken = normalizeRequiredText(token);
|
||||
const normalizedPin = normalizeRequiredText(value?.pin);
|
||||
const expiresAtMs = Number.isFinite(value?.expiresAtMs) ? Number(value.expiresAtMs) : null;
|
||||
|
||||
if (!normalizedToken || !normalizedPin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [[normalizedToken, { pin: normalizedPin, expiresAtMs }] as const];
|
||||
});
|
||||
|
||||
if (normalizedEntries.length === 0) {
|
||||
window.localStorage.removeItem(CHAT_SHARE_ACCESS_PIN_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(
|
||||
CHAT_SHARE_ACCESS_PIN_STORAGE_KEY,
|
||||
JSON.stringify(Object.fromEntries(normalizedEntries)),
|
||||
);
|
||||
} catch {
|
||||
// Ignore storage failures in restricted runtimes.
|
||||
}
|
||||
}
|
||||
|
||||
function removeStoredChatShareAccessPin(token: string) {
|
||||
const nextEntries = readStoredChatShareAccessPins();
|
||||
delete nextEntries[token];
|
||||
writeStoredChatShareAccessPins(nextEntries);
|
||||
}
|
||||
|
||||
export function getStoredChatShareAccessPin(token?: string | null) {
|
||||
const normalizedToken = normalizeRequiredText(token);
|
||||
|
||||
@@ -79,11 +157,19 @@ export function getStoredChatShareAccessPin(token?: string | null) {
|
||||
const stored = chatShareAccessPinMemory.get(normalizedToken);
|
||||
|
||||
if (!stored) {
|
||||
return '';
|
||||
const persisted = readStoredChatShareAccessPins()[normalizedToken];
|
||||
|
||||
if (!persisted) {
|
||||
return '';
|
||||
}
|
||||
|
||||
chatShareAccessPinMemory.set(normalizedToken, persisted);
|
||||
return persisted.pin.trim();
|
||||
}
|
||||
|
||||
if (stored.expiresAtMs != null && stored.expiresAtMs <= Date.now()) {
|
||||
chatShareAccessPinMemory.delete(normalizedToken);
|
||||
removeStoredChatShareAccessPin(normalizedToken);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -100,11 +186,19 @@ export function getStoredChatShareAccessPinExpiryMs(token?: string | null) {
|
||||
const stored = chatShareAccessPinMemory.get(normalizedToken);
|
||||
|
||||
if (!stored) {
|
||||
return null;
|
||||
const persisted = readStoredChatShareAccessPins()[normalizedToken];
|
||||
|
||||
if (!persisted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
chatShareAccessPinMemory.set(normalizedToken, persisted);
|
||||
return persisted.expiresAtMs;
|
||||
}
|
||||
|
||||
if (stored.expiresAtMs != null && stored.expiresAtMs <= Date.now()) {
|
||||
chatShareAccessPinMemory.delete(normalizedToken);
|
||||
removeStoredChatShareAccessPin(normalizedToken);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -131,18 +225,23 @@ export function setStoredChatShareAccessPin(
|
||||
const expiresAt = normalizeOptionalText(options?.expiresAt);
|
||||
const expiresAtMs = expiresAt ? Date.parse(expiresAt) : Number.NaN;
|
||||
const ttlMinutes = Number.isFinite(options?.ttlMinutes) ? Math.max(0, Number(options?.ttlMinutes)) : 0;
|
||||
chatShareAccessPinMemory.set(normalizedToken, {
|
||||
const nextEntry = {
|
||||
pin: normalizedPin,
|
||||
expiresAtMs: Number.isFinite(expiresAtMs)
|
||||
? expiresAtMs
|
||||
: ttlMinutes > 0
|
||||
? Date.now() + ttlMinutes * 60 * 1000
|
||||
: null,
|
||||
});
|
||||
};
|
||||
chatShareAccessPinMemory.set(normalizedToken, nextEntry);
|
||||
const nextEntries = readStoredChatShareAccessPins();
|
||||
nextEntries[normalizedToken] = nextEntry;
|
||||
writeStoredChatShareAccessPins(nextEntries);
|
||||
return;
|
||||
}
|
||||
|
||||
chatShareAccessPinMemory.delete(normalizedToken);
|
||||
removeStoredChatShareAccessPin(normalizedToken);
|
||||
}
|
||||
|
||||
function extractChatShareTokenFromPath(path: string) {
|
||||
@@ -433,7 +532,7 @@ function mergeConversationSummaries(
|
||||
roomScope: preferred.roomScope ?? fallback.roomScope ?? null,
|
||||
notifyOffline: preferred.notifyOffline ?? fallback.notifyOffline,
|
||||
hasUnreadResponse: resolveConversationUnreadMergeState(existing, incoming),
|
||||
hasPendingAttention: preferred.hasPendingAttention === true || fallback.hasPendingAttention === true,
|
||||
hasPendingAttention: preferred.hasPendingAttention === true,
|
||||
currentRequestId: preferred.currentRequestId?.trim() || fallback.currentRequestId?.trim() || null,
|
||||
currentJobStatus: preferred.currentJobStatus ?? fallback.currentJobStatus,
|
||||
currentJobMessage: preferred.currentJobMessage?.trim() || fallback.currentJobMessage?.trim() || null,
|
||||
@@ -572,6 +671,15 @@ function normalizeChatConversationRequest(item: ChatConversationRequest): ChatCo
|
||||
totalTokens: Math.max(0, Math.round(Number(item.usageSnapshot.totalTokens ?? 0) || 0)),
|
||||
}
|
||||
: null;
|
||||
const promptContextRef =
|
||||
item.promptContextRef?.key === 'prompt_parent_question' && normalizeRequiredText(item.promptContextRef.promptTitle)
|
||||
? {
|
||||
key: 'prompt_parent_question' as const,
|
||||
promptTitle: normalizeRequiredText(item.promptContextRef.promptTitle),
|
||||
promptDescription: normalizeOptionalText(item.promptContextRef.promptDescription),
|
||||
parentQuestionText: normalizeOptionalText(item.promptContextRef.parentQuestionText),
|
||||
}
|
||||
: null;
|
||||
|
||||
return {
|
||||
...item,
|
||||
@@ -581,7 +689,9 @@ function normalizeChatConversationRequest(item: ChatConversationRequest): ChatCo
|
||||
chatTypeId: normalizeOptionalText(item.chatTypeId),
|
||||
chatTypeLabel: normalizeRequiredText(item.chatTypeLabel),
|
||||
parentRequestId: normalizeOptionalText(item.parentRequestId),
|
||||
promptContextRef,
|
||||
statusMessage: normalizeOptionalText(item.statusMessage),
|
||||
retryCount: Number.isFinite(Number(item.retryCount)) ? Math.max(0, Math.round(Number(item.retryCount))) : 0,
|
||||
userText: normalizeRequiredText(item.userText),
|
||||
responseText: normalizeRequiredText(item.responseText),
|
||||
usageSnapshot,
|
||||
@@ -1519,13 +1629,13 @@ async function requestChatApi<T>(
|
||||
sharePin?: string | null;
|
||||
},
|
||||
): Promise<T> {
|
||||
const allowUnauthenticated = options?.allowUnauthenticated === true;
|
||||
const headers = appendClientIdHeader(init?.headers);
|
||||
const accessToken = getRegisteredAccessToken();
|
||||
const method = init?.method?.toUpperCase() ?? 'GET';
|
||||
const controller = new AbortController();
|
||||
const timeoutMs = Number.isFinite(options?.timeoutMs) ? Math.max(1000, Number(options?.timeoutMs)) : 8000;
|
||||
const timeoutId = window.setTimeout(() => controller.abort(), timeoutMs);
|
||||
const allowUnauthenticated = options?.allowUnauthenticated === true;
|
||||
|
||||
if (!allowUnauthenticated && !hasRegisteredAccessTokenAccess()) {
|
||||
window.clearTimeout(timeoutId);
|
||||
@@ -2311,6 +2421,11 @@ export type ChatShareSnapshot = {
|
||||
sessionId: string;
|
||||
title: string;
|
||||
requestBadgeLabel?: string | null;
|
||||
chatTypeId?: string | null;
|
||||
lastChatTypeId?: string | null;
|
||||
contextLabel?: string | null;
|
||||
contextDescription?: string | null;
|
||||
notifyOffline?: boolean;
|
||||
};
|
||||
rootRequestId: string;
|
||||
targetRequest: ChatConversationRequest;
|
||||
@@ -2447,20 +2562,40 @@ export async function createManagedChatShareRoom(payload: ManagedChatShareRoomDr
|
||||
} satisfies ManagedChatShareRoom;
|
||||
}
|
||||
|
||||
export async function saveChatShareRoomAccessPin(
|
||||
export async function saveChatShareRoomSettings(
|
||||
token: string,
|
||||
input: {
|
||||
accessPin?: string | null;
|
||||
accessPinPromptTtlMinutes?: number | null;
|
||||
chatTypeId?: string | null;
|
||||
chatTypeLabel?: string | null;
|
||||
notifyOffline?: boolean | null;
|
||||
},
|
||||
) {
|
||||
const response = await requestChatApi<{ ok: boolean; hasAccessPin: boolean; accessPinPromptTtlMinutes?: number | null }>(
|
||||
const response = await requestChatApi<{
|
||||
ok: boolean;
|
||||
hasAccessPin: boolean;
|
||||
accessPinPromptTtlMinutes?: number | null;
|
||||
conversation?: {
|
||||
sessionId?: string | null;
|
||||
title?: string | null;
|
||||
requestBadgeLabel?: string | null;
|
||||
chatTypeId?: string | null;
|
||||
lastChatTypeId?: string | null;
|
||||
contextLabel?: string | null;
|
||||
contextDescription?: string | null;
|
||||
notifyOffline?: boolean | null;
|
||||
} | null;
|
||||
}>(
|
||||
`/shares/${encodeURIComponent(token)}/room-settings`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
accessPin: input.accessPin,
|
||||
accessPinPromptTtlMinutes: input.accessPinPromptTtlMinutes,
|
||||
chatTypeId: input.chatTypeId,
|
||||
chatTypeLabel: input.chatTypeLabel,
|
||||
notifyOffline: input.notifyOffline,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@@ -2474,6 +2609,18 @@ export async function saveChatShareRoomAccessPin(
|
||||
Number.isFinite(response.accessPinPromptTtlMinutes) && Number(response.accessPinPromptTtlMinutes) >= 0
|
||||
? Math.max(0, Number(response.accessPinPromptTtlMinutes))
|
||||
: 0,
|
||||
conversation: response.conversation
|
||||
? {
|
||||
sessionId: normalizeOptionalText(response.conversation.sessionId),
|
||||
title: normalizeOptionalText(response.conversation.title),
|
||||
requestBadgeLabel: normalizeOptionalText(response.conversation.requestBadgeLabel),
|
||||
chatTypeId: normalizeOptionalText(response.conversation.chatTypeId),
|
||||
lastChatTypeId: normalizeOptionalText(response.conversation.lastChatTypeId),
|
||||
contextLabel: normalizeOptionalText(response.conversation.contextLabel),
|
||||
contextDescription: normalizeOptionalText(response.conversation.contextDescription),
|
||||
notifyOffline: response.conversation.notifyOffline === true,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2543,7 +2690,14 @@ export async function fetchChatShareSnapshot(token: string, options?: { sharePin
|
||||
)
|
||||
: [],
|
||||
},
|
||||
conversation: response.conversation,
|
||||
conversation: {
|
||||
...response.conversation,
|
||||
chatTypeId: normalizeOptionalText(response.conversation?.chatTypeId),
|
||||
lastChatTypeId: normalizeOptionalText(response.conversation?.lastChatTypeId),
|
||||
contextLabel: normalizeOptionalText(response.conversation?.contextLabel),
|
||||
contextDescription: normalizeOptionalText(response.conversation?.contextDescription),
|
||||
notifyOffline: response.conversation?.notifyOffline === true,
|
||||
},
|
||||
rootRequestId: response.rootRequestId,
|
||||
targetRequest: normalizeChatConversationRequest(response.targetRequest),
|
||||
requests: Array.isArray(response.requests) ? response.requests.map((item) => normalizeChatConversationRequest(item)) : [],
|
||||
@@ -2566,6 +2720,7 @@ export async function submitChatShareMessage(
|
||||
token: string,
|
||||
text: string,
|
||||
options?: {
|
||||
mode?: 'queue' | 'direct';
|
||||
parentRequestId?: string | null;
|
||||
},
|
||||
) {
|
||||
@@ -2575,6 +2730,7 @@ export async function submitChatShareMessage(
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
text,
|
||||
mode: options?.mode === 'direct' ? 'direct' : 'queue',
|
||||
parentRequestId: options?.parentRequestId?.trim() || undefined,
|
||||
}),
|
||||
},
|
||||
@@ -2604,6 +2760,7 @@ export async function submitChatSharePrompt(
|
||||
summaryText?: string | null;
|
||||
attachments?: ChatComposerAttachment[];
|
||||
followupText: string;
|
||||
mode?: 'queue' | 'direct';
|
||||
contextRef?: ChatPromptContextRef | null;
|
||||
},
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user