taskchef 9.4.0 → 9.6.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.
@@ -0,0 +1,47 @@
1
+ import { existsSync, lstatSync, realpathSync, readdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ export const dispatcherFiles = ["tasks.jsonl", "taskchef.json", ".taskchef-usage.json"];
6
+ const nativeEntries = ["settings.json", "done.json", "github-auth", "cache", "notifications.json", "notifications.json.errors.json", "delegation-memory.md", "usage-cache", "pricing.json"];
7
+
8
+ export function isNativeStateDir(directory) {
9
+ const entries = readdirSync(directory);
10
+ return !hasDispatcherState(directory) && (!entries.length || entries.some(name => nativeEntries.includes(name)));
11
+ }
12
+
13
+ export function taskChefStatePaths(home = homedir()) {
14
+ const agents = join(home, ".agents");
15
+ return { agents, current: join(agents, "taskchef"), previous: join(agents, "taskchef-next"), archive: join(agents, "taskchef-old"), registry: join(home, ".codex", "taskchef"), lock: join(agents, ".taskchef-storage-migration") };
16
+ }
17
+
18
+ export function hasDispatcherState(directory) {
19
+ return dispatcherFiles.some(name => existsSync(join(directory, name)));
20
+ }
21
+
22
+ // Resolve paths only. Moving user data requires the explicit offline migration.
23
+ export function taskChefStateDir(home = homedir()) {
24
+ const paths = taskChefStatePaths(home);
25
+ const fail = () => { throw new Error("TaskChef storage needs migration. Quit Codex and other TaskChef clients, then run node scripts/migrate-taskchef-state.js from the installed TaskChef package. See docs/taskchef-next-storage.md. No state was moved."); };
26
+ if (existsSync(paths.lock)) fail();
27
+ const retired = hasDispatcherState(paths.current);
28
+ let previousDirectory = false;
29
+ for (const directory of [paths.current, paths.previous]) {
30
+ let info;
31
+ try { info = lstatSync(directory); } catch (error) { if (error.code === "ENOENT") continue; throw error; }
32
+ if (directory === paths.previous && info.isSymbolicLink()) {
33
+ try { if (realpathSync(directory) === realpathSync(paths.current)) continue; } catch { /* Fail closed for dangling links. */ }
34
+ }
35
+ if (!info.isDirectory() || info.isSymbolicLink()) fail();
36
+ if (directory === paths.previous) previousDirectory = true;
37
+ }
38
+ if (previousDirectory) {
39
+ // Upgraded packages remain usable before the offline move. Never read the
40
+ // retired dispatcher directory as native state, and never choose between
41
+ // two independent native stores.
42
+ if ((!retired && existsSync(paths.current)) || !isNativeStateDir(paths.previous)) fail();
43
+ return paths.previous;
44
+ }
45
+ if (retired) fail();
46
+ return paths.current;
47
+ }