@nocoo/pew 0.6.1 → 0.7.0

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.
Files changed (60) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +179 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/init.d.ts +38 -0
  5. package/dist/commands/init.d.ts.map +1 -0
  6. package/dist/commands/init.js +73 -0
  7. package/dist/commands/init.js.map +1 -0
  8. package/dist/commands/notify.d.ts +11 -0
  9. package/dist/commands/notify.d.ts.map +1 -0
  10. package/dist/commands/notify.js +28 -0
  11. package/dist/commands/notify.js.map +1 -0
  12. package/dist/commands/status.d.ts +4 -0
  13. package/dist/commands/status.d.ts.map +1 -1
  14. package/dist/commands/status.js +1 -0
  15. package/dist/commands/status.js.map +1 -1
  16. package/dist/commands/uninstall.d.ts +39 -0
  17. package/dist/commands/uninstall.d.ts.map +1 -0
  18. package/dist/commands/uninstall.js +107 -0
  19. package/dist/commands/uninstall.js.map +1 -0
  20. package/dist/notifier/claude-hook.d.ts +18 -0
  21. package/dist/notifier/claude-hook.d.ts.map +1 -0
  22. package/dist/notifier/claude-hook.js +226 -0
  23. package/dist/notifier/claude-hook.js.map +1 -0
  24. package/dist/notifier/codex-notifier.d.ts +19 -0
  25. package/dist/notifier/codex-notifier.d.ts.map +1 -0
  26. package/dist/notifier/codex-notifier.js +258 -0
  27. package/dist/notifier/codex-notifier.js.map +1 -0
  28. package/dist/notifier/coordinator.d.ts +29 -0
  29. package/dist/notifier/coordinator.d.ts.map +1 -0
  30. package/dist/notifier/coordinator.js +176 -0
  31. package/dist/notifier/coordinator.js.map +1 -0
  32. package/dist/notifier/gemini-hook.d.ts +18 -0
  33. package/dist/notifier/gemini-hook.d.ts.map +1 -0
  34. package/dist/notifier/gemini-hook.js +243 -0
  35. package/dist/notifier/gemini-hook.js.map +1 -0
  36. package/dist/notifier/notify-handler.d.ts +41 -0
  37. package/dist/notifier/notify-handler.d.ts.map +1 -0
  38. package/dist/notifier/notify-handler.js +172 -0
  39. package/dist/notifier/notify-handler.js.map +1 -0
  40. package/dist/notifier/openclaw-hook.d.ts +28 -0
  41. package/dist/notifier/openclaw-hook.d.ts.map +1 -0
  42. package/dist/notifier/openclaw-hook.js +272 -0
  43. package/dist/notifier/openclaw-hook.js.map +1 -0
  44. package/dist/notifier/opencode-plugin.d.ts +20 -0
  45. package/dist/notifier/opencode-plugin.d.ts.map +1 -0
  46. package/dist/notifier/opencode-plugin.js +106 -0
  47. package/dist/notifier/opencode-plugin.js.map +1 -0
  48. package/dist/notifier/paths.d.ts +21 -0
  49. package/dist/notifier/paths.d.ts.map +1 -0
  50. package/dist/notifier/paths.js +36 -0
  51. package/dist/notifier/paths.js.map +1 -0
  52. package/dist/notifier/registry.d.ts +21 -0
  53. package/dist/notifier/registry.d.ts.map +1 -0
  54. package/dist/notifier/registry.js +145 -0
  55. package/dist/notifier/registry.js.map +1 -0
  56. package/dist/utils/paths.d.ts +4 -0
  57. package/dist/utils/paths.d.ts.map +1 -1
  58. package/dist/utils/paths.js +4 -0
  59. package/dist/utils/paths.js.map +1 -1
  60. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ import { unlink } from "node:fs/promises";
2
+ import { removeNotifyHandler } from "../notifier/notify-handler.js";
3
+ import { resolveNotifierPaths } from "../notifier/paths.js";
4
+ import { getAllDrivers, getDriver, uninstallAll, } from "../notifier/registry.js";
5
+ export async function executeUninstall(opts) {
6
+ const resolveNotifierPathsFn = opts.resolveNotifierPathsFn ?? resolveNotifierPaths;
7
+ const getAllDriversFn = opts.getAllDriversFn ?? getAllDrivers;
8
+ const paths = resolveNotifierPathsFn(opts.home, opts.env);
9
+ const allSources = getAllDriversFn().map((driver) => driver.source);
10
+ const selectedSources = opts.sources && opts.sources.length > 0 ? opts.sources : allSources;
11
+ const fullUninstall = selectedSources.length === allSources.length &&
12
+ allSources.every((source) => selectedSources.includes(source));
13
+ const shouldRemoveCodexBackup = fullUninstall || selectedSources.includes("codex");
14
+ if (opts.dryRun) {
15
+ return {
16
+ notifyHandler: {
17
+ changed: false,
18
+ path: paths.notifyPath,
19
+ detail: fullUninstall ? "dry-run" : "shared artifact kept",
20
+ },
21
+ codexBackup: {
22
+ changed: false,
23
+ path: paths.codexNotifyOriginalPath,
24
+ detail: shouldRemoveCodexBackup ? "dry-run" : "not selected",
25
+ },
26
+ hooks: selectedSources.map((source) => ({
27
+ source,
28
+ action: "skip",
29
+ changed: false,
30
+ detail: "dry-run",
31
+ })),
32
+ };
33
+ }
34
+ let hooks;
35
+ if (fullUninstall) {
36
+ const uninstallAllFn = opts.uninstallAllFn ?? uninstallAll;
37
+ hooks = await uninstallAllFn(paths, { spawn: opts.spawn });
38
+ }
39
+ else {
40
+ const uninstallDriverFn = opts.uninstallDriverFn ??
41
+ (async (source, notifierPaths, deps) => {
42
+ const driver = getDriver(source);
43
+ if (!driver) {
44
+ return {
45
+ source,
46
+ action: "skip",
47
+ changed: false,
48
+ detail: "Unknown source",
49
+ };
50
+ }
51
+ return driver.uninstall(notifierPaths, deps);
52
+ });
53
+ hooks = [];
54
+ for (const source of selectedSources) {
55
+ try {
56
+ hooks.push(await uninstallDriverFn(source, paths, { spawn: opts.spawn }));
57
+ }
58
+ catch (error) {
59
+ hooks.push({
60
+ source,
61
+ action: "skip",
62
+ changed: false,
63
+ detail: error instanceof Error ? error.message : String(error),
64
+ warnings: ["Driver uninstall failed"],
65
+ });
66
+ }
67
+ }
68
+ }
69
+ const removeNotifyHandlerFn = opts.removeNotifyHandlerFn ?? removeNotifyHandler;
70
+ const removeCodexBackupFn = opts.removeCodexBackupFn ?? removeOptionalFile;
71
+ const notifyHandler = fullUninstall
72
+ ? await removeNotifyHandlerFn({ notifyPath: paths.notifyPath })
73
+ : {
74
+ changed: false,
75
+ path: paths.notifyPath,
76
+ detail: "shared artifact kept",
77
+ };
78
+ const codexBackup = shouldRemoveCodexBackup
79
+ ? await removeCodexBackupFn(paths.codexNotifyOriginalPath)
80
+ : {
81
+ changed: false,
82
+ path: paths.codexNotifyOriginalPath,
83
+ detail: "not selected",
84
+ };
85
+ return { notifyHandler, codexBackup, hooks };
86
+ }
87
+ async function removeOptionalFile(path) {
88
+ try {
89
+ await unlink(path);
90
+ return {
91
+ changed: true,
92
+ path,
93
+ detail: "artifact removed",
94
+ };
95
+ }
96
+ catch (err) {
97
+ if (err?.code === "ENOENT") {
98
+ return {
99
+ changed: false,
100
+ path,
101
+ detail: "artifact not found",
102
+ };
103
+ }
104
+ throw err;
105
+ }
106
+ }
107
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { NotifierOperationResult, NotifierStatus } from "@pew/core";
2
+ interface ClaudeHookFs {
3
+ readFile: (path: string, encoding: BufferEncoding) => Promise<string>;
4
+ writeFile: (path: string, data: string, encoding: BufferEncoding) => Promise<unknown>;
5
+ mkdir: (path: string, options: {
6
+ recursive: boolean;
7
+ }) => Promise<unknown>;
8
+ }
9
+ export interface ClaudeHookOptions {
10
+ settingsPath: string;
11
+ notifyPath: string;
12
+ fs?: ClaudeHookFs;
13
+ }
14
+ export declare function installClaudeHook(opts: ClaudeHookOptions): Promise<NotifierOperationResult>;
15
+ export declare function uninstallClaudeHook(opts: ClaudeHookOptions): Promise<NotifierOperationResult>;
16
+ export declare function getClaudeHookStatus(opts: ClaudeHookOptions): Promise<NotifierStatus>;
17
+ export {};
18
+ //# sourceMappingURL=claude-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hook.d.ts","sourceRoot":"","sources":["../../src/notifier/claude-hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzE,UAAU,YAAY;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,uBAAuB,CAAC,CAwDlC;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,uBAAuB,CAAC,CA2DlC;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAW1F"}
@@ -0,0 +1,226 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ const EVENT_NAME = "SessionEnd";
3
+ const SOURCE = "claude-code";
4
+ export async function installClaudeHook(opts) {
5
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
6
+ const command = buildClaudeHookCommand(opts.notifyPath);
7
+ const loaded = await loadSettings(opts.settingsPath, fs);
8
+ if (loaded.status === "invalid") {
9
+ return {
10
+ source: SOURCE,
11
+ action: "skip",
12
+ changed: false,
13
+ detail: "Invalid Claude settings.json",
14
+ };
15
+ }
16
+ const settings = loaded.settings ?? {};
17
+ const hooks = normalizeObject(settings.hooks);
18
+ const entries = normalizeArray(hooks[EVENT_NAME]);
19
+ let changed = false;
20
+ const nextEntries = entries.map((entry) => {
21
+ const normalized = normalizeEntry(entry, command);
22
+ if (normalized !== entry)
23
+ changed = true;
24
+ return normalized;
25
+ });
26
+ if (!hasCommand(nextEntries, command)) {
27
+ nextEntries.push({
28
+ hooks: [{ type: "command", command }],
29
+ });
30
+ changed = true;
31
+ }
32
+ if (!changed) {
33
+ return {
34
+ source: SOURCE,
35
+ action: "install",
36
+ changed: false,
37
+ detail: "Claude hook already installed",
38
+ };
39
+ }
40
+ const nextSettings = {
41
+ ...settings,
42
+ hooks: {
43
+ ...hooks,
44
+ [EVENT_NAME]: nextEntries,
45
+ },
46
+ };
47
+ const backupPath = await writeSettings(opts.settingsPath, nextSettings, loaded.raw, fs);
48
+ return {
49
+ source: SOURCE,
50
+ action: "install",
51
+ changed: true,
52
+ detail: "Claude hook installed",
53
+ backupPath: backupPath ?? undefined,
54
+ };
55
+ }
56
+ export async function uninstallClaudeHook(opts) {
57
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
58
+ const command = buildClaudeHookCommand(opts.notifyPath);
59
+ const loaded = await loadSettings(opts.settingsPath, fs);
60
+ if (loaded.status === "missing") {
61
+ return {
62
+ source: SOURCE,
63
+ action: "skip",
64
+ changed: false,
65
+ detail: "Claude settings.json not found",
66
+ };
67
+ }
68
+ if (loaded.status === "invalid" || !loaded.settings) {
69
+ return {
70
+ source: SOURCE,
71
+ action: "skip",
72
+ changed: false,
73
+ detail: "Invalid Claude settings.json",
74
+ };
75
+ }
76
+ const settings = loaded.settings;
77
+ const hooks = normalizeObject(settings.hooks);
78
+ const entries = normalizeArray(hooks[EVENT_NAME]);
79
+ let removed = false;
80
+ const nextEntries = entries
81
+ .map((entry) => {
82
+ const stripped = stripCommand(entry, command);
83
+ if (stripped !== entry)
84
+ removed = true;
85
+ return stripped;
86
+ })
87
+ .filter(Boolean);
88
+ if (!removed) {
89
+ return {
90
+ source: SOURCE,
91
+ action: "skip",
92
+ changed: false,
93
+ detail: "Claude hook not installed",
94
+ };
95
+ }
96
+ const nextHooks = { ...hooks };
97
+ if (nextEntries.length > 0)
98
+ nextHooks[EVENT_NAME] = nextEntries;
99
+ else
100
+ delete nextHooks[EVENT_NAME];
101
+ const nextSettings = { ...settings };
102
+ if (Object.keys(nextHooks).length > 0)
103
+ nextSettings.hooks = nextHooks;
104
+ else
105
+ delete nextSettings.hooks;
106
+ const backupPath = await writeSettings(opts.settingsPath, nextSettings, loaded.raw, fs);
107
+ return {
108
+ source: SOURCE,
109
+ action: "uninstall",
110
+ changed: true,
111
+ detail: "Claude hook removed",
112
+ backupPath: backupPath ?? undefined,
113
+ };
114
+ }
115
+ export async function getClaudeHookStatus(opts) {
116
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
117
+ const loaded = await loadSettings(opts.settingsPath, fs);
118
+ if (loaded.status === "missing")
119
+ return "not-installed";
120
+ if (loaded.status === "invalid" || !loaded.settings)
121
+ return "error";
122
+ return hasCommand(normalizeArray(normalizeObject(loaded.settings.hooks)[EVENT_NAME]), buildClaudeHookCommand(opts.notifyPath))
123
+ ? "installed"
124
+ : "not-installed";
125
+ }
126
+ function buildClaudeHookCommand(notifyPath) {
127
+ return `/usr/bin/env node ${quoteArg(notifyPath)} --source=${SOURCE}`;
128
+ }
129
+ async function loadSettings(settingsPath, fs) {
130
+ try {
131
+ const raw = await fs.readFile(settingsPath, "utf8");
132
+ try {
133
+ const parsed = JSON.parse(raw);
134
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
135
+ return { status: "invalid", settings: null, raw };
136
+ }
137
+ return { status: "ok", settings: parsed, raw };
138
+ }
139
+ catch {
140
+ return { status: "invalid", settings: null, raw };
141
+ }
142
+ }
143
+ catch (err) {
144
+ if (err?.code === "ENOENT") {
145
+ return { status: "missing", settings: null, raw: null };
146
+ }
147
+ throw err;
148
+ }
149
+ }
150
+ function normalizeObject(value) {
151
+ return value && typeof value === "object" && !Array.isArray(value)
152
+ ? { ...value }
153
+ : {};
154
+ }
155
+ function normalizeArray(value) {
156
+ return Array.isArray(value)
157
+ ? value.filter((entry) => Boolean(entry && typeof entry === "object"))
158
+ : [];
159
+ }
160
+ function normalizeEntry(entry, command) {
161
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : null;
162
+ if (!hooks)
163
+ return entry;
164
+ let changed = false;
165
+ const nextHooks = hooks.map((hook) => {
166
+ if (!hook || typeof hook !== "object")
167
+ return hook;
168
+ const hookObject = hook;
169
+ if (!commandMatches(hookObject.command, command))
170
+ return hookObject;
171
+ if (hookObject.type === "command")
172
+ return hookObject;
173
+ changed = true;
174
+ return { ...hookObject, type: "command" };
175
+ });
176
+ return changed ? { ...entry, hooks: nextHooks } : entry;
177
+ }
178
+ function stripCommand(entry, command) {
179
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : null;
180
+ if (!hooks)
181
+ return entry;
182
+ const nextHooks = hooks.filter((hook) => {
183
+ if (!hook || typeof hook !== "object")
184
+ return true;
185
+ return !commandMatches(hook.command, command);
186
+ });
187
+ if (nextHooks.length === hooks.length)
188
+ return entry;
189
+ if (nextHooks.length === 0)
190
+ return null;
191
+ return { ...entry, hooks: nextHooks };
192
+ }
193
+ function hasCommand(entries, command) {
194
+ return entries.some((entry) => {
195
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
196
+ return hooks.some((hook) => {
197
+ if (!hook || typeof hook !== "object")
198
+ return false;
199
+ return commandMatches(hook.command, command);
200
+ });
201
+ });
202
+ }
203
+ function commandMatches(value, command) {
204
+ return value === command;
205
+ }
206
+ async function writeSettings(settingsPath, settings, previousRaw, fs) {
207
+ const backupPath = previousRaw !== null
208
+ ? `${settingsPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`
209
+ : null;
210
+ await fs.mkdir(new URL(".", `file://${settingsPath}`).pathname, { recursive: true }).catch(async () => {
211
+ const lastSlash = settingsPath.lastIndexOf("/");
212
+ const dir = lastSlash >= 0 ? settingsPath.slice(0, lastSlash) : ".";
213
+ await fs.mkdir(dir, { recursive: true });
214
+ });
215
+ if (backupPath && previousRaw !== null) {
216
+ await fs.writeFile(backupPath, previousRaw, "utf8");
217
+ }
218
+ await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
219
+ return backupPath;
220
+ }
221
+ function quoteArg(value) {
222
+ if (/^[A-Za-z0-9_\-./:@]+$/.test(value))
223
+ return value;
224
+ return `"${value.replace(/"/g, '\\"')}"`;
225
+ }
226
+ //# sourceMappingURL=claude-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hook.js","sourceRoot":"","sources":["../../src/notifier/claude-hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { NotifierOperationResult, NotifierStatus } from "@pew/core";
2
+ interface CodexNotifierFs {
3
+ readFile: (path: string, encoding: BufferEncoding) => Promise<string>;
4
+ writeFile: (path: string, data: string, encoding: BufferEncoding) => Promise<unknown>;
5
+ mkdir: (path: string, options: {
6
+ recursive: boolean;
7
+ }) => Promise<unknown>;
8
+ }
9
+ export interface CodexNotifierOptions {
10
+ configPath: string;
11
+ notifyPath: string;
12
+ originalBackupPath: string;
13
+ fs?: CodexNotifierFs;
14
+ }
15
+ export declare function installCodexNotifier(opts: CodexNotifierOptions): Promise<NotifierOperationResult>;
16
+ export declare function uninstallCodexNotifier(opts: CodexNotifierOptions): Promise<NotifierOperationResult>;
17
+ export declare function getCodexNotifierStatus(opts: CodexNotifierOptions): Promise<NotifierStatus>;
18
+ export {};
19
+ //# sourceMappingURL=codex-notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-notifier.d.ts","sourceRoot":"","sources":["../../src/notifier/codex-notifier.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzE,UAAU,eAAe;IACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,EAAE,CAAC,EAAE,eAAe,CAAC;CACtB;AAID,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CA+ClC;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CAwClC;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,cAAc,CAAC,CAOzB"}
@@ -0,0 +1,258 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ const SOURCE = "codex";
4
+ export async function installCodexNotifier(opts) {
5
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
6
+ const text = await readOptional(opts.configPath, fs);
7
+ if (text === null) {
8
+ return {
9
+ source: SOURCE,
10
+ action: "skip",
11
+ changed: false,
12
+ detail: "Codex config.toml not found",
13
+ };
14
+ }
15
+ const notify = buildNotifyCommand(opts.notifyPath);
16
+ const existingNotify = extractNotify(text);
17
+ if (arraysEqual(existingNotify, notify)) {
18
+ return {
19
+ source: SOURCE,
20
+ action: "install",
21
+ changed: false,
22
+ detail: "Codex notifier already installed",
23
+ };
24
+ }
25
+ if (existingNotify && existingNotify.length > 0) {
26
+ const existingBackup = await readOptional(opts.originalBackupPath, fs);
27
+ if (existingBackup === null) {
28
+ await fs.mkdir(dirname(opts.originalBackupPath), { recursive: true });
29
+ await fs.writeFile(opts.originalBackupPath, `${JSON.stringify({ notify: existingNotify, capturedAt: new Date().toISOString() }, null, 2)}\n`, "utf8");
30
+ }
31
+ }
32
+ const updated = setNotify(text, notify);
33
+ const backupPath = `${opts.configPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
34
+ await fs.writeFile(backupPath, text, "utf8");
35
+ await fs.writeFile(opts.configPath, updated, "utf8");
36
+ return {
37
+ source: SOURCE,
38
+ action: "install",
39
+ changed: true,
40
+ detail: "Codex notifier installed",
41
+ backupPath,
42
+ };
43
+ }
44
+ export async function uninstallCodexNotifier(opts) {
45
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
46
+ const text = await readOptional(opts.configPath, fs);
47
+ if (text === null) {
48
+ return {
49
+ source: SOURCE,
50
+ action: "skip",
51
+ changed: false,
52
+ detail: "Codex config.toml not found",
53
+ };
54
+ }
55
+ const existingNotify = extractNotify(text);
56
+ const expectedNotify = buildNotifyCommand(opts.notifyPath);
57
+ if (!arraysEqual(existingNotify, expectedNotify)) {
58
+ return {
59
+ source: SOURCE,
60
+ action: "skip",
61
+ changed: false,
62
+ detail: "Codex notifier not installed",
63
+ };
64
+ }
65
+ const originalBackup = await readOptional(opts.originalBackupPath, fs);
66
+ const originalNotify = originalBackup
67
+ ? JSON.parse(originalBackup).notify ?? null
68
+ : null;
69
+ const updated = originalNotify ? setNotify(text, originalNotify) : removeNotify(text);
70
+ const backupPath = `${opts.configPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
71
+ await fs.writeFile(backupPath, text, "utf8");
72
+ await fs.writeFile(opts.configPath, updated, "utf8");
73
+ return {
74
+ source: SOURCE,
75
+ action: "uninstall",
76
+ changed: true,
77
+ detail: originalNotify ? "Codex notifier restored" : "Codex notifier removed",
78
+ backupPath,
79
+ };
80
+ }
81
+ export async function getCodexNotifierStatus(opts) {
82
+ const fs = opts.fs ?? { readFile, writeFile, mkdir };
83
+ const text = await readOptional(opts.configPath, fs);
84
+ if (text === null)
85
+ return "not-installed";
86
+ return arraysEqual(extractNotify(text), buildNotifyCommand(opts.notifyPath))
87
+ ? "installed"
88
+ : "not-installed";
89
+ }
90
+ function buildNotifyCommand(notifyPath) {
91
+ return ["/usr/bin/env", "node", notifyPath, "--source=codex"];
92
+ }
93
+ async function readOptional(filePath, fs) {
94
+ try {
95
+ return await fs.readFile(filePath, "utf8");
96
+ }
97
+ catch (err) {
98
+ if (err?.code === "ENOENT")
99
+ return null;
100
+ throw err;
101
+ }
102
+ }
103
+ function extractNotify(text) {
104
+ const lines = text.split(/\r?\n/);
105
+ for (let i = 0; i < lines.length; i++) {
106
+ const match = lines[i].match(/^\s*notify\s*=\s*(.*)\s*$/);
107
+ if (!match)
108
+ continue;
109
+ const rhs = (match[1] ?? "").trim();
110
+ const literal = readTomlArrayLiteral(lines, i, rhs);
111
+ if (!literal)
112
+ continue;
113
+ return parseTomlStringArray(literal);
114
+ }
115
+ return null;
116
+ }
117
+ function setNotify(text, notify) {
118
+ const lines = text.split(/\r?\n/);
119
+ const replacement = `notify = ${formatTomlStringArray(notify)}`;
120
+ const out = [];
121
+ let replaced = false;
122
+ for (let i = 0; i < lines.length; i++) {
123
+ const match = lines[i].match(/^\s*notify\s*=\s*(.*)\s*$/);
124
+ if (!match) {
125
+ out.push(lines[i]);
126
+ continue;
127
+ }
128
+ if (!replaced) {
129
+ out.push(replacement);
130
+ replaced = true;
131
+ }
132
+ i = findTomlArrayBlockEnd(lines, i, (match[1] ?? "").trim());
133
+ }
134
+ if (!replaced) {
135
+ const firstTableIndex = out.findIndex((line) => /^\s*\[/.test(line));
136
+ const insertAt = firstTableIndex === -1 ? out.length : firstTableIndex;
137
+ out.splice(insertAt, 0, replacement);
138
+ }
139
+ return `${out.join("\n").replace(/\n+$/, "")}\n`;
140
+ }
141
+ function removeNotify(text) {
142
+ const lines = text.split(/\r?\n/);
143
+ const out = [];
144
+ for (let i = 0; i < lines.length; i++) {
145
+ const match = lines[i].match(/^\s*notify\s*=\s*(.*)\s*$/);
146
+ if (!match) {
147
+ out.push(lines[i]);
148
+ continue;
149
+ }
150
+ i = findTomlArrayBlockEnd(lines, i, (match[1] ?? "").trim());
151
+ }
152
+ return `${out.join("\n").replace(/\n+$/, "")}\n`;
153
+ }
154
+ function parseTomlStringArray(text) {
155
+ if (!text.startsWith("[") || !text.endsWith("]"))
156
+ return null;
157
+ const inner = text.slice(1, -1).trim();
158
+ if (!inner)
159
+ return [];
160
+ const parts = [];
161
+ let current = "";
162
+ let inString = false;
163
+ let quote = null;
164
+ for (let i = 0; i < inner.length; i++) {
165
+ const char = inner[i];
166
+ if (!inString) {
167
+ if (char === '"' || char === "'") {
168
+ inString = true;
169
+ quote = char;
170
+ current = "";
171
+ }
172
+ continue;
173
+ }
174
+ if (char === quote) {
175
+ parts.push(current);
176
+ inString = false;
177
+ quote = null;
178
+ continue;
179
+ }
180
+ current += char;
181
+ }
182
+ return parts.length > 0 ? parts : null;
183
+ }
184
+ function formatTomlStringArray(values) {
185
+ return `[${values.map((value) => JSON.stringify(value)).join(", ")}]`;
186
+ }
187
+ function readTomlArrayLiteral(lines, startIndex, rhs) {
188
+ if (!rhs.startsWith("["))
189
+ return null;
190
+ let depth = 0;
191
+ let inString = false;
192
+ let quote = null;
193
+ const chunks = [];
194
+ for (let i = startIndex; i < lines.length; i++) {
195
+ const chunk = i === startIndex ? rhs : lines[i];
196
+ chunks.push(chunk.trim());
197
+ for (let j = 0; j < chunk.length; j++) {
198
+ const char = chunk[j];
199
+ if (!inString) {
200
+ if (char === '"' || char === "'") {
201
+ inString = true;
202
+ quote = char;
203
+ continue;
204
+ }
205
+ if (char === "[")
206
+ depth += 1;
207
+ else if (char === "]")
208
+ depth -= 1;
209
+ continue;
210
+ }
211
+ if (char === quote) {
212
+ inString = false;
213
+ quote = null;
214
+ }
215
+ }
216
+ if (depth === 0)
217
+ return chunks.join(" ");
218
+ }
219
+ return null;
220
+ }
221
+ function findTomlArrayBlockEnd(lines, startIndex, rhs) {
222
+ if (!rhs.startsWith("["))
223
+ return startIndex;
224
+ let depth = 0;
225
+ let inString = false;
226
+ let quote = null;
227
+ for (let i = startIndex; i < lines.length; i++) {
228
+ const chunk = i === startIndex ? rhs : lines[i];
229
+ for (let j = 0; j < chunk.length; j++) {
230
+ const char = chunk[j];
231
+ if (!inString) {
232
+ if (char === '"' || char === "'") {
233
+ inString = true;
234
+ quote = char;
235
+ continue;
236
+ }
237
+ if (char === "[")
238
+ depth += 1;
239
+ else if (char === "]")
240
+ depth -= 1;
241
+ continue;
242
+ }
243
+ if (char === quote) {
244
+ inString = false;
245
+ quote = null;
246
+ }
247
+ }
248
+ if (depth === 0)
249
+ return i;
250
+ }
251
+ return startIndex;
252
+ }
253
+ function arraysEqual(left, right) {
254
+ if (!left || left.length !== right.length)
255
+ return false;
256
+ return left.every((value, index) => value === right[index]);
257
+ }
258
+ //# sourceMappingURL=codex-notifier.js.map