Make index-signature access explicit

This commit is contained in:
murashit 2026-05-23 07:54:26 +09:00
parent 63086634df
commit d77b7bf526
13 changed files with 63 additions and 60 deletions

View file

@ -193,10 +193,7 @@ export class AppServerClient {
}
setHookEnabled(hook: HookMetadata, enabled: boolean): Promise<ConfigWriteResponse> {
const state: Record<string, JsonValue> = { enabled };
if (hook.trustStatus === "trusted") {
state.trusted_hash = hook.currentHash;
}
const state: Record<string, JsonValue> = hook.trustStatus === "trusted" ? { enabled, trusted_hash: hook.currentHash } : { enabled };
return this.writeHookState(hook.key, state);
}

View file

@ -9,10 +9,11 @@ export function classifyAppServerLog(message: string): ClassifiedAppServerLog {
const parsed = parseAppServerLog(normalized);
if (!parsed) return null;
const level = logString(parsed.level).toUpperCase();
const fields = parsed.fields && typeof parsed.fields === "object" ? (parsed.fields as Record<string, unknown>) : {};
const text = stripAnsi(logString(fields.message, normalized));
const target = logString(parsed.target);
const level = logString(parsed["level"]).toUpperCase();
const parsedFields = parsed["fields"];
const fields = parsedFields && typeof parsedFields === "object" ? (parsedFields as Record<string, unknown>) : {};
const text = stripAnsi(logString(fields["message"], normalized));
const target = logString(parsed["target"]);
if (target.includes("rmcp::transport::worker") && isMcpTokenRefreshLog(text)) return null;
if (target.includes("codex_core::tools::router") && text.includes("apply_patch verification failed")) return null;
if (level === "ERROR") return { kind: "error", text };

View file

@ -85,7 +85,7 @@ export class PanelSessionController {
try {
const response = await client.readAccountRateLimits();
const rateLimitsByLimitId = response.rateLimitsByLimitId;
const codexRateLimit = rateLimitsByLimitId && Object.hasOwn(rateLimitsByLimitId, "codex") ? rateLimitsByLimitId.codex : undefined;
const codexRateLimit = rateLimitsByLimitId && Object.hasOwn(rateLimitsByLimitId, "codex") ? rateLimitsByLimitId["codex"] : undefined;
this.host.state.rateLimit = codexRateLimit ?? response.rateLimits;
} catch {
this.host.state.rateLimit = null;

View file

@ -43,8 +43,9 @@ export function configRecord(effectiveConfig: ConfigReadResponse | null): Record
}
export function selectedProfileConfig(config: Record<string, unknown>): Record<string, unknown> {
const profileName = typeof config.profile === "string" && config.profile.length > 0 ? config.profile : null;
return profileName ? asRecord(asRecord(config.profiles)[profileName]) : {};
const profile = config["profile"];
const profileName = typeof profile === "string" && profile.length > 0 ? profile : null;
return profileName ? asRecord(asRecord(config["profiles"])[profileName]) : {};
}
export function resolvedConfigValue(config: Record<string, unknown>, key: string): unknown {

View file

@ -47,7 +47,7 @@ export interface EffectiveConfigSection {
export function contextSummary(snapshot: RuntimeSnapshot): ContextSummary | null {
const usage = snapshot.tokenUsage;
const config = configRecord(snapshot.effectiveConfig);
const contextWindow = usage?.modelContextWindow ?? toNumber(config.model_context_window);
const contextWindow = usage?.modelContextWindow ?? toNumber(config["model_context_window"]);
if (!usage) {
if (!snapshot.activeThreadId) return null;
if (!snapshot.displayItems.some((item) => item.turnId)) {
@ -106,15 +106,15 @@ export function rateLimitSummary(snapshot: RuntimeSnapshot, nowMs = Date.now()):
export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: string): EffectiveConfigSection[] {
const config = configRecord(snapshot.effectiveConfig);
const features = asRecord(config.features);
const features = asRecord(config["features"]);
const tools = asRecord(resolvedConfigValue(config, "tools"));
const workspaceWrite = asRecord(config.sandbox_workspace_write);
const workspaceWrite = asRecord(config["sandbox_workspace_write"]);
return [
{
title: "Scope",
rows: [
{ key: "cwd", value: vaultPath },
{ key: "profile", value: stringValue(config.profile, "(default)") },
{ key: "profile", value: stringValue(config["profile"], "(default)") },
{ key: "model provider", value: stringValue(resolvedConfigValue(config, "model_provider"), "(from default)") },
],
},
@ -141,8 +141,8 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st
{ key: "config service tier", value: configuredServiceTier(config) ?? "(not reported)" },
{ key: "active service tier", value: activeRuntimeValueLabel(snapshot.activeServiceTier) },
{ key: "fast mode", value: fastModeLabel(snapshot, config) },
{ key: "context window", value: tokenLimitLabel(config.model_context_window) },
{ key: "auto compact limit", value: tokenLimitLabel(config.model_auto_compact_token_limit) },
{ key: "context window", value: tokenLimitLabel(config["model_context_window"]) },
{ key: "auto compact limit", value: tokenLimitLabel(config["model_auto_compact_token_limit"]) },
],
},
{
@ -156,19 +156,19 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st
key: "active reviewer",
value: activeRuntimeValueLabel(snapshot.activeApprovalsReviewer),
},
{ key: "sandbox", value: stringValue(config.sandbox_mode, "(from default)") },
{ key: "workspace network", value: stringValue(workspaceWrite.network_access, "(from default)") },
{ key: "writable roots", value: writableRootsLabel(workspaceWrite.writable_roots) },
{ key: "sandbox", value: stringValue(config["sandbox_mode"], "(from default)") },
{ key: "workspace network", value: stringValue(workspaceWrite["network_access"], "(from default)") },
{ key: "writable roots", value: writableRootsLabel(workspaceWrite["writable_roots"]) },
{ key: "web search", value: stringValue(resolvedConfigValue(config, "web_search"), "(from default)") },
],
},
{
title: "Features",
rows: [
{ key: "hooks", value: stringValue(features.hooks, "(from default)") },
{ key: "apply patch freeform", value: stringValue(features.apply_patch_freeform, "(from default)") },
{ key: "tool web search", value: stringValue(tools.web_search, "(from default)") },
{ key: "apps", value: enabledAppsLabel(config.apps) },
{ key: "hooks", value: stringValue(features["hooks"], "(from default)") },
{ key: "apply patch freeform", value: stringValue(features["apply_patch_freeform"], "(from default)") },
{ key: "tool web search", value: stringValue(tools["web_search"], "(from default)") },
{ key: "apps", value: enabledAppsLabel(config["apps"]) },
],
},
];
@ -275,12 +275,12 @@ function writableRootsLabel(value: unknown): string {
function enabledAppsLabel(value: unknown): string {
const apps = asRecord(value);
const enabled = Object.entries(apps)
.filter(([key, app]) => key !== "_default" && asRecord(app).enabled === true)
.filter(([key, app]) => key !== "_default" && asRecord(app)["enabled"] === true)
.map(([key]) => key)
.sort();
if (enabled.length > 0) return enabled.join(", ");
const defaultConfig = asRecord(apps._default);
return stringValue(defaultConfig.enabled, "(from default)");
const defaultConfig = asRecord(apps["_default"]);
return stringValue(defaultConfig["enabled"], "(from default)");
}
function asRecord(value: unknown): Record<string, unknown> {

View file

@ -35,18 +35,21 @@ export const DEFAULT_SETTINGS: CodexPanelSettings = {
export function normalizeSettings(data: unknown): CodexPanelSettings {
const record = asRecord(data);
return {
codexPath: stringOrDefault(record.codexPath, DEFAULT_CODEX_PATH).trim() || DEFAULT_CODEX_PATH,
threadNamingModel: threadNamingModelOrDefault(record.threadNamingModel),
threadNamingEffort: reasoningEffortOrDefault(record.threadNamingEffort),
rewriteSelectionModel: modelOrDefault(record.rewriteSelectionModel),
rewriteSelectionEffort: reasoningEffortOrDefault(record.rewriteSelectionEffort),
sendShortcut: sendShortcutOrDefault(record.sendShortcut),
archiveExportEnabled: booleanOrDefault(record.archiveExportEnabled, DEFAULT_SETTINGS.archiveExportEnabled),
archiveExportFolderTemplate: stringOrDefault(record.archiveExportFolderTemplate, DEFAULT_SETTINGS.archiveExportFolderTemplate).trim(),
codexPath: stringOrDefault(record["codexPath"], DEFAULT_CODEX_PATH).trim() || DEFAULT_CODEX_PATH,
threadNamingModel: threadNamingModelOrDefault(record["threadNamingModel"]),
threadNamingEffort: reasoningEffortOrDefault(record["threadNamingEffort"]),
rewriteSelectionModel: modelOrDefault(record["rewriteSelectionModel"]),
rewriteSelectionEffort: reasoningEffortOrDefault(record["rewriteSelectionEffort"]),
sendShortcut: sendShortcutOrDefault(record["sendShortcut"]),
archiveExportEnabled: booleanOrDefault(record["archiveExportEnabled"], DEFAULT_SETTINGS.archiveExportEnabled),
archiveExportFolderTemplate: stringOrDefault(
record["archiveExportFolderTemplate"],
DEFAULT_SETTINGS.archiveExportFolderTemplate,
).trim(),
archiveExportFilenameTemplate:
stringOrDefault(record.archiveExportFilenameTemplate, DEFAULT_SETTINGS.archiveExportFilenameTemplate).trim() ||
stringOrDefault(record["archiveExportFilenameTemplate"], DEFAULT_SETTINGS.archiveExportFilenameTemplate).trim() ||
DEFAULT_SETTINGS.archiveExportFilenameTemplate,
archiveExportTags: stringOrDefault(record.archiveExportTags, DEFAULT_SETTINGS.archiveExportTags).trim(),
archiveExportTags: stringOrDefault(record["archiveExportTags"], DEFAULT_SETTINGS.archiveExportTags).trim(),
};
}
@ -54,16 +57,16 @@ export function settingsMatchNormalizedData(data: unknown, settings: CodexPanelS
const record = asRecord(data);
return (
Object.keys(record).length === 10 &&
record.codexPath === settings.codexPath &&
record.threadNamingModel === settings.threadNamingModel &&
record.threadNamingEffort === settings.threadNamingEffort &&
record.rewriteSelectionModel === settings.rewriteSelectionModel &&
record.rewriteSelectionEffort === settings.rewriteSelectionEffort &&
record.sendShortcut === settings.sendShortcut &&
record.archiveExportEnabled === settings.archiveExportEnabled &&
record.archiveExportFolderTemplate === settings.archiveExportFolderTemplate &&
record.archiveExportFilenameTemplate === settings.archiveExportFilenameTemplate &&
record.archiveExportTags === settings.archiveExportTags
record["codexPath"] === settings.codexPath &&
record["threadNamingModel"] === settings.threadNamingModel &&
record["threadNamingEffort"] === settings.threadNamingEffort &&
record["rewriteSelectionModel"] === settings.rewriteSelectionModel &&
record["rewriteSelectionEffort"] === settings.rewriteSelectionEffort &&
record["sendShortcut"] === settings.sendShortcut &&
record["archiveExportEnabled"] === settings.archiveExportEnabled &&
record["archiveExportFolderTemplate"] === settings.archiveExportFolderTemplate &&
record["archiveExportFilenameTemplate"] === settings.archiveExportFilenameTemplate &&
record["archiveExportTags"] === settings.archiveExportTags
);
}

View file

@ -90,8 +90,8 @@ export function syncComposerControls(
sendButton.classList.toggle("is-interrupt", interruptMode);
sendButton.classList.toggle("is-steer", canSteer);
const mode = interruptMode ? "interrupt" : canSteer ? "steer" : "send";
if (sendButton.dataset.codexMode !== mode) {
sendButton.dataset.codexMode = mode;
if (sendButton.dataset["codexMode"] !== mode) {
sendButton.dataset["codexMode"] = mode;
setButtonIcon(sendButton, interruptMode ? "square" : canSteer ? "corner-down-right" : "send");
}
}

View file

@ -117,7 +117,7 @@ export function messageRenderBlocks(context: MessageStreamContext): MessageRende
export function syncMessageRenderBlocks(parent: HTMLElement, blocks: MessageRenderBlock[], signatures: Map<string, string>): void {
const existing = new Map<string, HTMLElement>();
parent.querySelectorAll<HTMLElement>(":scope > [data-codex-panel-block-key]").forEach((element) => {
const key = element.dataset.codexPanelBlockKey;
const key = element.dataset["codexPanelBlockKey"];
if (key) existing.set(key, element);
});
@ -129,8 +129,8 @@ export function syncMessageRenderBlocks(parent: HTMLElement, blocks: MessageRend
const currentWasNext = current === nextPosition;
if (!element || signatures.get(block.key) !== block.signature) {
element = block.render();
element.dataset.codexPanelBlockKey = block.key;
element.dataset.codexPanelBlockSignature = shortSignature(block.signature);
element.dataset["codexPanelBlockKey"] = block.key;
element.dataset["codexPanelBlockSignature"] = shortSignature(block.signature);
signatures.set(block.key, block.signature);
if (current) {
current.replaceWith(element);

View file

@ -244,7 +244,7 @@ describe("AppServerClient", () => {
input: [{ type: "text", text: "default", text_elements: [] }],
},
});
expect((transport.sent[2] as { params?: Record<string, unknown> }).params?.collaborationMode).toBeUndefined();
expect((transport.sent[2] as { params?: Record<string, unknown> }).params?.["collaborationMode"]).toBeUndefined();
transport.emitLine({ id: 2, result: { turn: { id: "turn-default" } } });
await defaultTurn;
@ -492,7 +492,7 @@ describe("AppServerClient", () => {
method: "skills/list",
params: { cwds: ["/vault"], forceReload: false },
});
expect((transport.sent[2] as { params?: Record<string, unknown> }).params?.perCwdExtraUserRoots).toBeUndefined();
expect((transport.sent[2] as { params?: Record<string, unknown> }).params?.["perCwdExtraUserRoots"]).toBeUndefined();
transport.emitLine({ id: 2, result: { data: [] } });
await skills;

View file

@ -248,7 +248,7 @@ class ToggleComponent {
}
export function setIcon(element: HTMLElement, icon: string): void {
element.dataset.icon = icon;
element.dataset["icon"] = icon;
}
function ensureElementHelpers(): void {

View file

@ -149,7 +149,7 @@ describe("runtime settings", () => {
effectiveConfig: { config } as unknown as RuntimeSnapshot["effectiveConfig"],
});
expect(config.approvals_reviewer).toBe("auto_review");
expect(config["approvals_reviewer"]).toBe("auto_review");
expect(autoReviewActive(snapshot, config)).toBe(false);
});
@ -360,8 +360,8 @@ describe("runtime settings", () => {
"active service tier": "(not reported)",
"fast mode": "on",
});
expect(policyRows.approval).toBe("never");
expect(policyRows.reviewer).toBe("auto_review");
expect(policyRows["approval"]).toBe("never");
expect(policyRows["reviewer"]).toBe("auto_review");
expect(policyRows["auto-review"]).toBe("on");
expect(policyRows["config reviewer"]).toBe("auto_review");
expect(policyRows["active reviewer"]).toBe("(not reported)");

View file

@ -2157,14 +2157,14 @@ describe("composer renderer decisions", () => {
expect(sendButton?.getAttribute("aria-label")).toBe("Interrupt");
expect(sendButton?.classList.contains("is-interrupt")).toBe(true);
expect(sendButton?.classList.contains("is-steer")).toBe(false);
expect(sendButton?.dataset.icon).toBe("square");
expect(sendButton?.dataset["icon"]).toBe("square");
composer.value = "adjust course";
syncComposerControls(parent, composer, true, true);
expect(sendButton?.getAttribute("aria-label")).toBe("Steer");
expect(sendButton?.classList.contains("is-interrupt")).toBe(false);
expect(sendButton?.classList.contains("is-steer")).toBe(true);
expect(sendButton?.dataset.icon).toBe("corner-down-right");
expect(sendButton?.dataset["icon"]).toBe("corner-down-right");
});
it("honors the smaller viewport branch of the composer max-height CSS", () => {

View file

@ -13,6 +13,7 @@
"noImplicitOverride": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,