@gtc6244/immediacy-daemon 0.1.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 (133) hide show
  1. package/dist/src/actions.d.ts +22 -0
  2. package/dist/src/actions.js +44 -0
  3. package/dist/src/actions.js.map +1 -0
  4. package/dist/src/agent/agent.d.ts +89 -0
  5. package/dist/src/agent/agent.js +22 -0
  6. package/dist/src/agent/agent.js.map +1 -0
  7. package/dist/src/agent/claude.d.ts +70 -0
  8. package/dist/src/agent/claude.js +404 -0
  9. package/dist/src/agent/claude.js.map +1 -0
  10. package/dist/src/agent/codex.d.ts +44 -0
  11. package/dist/src/agent/codex.js +303 -0
  12. package/dist/src/agent/codex.js.map +1 -0
  13. package/dist/src/agent/mock.d.ts +31 -0
  14. package/dist/src/agent/mock.js +155 -0
  15. package/dist/src/agent/mock.js.map +1 -0
  16. package/dist/src/agent/model-error.d.ts +8 -0
  17. package/dist/src/agent/model-error.js +25 -0
  18. package/dist/src/agent/model-error.js.map +1 -0
  19. package/dist/src/agent/openrouter.d.ts +38 -0
  20. package/dist/src/agent/openrouter.js +60 -0
  21. package/dist/src/agent/openrouter.js.map +1 -0
  22. package/dist/src/agent/workspace-scope.d.ts +25 -0
  23. package/dist/src/agent/workspace-scope.js +71 -0
  24. package/dist/src/agent/workspace-scope.js.map +1 -0
  25. package/dist/src/allowlist.d.ts +56 -0
  26. package/dist/src/allowlist.js +141 -0
  27. package/dist/src/allowlist.js.map +1 -0
  28. package/dist/src/chatlog.d.ts +50 -0
  29. package/dist/src/chatlog.js +179 -0
  30. package/dist/src/chatlog.js.map +1 -0
  31. package/dist/src/cli/paths.d.ts +40 -0
  32. package/dist/src/cli/paths.js +76 -0
  33. package/dist/src/cli/paths.js.map +1 -0
  34. package/dist/src/cli/service.d.ts +20 -0
  35. package/dist/src/cli/service.js +245 -0
  36. package/dist/src/cli/service.js.map +1 -0
  37. package/dist/src/cli.d.ts +2 -0
  38. package/dist/src/cli.js +227 -0
  39. package/dist/src/cli.js.map +1 -0
  40. package/dist/src/config.d.ts +33 -0
  41. package/dist/src/config.js +122 -0
  42. package/dist/src/config.js.map +1 -0
  43. package/dist/src/credentials.d.ts +38 -0
  44. package/dist/src/credentials.js +120 -0
  45. package/dist/src/credentials.js.map +1 -0
  46. package/dist/src/crypto.d.ts +49 -0
  47. package/dist/src/crypto.js +202 -0
  48. package/dist/src/crypto.js.map +1 -0
  49. package/dist/src/daemon-identity.d.ts +31 -0
  50. package/dist/src/daemon-identity.js +97 -0
  51. package/dist/src/daemon-identity.js.map +1 -0
  52. package/dist/src/envelope.d.ts +14 -0
  53. package/dist/src/envelope.js +28 -0
  54. package/dist/src/envelope.js.map +1 -0
  55. package/dist/src/events.d.ts +40 -0
  56. package/dist/src/events.js +24 -0
  57. package/dist/src/events.js.map +1 -0
  58. package/dist/src/forward.d.ts +51 -0
  59. package/dist/src/forward.js +170 -0
  60. package/dist/src/forward.js.map +1 -0
  61. package/dist/src/fs-scope.d.ts +17 -0
  62. package/dist/src/fs-scope.js +47 -0
  63. package/dist/src/fs-scope.js.map +1 -0
  64. package/dist/src/github.d.ts +70 -0
  65. package/dist/src/github.js +178 -0
  66. package/dist/src/github.js.map +1 -0
  67. package/dist/src/gitops.d.ts +113 -0
  68. package/dist/src/gitops.js +557 -0
  69. package/dist/src/gitops.js.map +1 -0
  70. package/dist/src/hub.d.ts +50 -0
  71. package/dist/src/hub.js +92 -0
  72. package/dist/src/hub.js.map +1 -0
  73. package/dist/src/index.d.ts +22 -0
  74. package/dist/src/index.js +56 -0
  75. package/dist/src/index.js.map +1 -0
  76. package/dist/src/jobs/scheduler.d.ts +94 -0
  77. package/dist/src/jobs/scheduler.js +373 -0
  78. package/dist/src/jobs/scheduler.js.map +1 -0
  79. package/dist/src/jobs/store.d.ts +33 -0
  80. package/dist/src/jobs/store.js +111 -0
  81. package/dist/src/jobs/store.js.map +1 -0
  82. package/dist/src/log.d.ts +61 -0
  83. package/dist/src/log.js +166 -0
  84. package/dist/src/log.js.map +1 -0
  85. package/dist/src/migrate-state-dir.d.ts +1 -0
  86. package/dist/src/migrate-state-dir.js +47 -0
  87. package/dist/src/migrate-state-dir.js.map +1 -0
  88. package/dist/src/models.d.ts +36 -0
  89. package/dist/src/models.js +71 -0
  90. package/dist/src/models.js.map +1 -0
  91. package/dist/src/permission-gate.d.ts +48 -0
  92. package/dist/src/permission-gate.js +93 -0
  93. package/dist/src/permission-gate.js.map +1 -0
  94. package/dist/src/permission-scope.d.ts +77 -0
  95. package/dist/src/permission-scope.js +244 -0
  96. package/dist/src/permission-scope.js.map +1 -0
  97. package/dist/src/ports.d.ts +71 -0
  98. package/dist/src/ports.js +216 -0
  99. package/dist/src/ports.js.map +1 -0
  100. package/dist/src/projects.d.ts +83 -0
  101. package/dist/src/projects.js +205 -0
  102. package/dist/src/projects.js.map +1 -0
  103. package/dist/src/protocol.d.ts +1226 -0
  104. package/dist/src/protocol.js +16 -0
  105. package/dist/src/protocol.js.map +1 -0
  106. package/dist/src/repo-sync.d.ts +49 -0
  107. package/dist/src/repo-sync.js +83 -0
  108. package/dist/src/repo-sync.js.map +1 -0
  109. package/dist/src/server.d.ts +33 -0
  110. package/dist/src/server.js +973 -0
  111. package/dist/src/server.js.map +1 -0
  112. package/dist/src/terminal.d.ts +32 -0
  113. package/dist/src/terminal.js +92 -0
  114. package/dist/src/terminal.js.map +1 -0
  115. package/dist/src/titler.d.ts +15 -0
  116. package/dist/src/titler.js +72 -0
  117. package/dist/src/titler.js.map +1 -0
  118. package/dist/src/types.d.ts +99 -0
  119. package/dist/src/types.js +3 -0
  120. package/dist/src/types.js.map +1 -0
  121. package/dist/src/usage-limit.d.ts +49 -0
  122. package/dist/src/usage-limit.js +83 -0
  123. package/dist/src/usage-limit.js.map +1 -0
  124. package/dist/src/usage.d.ts +23 -0
  125. package/dist/src/usage.js +63 -0
  126. package/dist/src/usage.js.map +1 -0
  127. package/dist/src/workspace.d.ts +303 -0
  128. package/dist/src/workspace.js +1086 -0
  129. package/dist/src/workspace.js.map +1 -0
  130. package/dist/src/worktree.d.ts +133 -0
  131. package/dist/src/worktree.js +561 -0
  132. package/dist/src/worktree.js.map +1 -0
  133. package/package.json +67 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Worktree-owned port discovery.
3
+ *
4
+ * When an agent runs `npm run dev` (or anything that binds a TCP port) inside
5
+ * its worktree, we want to surface that port to the operator so they can preview
6
+ * it in a local browser via a forwarded stream (see `forward.ts`).
7
+ *
8
+ * The authoritative signal is the OS, not the dev server's banner text: we ask
9
+ * `lsof` which processes are LISTENing on TCP, then correlate each listening
10
+ * process to a worktree by its working directory (`cwd`). A process whose `cwd`
11
+ * resolves inside a worktree path is attributed to that workspace — this catches
12
+ * servers launched in the interactive PTY and by the agent alike, and (because
13
+ * `cwd` survives it) a `npm run dev &` that gets reparented to launchd.
14
+ *
15
+ * The same attribution doubles as the security boundary: `forward.open` only
16
+ * dials ports that appear here, so a client cannot reach arbitrary loopback
17
+ * ports on the daemon box (the daemon's own WSS port, SSH, etc. never resolve to
18
+ * a worktree cwd, so they never appear).
19
+ */
20
+ import { execFile } from "node:child_process";
21
+ import { realpathSync } from "node:fs";
22
+ import { logger } from "./log.js";
23
+ const SCAN_MS = Math.max(1000, Number(process.env.FORWARD_SCAN_MS) || 4000);
24
+ /** Hard cap on a single lsof invocation (independent of the scan interval). */
25
+ const LSOF_TIMEOUT_MS = 4000;
26
+ /**
27
+ * Run lsof and return stdout even on a non-zero exit. lsof exits 1 when it can't
28
+ * stat *some* of the entries it found (a process we don't own, a socket that
29
+ * just closed) while still printing the rest — that partial output is exactly
30
+ * what we want, so a non-zero exit is not treated as failure.
31
+ */
32
+ function runLsof(args) {
33
+ return new Promise((resolve) => {
34
+ execFile("lsof", args, { timeout: LSOF_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024 }, (err, stdout) => {
35
+ if (err && !stdout) {
36
+ // lsof missing, timed out, or produced nothing — degrade to "no ports".
37
+ logger.debug?.("forward", "lsof failed", {
38
+ data: { message: err instanceof Error ? err.message : String(err) },
39
+ });
40
+ }
41
+ resolve(stdout ?? "");
42
+ });
43
+ });
44
+ }
45
+ /** Parse `host:port` (incl. IPv6 `[::1]:port` / `*:port`) → {addr, port}. */
46
+ export function splitHostPort(name) {
47
+ const colon = name.lastIndexOf(":");
48
+ if (colon < 0)
49
+ return null;
50
+ const port = Number(name.slice(colon + 1));
51
+ if (!Number.isInteger(port) || port <= 0 || port > 65535)
52
+ return null;
53
+ let addr = name.slice(0, colon);
54
+ if (addr.startsWith("[") && addr.endsWith("]"))
55
+ addr = addr.slice(1, -1);
56
+ return { addr, port };
57
+ }
58
+ /**
59
+ * All processes LISTENing on TCP, as {pid, port, addr}. `-n`/`-P` keep hosts and
60
+ * ports numeric; `-Fpn` gives machine-readable field output (`p<pid>` at the
61
+ * process level, `n<addr:port>` per socket).
62
+ */
63
+ async function listeningSockets() {
64
+ const out = await runLsof(["-nP", "-iTCP", "-sTCP:LISTEN", "-Fpn"]);
65
+ const sockets = [];
66
+ let pid = 0;
67
+ for (const line of out.split("\n")) {
68
+ if (line.length < 2)
69
+ continue;
70
+ const tag = line[0];
71
+ const val = line.slice(1);
72
+ if (tag === "p") {
73
+ pid = Number(val) || 0;
74
+ }
75
+ else if (tag === "n" && pid) {
76
+ const hp = splitHostPort(val);
77
+ if (hp)
78
+ sockets.push({ pid, port: hp.port, addr: hp.addr });
79
+ }
80
+ }
81
+ return sockets;
82
+ }
83
+ /** cwd for each pid: `lsof -a -p <csv> -d cwd -Fpn` → pid→directory. */
84
+ async function cwdOf(pids) {
85
+ const map = new Map();
86
+ if (pids.length === 0)
87
+ return map;
88
+ const out = await runLsof(["-a", "-p", pids.join(","), "-d", "cwd", "-Fpn"]);
89
+ let pid = 0;
90
+ for (const line of out.split("\n")) {
91
+ if (line.length < 2)
92
+ continue;
93
+ const tag = line[0];
94
+ const val = line.slice(1);
95
+ if (tag === "p")
96
+ pid = Number(val) || 0;
97
+ else if (tag === "n" && pid)
98
+ map.set(pid, val);
99
+ }
100
+ return map;
101
+ }
102
+ /** True when `cwd` is `root` itself or nested inside it. */
103
+ function underPath(cwd, root) {
104
+ if (!cwd || !root)
105
+ return false;
106
+ const r = root.endsWith("/") ? root.slice(0, -1) : root;
107
+ return cwd === r || cwd.startsWith(r + "/");
108
+ }
109
+ /**
110
+ * One discovery pass: which worktree-owned ports are listening right now.
111
+ * Deduped by (ws, port); a port that binds both IPv4 and IPv6 collapses to one
112
+ * entry, preferring an explicit loopback address for display.
113
+ */
114
+ export async function scanOwnedPorts(worktrees) {
115
+ if (worktrees.length === 0)
116
+ return [];
117
+ const sockets = await listeningSockets();
118
+ if (sockets.length === 0)
119
+ return [];
120
+ const cwds = await cwdOf([...new Set(sockets.map((s) => s.pid))]);
121
+ // Canonicalize worktree roots: lsof reports a process's cwd as its real path
122
+ // (e.g. macOS resolves /var → /private/var, /tmp → /private/tmp), so we must
123
+ // compare against the resolved root or a legitimately-owned port never matches.
124
+ const roots = worktrees.map((w) => {
125
+ try {
126
+ return { ws: w.ws, root: realpathSync(w.worktreePath) };
127
+ }
128
+ catch {
129
+ return { ws: w.ws, root: w.worktreePath };
130
+ }
131
+ });
132
+ const byKey = new Map();
133
+ for (const s of sockets) {
134
+ const cwd = cwds.get(s.pid);
135
+ if (!cwd)
136
+ continue;
137
+ const wt = roots.find((w) => underPath(cwd, w.root));
138
+ if (!wt)
139
+ continue;
140
+ const key = `${wt.ws}:${s.port}`;
141
+ const existing = byKey.get(key);
142
+ // Prefer a concrete loopback bind for the displayed addr.
143
+ if (!existing || (existing.addr !== "127.0.0.1" && s.addr === "127.0.0.1")) {
144
+ byKey.set(key, { ws: wt.ws, port: s.port, addr: s.addr });
145
+ }
146
+ }
147
+ return [...byKey.values()].sort((a, b) => a.port - b.port);
148
+ }
149
+ /**
150
+ * Polls `scanOwnedPorts` on an interval and fires `onChange` only when the set
151
+ * actually moves (a new server came up / one went away), so clients get an
152
+ * event on transitions rather than a steady heartbeat. Start it when at least
153
+ * one client is attached; stop it when the last one leaves.
154
+ */
155
+ export class PortScanner {
156
+ worktrees;
157
+ onChange;
158
+ timer = null;
159
+ last = "";
160
+ scanning = false;
161
+ /** A refresh() that landed during an in-flight scan; honored before tick ends. */
162
+ rescanRequested = false;
163
+ constructor(worktrees, onChange) {
164
+ this.worktrees = worktrees;
165
+ this.onChange = onChange;
166
+ }
167
+ /** The most recent scan result (used to answer `forward.list` immediately). */
168
+ current = [];
169
+ start() {
170
+ if (this.timer)
171
+ return;
172
+ void this.tick();
173
+ this.timer = setInterval(() => void this.tick(), SCAN_MS);
174
+ this.timer.unref?.();
175
+ }
176
+ stop() {
177
+ if (this.timer)
178
+ clearInterval(this.timer);
179
+ this.timer = null;
180
+ }
181
+ /**
182
+ * Force an out-of-band scan (e.g. a client just attached, or an agent turn
183
+ * finished). Guaranteed to yield a scan that started after this call: if one
184
+ * is already running, `rescanRequested` makes the in-flight loop run again
185
+ * rather than dropping the request.
186
+ */
187
+ async refresh() {
188
+ this.rescanRequested = true;
189
+ await this.tick();
190
+ }
191
+ async tick() {
192
+ if (this.scanning)
193
+ return; // a scan owns the loop; rescanRequested re-runs it
194
+ this.scanning = true;
195
+ try {
196
+ do {
197
+ this.rescanRequested = false;
198
+ const ports = await scanOwnedPorts(this.worktrees());
199
+ const sig = ports.map((p) => `${p.ws}:${p.port}`).join(",");
200
+ this.current = ports;
201
+ if (sig !== this.last) {
202
+ this.last = sig;
203
+ this.onChange(ports);
204
+ }
205
+ } while (this.rescanRequested);
206
+ }
207
+ finally {
208
+ this.scanning = false;
209
+ }
210
+ }
211
+ /** Is `port` currently owned by `ws`? The forward-open security check. */
212
+ isOwned(ws, port) {
213
+ return this.current.some((p) => p.ws === ws && p.port === port);
214
+ }
215
+ }
216
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/ports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ProjectStore — the registry of projects (one repo each) that worktrees hang
3
+ * under. First-class and persisted (projects.json) so a project stays a stable
4
+ * "+ new worktree here" target even with zero live worktrees.
5
+ *
6
+ * Where a project lives on disk is inherent, never chosen by a client: blank
7
+ * and GitHub projects are materialized under the daemon-managed projects root
8
+ * (a path derived from name + id), and an adopted local repo stays wherever it
9
+ * already is. A project can therefore start from nothing — no existing repo,
10
+ * local or remote — via a fresh `git init` (see worktree.ts `initRepo`).
11
+ */
12
+ import type { ProjectRecord } from "./types.js";
13
+ /** Managed root where blank + GitHub projects live; override with PROJECTS_ROOT. */
14
+ export declare const PROJECTS_ROOT: string;
15
+ /**
16
+ * How to populate a project's repo. Exactly one intent is expected:
17
+ * - `blank` — `git init` a brand-new repo (no existing repo anywhere);
18
+ * - `github` — clone a GitHub repo into the managed root;
19
+ * - `repo` — adopt an existing local git repo in place.
20
+ */
21
+ export interface ProjectSource {
22
+ blank?: boolean;
23
+ repo?: string;
24
+ github?: {
25
+ fullName: string;
26
+ defaultBranch?: string;
27
+ };
28
+ name?: string;
29
+ }
30
+ export declare class ProjectStore {
31
+ private projects;
32
+ load(): Promise<void>;
33
+ /**
34
+ * One-time cleanup: projects created before names were humanised stored the
35
+ * "owner__repo" clone-dir basename as their name. Rewrite those to the bare
36
+ * repo name. Only touches names still equal to the clone-dir basename, so a
37
+ * name the user has since edited is left untouched. Returns whether anything
38
+ * changed.
39
+ */
40
+ private renameCachedClonesInPlace;
41
+ private persist;
42
+ list(): ProjectRecord[];
43
+ get(id: string): ProjectRecord | undefined;
44
+ /** The project whose repo resolves to the same absolute path, if any. */
45
+ findByRepo(repo: string): ProjectRecord | undefined;
46
+ /**
47
+ * The project a source already maps to, if any. GitHub sources de-dup by full
48
+ * name, adopted local repos by resolved path; a blank project never de-dups —
49
+ * each `git init` is a distinct new repo.
50
+ */
51
+ private findExisting;
52
+ /**
53
+ * Materialize a source into an on-disk repo path. GitHub and blank projects
54
+ * land in the managed root at a name+id-derived dir (never client-chosen); an
55
+ * adopted local repo stays where it already is and is validated to be a git
56
+ * repo. GitHub is cloned, blank is `git init`ed with an empty first commit.
57
+ */
58
+ private materializeRepo;
59
+ /** Managed dir name: a readable slug plus a short id so names never collide. */
60
+ private slugFor;
61
+ /**
62
+ * Default project name from its source: an explicit name wins, else the bare
63
+ * GitHub repo name (no `owner/` prefix), else an adopted repo's path
64
+ * basename, else "project" (a blank repo).
65
+ */
66
+ private defaultName;
67
+ /**
68
+ * Find-or-create the project for a source. GitHub/local sources are idempotent
69
+ * (see `findExisting`); a blank source always creates. The backbone of
70
+ * `project.create` and the legacy bare-repo workspace create path.
71
+ */
72
+ ensureForSource(src: ProjectSource): Promise<ProjectRecord>;
73
+ /** Remove a project. The caller guards that no worktrees still reference it. */
74
+ remove(id: string): Promise<void>;
75
+ /**
76
+ * Rewrite the stored project order. `list()` returns projects in Map insertion
77
+ * order, so we rebuild the Map in the requested order. Unknown ids are ignored;
78
+ * any known project absent from `ids` (e.g. one created after the client took
79
+ * its snapshot) is appended in its prior relative order so nothing is dropped.
80
+ * Persists only when the order actually changed.
81
+ */
82
+ reorder(ids: string[]): Promise<void>;
83
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * ProjectStore — the registry of projects (one repo each) that worktrees hang
3
+ * under. First-class and persisted (projects.json) so a project stays a stable
4
+ * "+ new worktree here" target even with zero live worktrees.
5
+ *
6
+ * Where a project lives on disk is inherent, never chosen by a client: blank
7
+ * and GitHub projects are materialized under the daemon-managed projects root
8
+ * (a path derived from name + id), and an adopted local repo stays wherever it
9
+ * already is. A project can therefore start from nothing — no existing repo,
10
+ * local or remote — via a fresh `git init` (see worktree.ts `initRepo`).
11
+ */
12
+ import { randomUUID } from "node:crypto";
13
+ import { existsSync } from "node:fs";
14
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
15
+ import { homedir } from "node:os";
16
+ import { basename, dirname, join, resolve, sep } from "node:path";
17
+ import { cloneInto, refreshRepo, REPO_CACHE } from "./github.js";
18
+ import { initRepo } from "./worktree.js";
19
+ const PROJECTS_FILE = process.env.PROJECTS_FILE ?? join(homedir(), ".immediacy", "projects.json");
20
+ /** Managed root where blank + GitHub projects live; override with PROJECTS_ROOT. */
21
+ export const PROJECTS_ROOT = process.env.PROJECTS_ROOT ?? join(homedir(), ".immediacy", "projects");
22
+ /** True when `repoPath` is a clone under the GitHub repo cache. */
23
+ function isCachedClone(repoPath) {
24
+ const cache = resolve(REPO_CACHE);
25
+ const p = resolve(repoPath);
26
+ return p === cache || p.startsWith(cache + sep);
27
+ }
28
+ /**
29
+ * The bare repo name for a cached clone. `cacheDirFor` maps "owner/repo" to
30
+ * "owner__repo" (the "/" is the only separator turned into "__", since repo and
31
+ * owner names are already `[A-Za-z0-9._-]`), so the last "__"-segment is the
32
+ * repo — e.g. "GTC6244__immediacy" → "immediacy".
33
+ */
34
+ function repoNameFromCacheDir(repoPath) {
35
+ const base = basename(repoPath);
36
+ return base.split("__").pop() || base;
37
+ }
38
+ export class ProjectStore {
39
+ projects = new Map();
40
+ async load() {
41
+ try {
42
+ const raw = await readFile(PROJECTS_FILE, "utf8");
43
+ const records = JSON.parse(raw);
44
+ for (const r of records)
45
+ this.projects.set(r.id, r);
46
+ console.log(`[projects] restored ${this.projects.size} project(s)`);
47
+ if (this.renameCachedClonesInPlace())
48
+ await this.persist();
49
+ }
50
+ catch {
51
+ /* no prior state */
52
+ }
53
+ }
54
+ /**
55
+ * One-time cleanup: projects created before names were humanised stored the
56
+ * "owner__repo" clone-dir basename as their name. Rewrite those to the bare
57
+ * repo name. Only touches names still equal to the clone-dir basename, so a
58
+ * name the user has since edited is left untouched. Returns whether anything
59
+ * changed.
60
+ */
61
+ renameCachedClonesInPlace() {
62
+ let changed = false;
63
+ for (const r of this.projects.values()) {
64
+ if (isCachedClone(r.repo) && r.name === basename(r.repo)) {
65
+ r.name = repoNameFromCacheDir(r.repo);
66
+ changed = true;
67
+ }
68
+ }
69
+ return changed;
70
+ }
71
+ async persist() {
72
+ await mkdir(dirname(PROJECTS_FILE), { recursive: true });
73
+ const records = [...this.projects.values()];
74
+ await writeFile(PROJECTS_FILE, JSON.stringify(records, null, 2), "utf8");
75
+ }
76
+ list() {
77
+ return [...this.projects.values()];
78
+ }
79
+ get(id) {
80
+ return this.projects.get(id);
81
+ }
82
+ /** The project whose repo resolves to the same absolute path, if any. */
83
+ findByRepo(repo) {
84
+ const key = resolve(repo);
85
+ return [...this.projects.values()].find((p) => resolve(p.repo) === key);
86
+ }
87
+ /**
88
+ * The project a source already maps to, if any. GitHub sources de-dup by full
89
+ * name, adopted local repos by resolved path; a blank project never de-dups —
90
+ * each `git init` is a distinct new repo.
91
+ */
92
+ findExisting(src) {
93
+ if (src.blank)
94
+ return undefined;
95
+ if (src.github?.fullName) {
96
+ return [...this.projects.values()].find((p) => p.github?.fullName === src.github.fullName);
97
+ }
98
+ if (src.repo)
99
+ return this.findByRepo(src.repo);
100
+ return undefined;
101
+ }
102
+ /**
103
+ * Materialize a source into an on-disk repo path. GitHub and blank projects
104
+ * land in the managed root at a name+id-derived dir (never client-chosen); an
105
+ * adopted local repo stays where it already is and is validated to be a git
106
+ * repo. GitHub is cloned, blank is `git init`ed with an empty first commit.
107
+ */
108
+ async materializeRepo(src, id, name) {
109
+ // Adopt an existing local repo in place — the one non-managed source.
110
+ if (src.repo && !src.blank && !src.github?.fullName) {
111
+ const repoPath = resolve(src.repo);
112
+ if (!existsSync(join(repoPath, ".git"))) {
113
+ throw new Error(`not a git repo: ${repoPath}`);
114
+ }
115
+ return repoPath;
116
+ }
117
+ const dest = join(PROJECTS_ROOT, this.slugFor(name, id));
118
+ if (src.github?.fullName) {
119
+ await cloneInto(src.github.fullName, dest);
120
+ return dest;
121
+ }
122
+ return initRepo(dest);
123
+ }
124
+ /** Managed dir name: a readable slug plus a short id so names never collide. */
125
+ slugFor(name, id) {
126
+ const slug = name.replace(/[^a-zA-Z0-9._-]/g, "-").toLowerCase().replace(/^-+|-+$/g, "") ||
127
+ "project";
128
+ return `${slug}-${id.slice(0, 8)}`;
129
+ }
130
+ /**
131
+ * Default project name from its source: an explicit name wins, else the bare
132
+ * GitHub repo name (no `owner/` prefix), else an adopted repo's path
133
+ * basename, else "project" (a blank repo).
134
+ */
135
+ defaultName(src) {
136
+ const explicit = src.name?.trim();
137
+ if (explicit)
138
+ return explicit;
139
+ if (src.github?.fullName) {
140
+ return src.github.fullName.split("/").pop() || src.github.fullName;
141
+ }
142
+ if (src.repo && !src.blank)
143
+ return basename(resolve(src.repo)) || "project";
144
+ return "project";
145
+ }
146
+ /**
147
+ * Find-or-create the project for a source. GitHub/local sources are idempotent
148
+ * (see `findExisting`); a blank source always creates. The backbone of
149
+ * `project.create` and the legacy bare-repo workspace create path.
150
+ */
151
+ async ensureForSource(src) {
152
+ const existing = this.findExisting(src);
153
+ if (existing) {
154
+ // Freshen a reused GitHub clone; blank/adopted repos have nothing to pull.
155
+ if (existing.github)
156
+ await refreshRepo(existing.repo);
157
+ return existing;
158
+ }
159
+ const id = randomUUID();
160
+ const name = this.defaultName(src);
161
+ const repo = await this.materializeRepo(src, id, name);
162
+ const record = {
163
+ id,
164
+ name,
165
+ repo,
166
+ github: src.github?.fullName
167
+ ? { fullName: src.github.fullName, defaultBranch: src.github.defaultBranch }
168
+ : undefined,
169
+ createdAt: Date.now(),
170
+ };
171
+ this.projects.set(record.id, record);
172
+ await this.persist();
173
+ return record;
174
+ }
175
+ /** Remove a project. The caller guards that no worktrees still reference it. */
176
+ async remove(id) {
177
+ if (this.projects.delete(id))
178
+ await this.persist();
179
+ }
180
+ /**
181
+ * Rewrite the stored project order. `list()` returns projects in Map insertion
182
+ * order, so we rebuild the Map in the requested order. Unknown ids are ignored;
183
+ * any known project absent from `ids` (e.g. one created after the client took
184
+ * its snapshot) is appended in its prior relative order so nothing is dropped.
185
+ * Persists only when the order actually changed.
186
+ */
187
+ async reorder(ids) {
188
+ const next = new Map();
189
+ for (const id of ids) {
190
+ const r = this.projects.get(id);
191
+ if (r && !next.has(id))
192
+ next.set(id, r);
193
+ }
194
+ for (const [id, r] of this.projects) {
195
+ if (!next.has(id))
196
+ next.set(id, r);
197
+ }
198
+ const prevOrder = [...this.projects.keys()];
199
+ const changed = [...next.keys()].some((id, i) => id !== prevOrder[i]);
200
+ this.projects = next;
201
+ if (changed)
202
+ await this.persist();
203
+ }
204
+ }
205
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/projects.ts"],"names":[],"mappings":"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"}