mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Fix hook trust state transitions
This commit is contained in:
parent
08cd3f624e
commit
3db509a98a
4 changed files with 33 additions and 38 deletions
|
|
@ -95,15 +95,15 @@ export async function listHookCatalog(client: AppServerRequestClient, cwd: strin
|
|||
export async function trustHookItem(client: AppServerRequestClient, hook: HookItem): Promise<void> {
|
||||
const operation = appServerHookOperationFromHookItem(hook);
|
||||
await writeHookState(client, operation.key, {
|
||||
enabled: true,
|
||||
trusted_hash: operation.currentHash,
|
||||
});
|
||||
}
|
||||
|
||||
export async function setHookItemEnabled(client: AppServerRequestClient, hook: HookItem, enabled: boolean): Promise<void> {
|
||||
if (hook.isManaged) throw new Error("Managed hooks cannot be enabled or disabled here.");
|
||||
const operation = appServerHookOperationFromHookItem(hook);
|
||||
const state: HookConfigState = operation.trustStatus === "trusted" ? { enabled, trusted_hash: operation.currentHash } : { enabled };
|
||||
await writeHookState(client, operation.key, state);
|
||||
if (operation.trustStatus !== "trusted") throw new Error("Trust the current hook definition before enabling it.");
|
||||
await writeHookState(client, operation.key, { enabled });
|
||||
}
|
||||
|
||||
type HookConfigState = Record<string, string | boolean | null>;
|
||||
|
|
|
|||
|
|
@ -44,27 +44,34 @@ function Hooks({ state }: { state: HookSectionState }): UiNode {
|
|||
|
||||
function HookRow({ hook, state }: { hook: HookItem; state: HookSectionState }): UiNode {
|
||||
const canTrust = !hook.isManaged && (hook.trustStatus === "untrusted" || hook.trustStatus === "modified");
|
||||
const canToggle = !hook.isManaged && hook.trustStatus === "trusted";
|
||||
const hookName = firstNonEmptyString(hook.statusMessage, hook.command, hook.matcher, hook.eventName);
|
||||
const executionStatus =
|
||||
hook.trustStatus === "trusted" || hook.trustStatus === "managed" ? (hook.enabled ? "enabled" : "disabled") : "inactive";
|
||||
return (
|
||||
<SettingRow
|
||||
className="codex-panel-settings__dynamic-row codex-panel-settings__hook-row"
|
||||
name={hookName}
|
||||
desc={`${hook.eventName} · ${hook.matcher ?? "(no matcher)"} · ${hook.trustStatus} · ${hook.enabled ? "enabled" : "disabled"}`}
|
||||
desc={`${hook.eventName} · ${hook.matcher ?? "(no matcher)"} · ${hook.trustStatus} · ${executionStatus}`}
|
||||
>
|
||||
<ObsidianButton
|
||||
text="Trust"
|
||||
disabled={state.loading || !canTrust}
|
||||
onClick={() => {
|
||||
state.onTrust(hook);
|
||||
}}
|
||||
/>
|
||||
<ObsidianButton
|
||||
text={hook.enabled ? "Disable" : "Enable"}
|
||||
disabled={state.loading || hook.isManaged}
|
||||
onClick={() => {
|
||||
state.onToggleEnabled(hook, !hook.enabled);
|
||||
}}
|
||||
/>
|
||||
{canTrust ? (
|
||||
<ObsidianButton
|
||||
text="Trust"
|
||||
disabled={state.loading}
|
||||
onClick={() => {
|
||||
state.onTrust(hook);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{canToggle ? (
|
||||
<ObsidianButton
|
||||
text={hook.enabled ? "Disable" : "Enable"}
|
||||
disabled={state.loading}
|
||||
onClick={() => {
|
||||
state.onToggleEnabled(hook, !hook.enabled);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</SettingRow>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ describe("app-server catalog adapters", () => {
|
|||
keyPath: "hooks.state",
|
||||
value: {
|
||||
"hook-trust": {
|
||||
enabled: true,
|
||||
trusted_hash: "newhash",
|
||||
},
|
||||
},
|
||||
|
|
@ -191,7 +190,7 @@ describe("app-server catalog adapters", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("preserves trusted hashes when toggling already trusted hooks", async () => {
|
||||
it("updates hook enablement without rewriting independently managed trust state", async () => {
|
||||
const client = {
|
||||
request: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as AppServerRequestClient;
|
||||
|
|
@ -207,7 +206,6 @@ describe("app-server catalog adapters", () => {
|
|||
value: {
|
||||
"hook-enabled": {
|
||||
enabled: false,
|
||||
trusted_hash: "trustedhash",
|
||||
},
|
||||
},
|
||||
mergeStrategy: "upsert",
|
||||
|
|
@ -217,7 +215,7 @@ describe("app-server catalog adapters", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("does not write trusted hashes when toggling untrusted hook revisions", async () => {
|
||||
it("rejects enablement changes for untrusted hook revisions", async () => {
|
||||
const client = {
|
||||
request: vi.fn().mockResolvedValue({}),
|
||||
} as unknown as AppServerRequestClient;
|
||||
|
|
@ -226,22 +224,8 @@ describe("app-server catalog adapters", () => {
|
|||
])[0];
|
||||
if (!hook) throw new Error("Expected mapped hook");
|
||||
|
||||
await setHookItemEnabled(client, hook, true);
|
||||
|
||||
expect(client.request).toHaveBeenCalledWith("config/batchWrite", {
|
||||
edits: [
|
||||
{
|
||||
keyPath: "hooks.state",
|
||||
value: {
|
||||
"hook-modified": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
mergeStrategy: "upsert",
|
||||
},
|
||||
],
|
||||
reloadUserConfig: true,
|
||||
});
|
||||
await expect(setHookItemEnabled(client, hook, true)).rejects.toThrow("Trust the current hook definition before enabling it.");
|
||||
expect(client.request).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -854,7 +854,11 @@ describe("settings tab", () => {
|
|||
expect(tab.containerEl.querySelectorAll(".codex-panel-settings__hook-list .codex-panel-settings__hook-row")).toHaveLength(1);
|
||||
expect(tab.containerEl.querySelectorAll(".codex-panel-settings__archived-list .codex-panel-settings__archived-row")).toHaveLength(1);
|
||||
expect(tab.containerEl.querySelector(".codex-panel-settings__hook-list")?.textContent).not.toContain("abc123");
|
||||
expect(tab.containerEl.querySelector(".codex-panel-settings__hook-list")?.textContent).toContain("untrusted · inactive");
|
||||
expect(tab.containerEl.querySelector(".codex-panel-settings__archived-list")?.textContent).toContain("Archived thread");
|
||||
expect(buttonTexts(tab)).toContain("Trust");
|
||||
expect(buttonTexts(tab)).not.toContain("Enable");
|
||||
expect(buttonTexts(tab)).not.toContain("Disable");
|
||||
expect(buttonLabels(tab)).toContain("Restore thread");
|
||||
expect(buttonLabels(tab)).toContain("Delete thread");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue