looply-cli 0.2.0 → 0.4.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 (52) hide show
  1. package/dist/.src-hash +1 -1
  2. package/dist/bin.d.ts.map +1 -1
  3. package/dist/bin.js +25 -11
  4. package/dist/bin.js.map +1 -1
  5. package/dist/connect-node.d.ts +8 -0
  6. package/dist/connect-node.d.ts.map +1 -0
  7. package/dist/connect-node.js +110 -0
  8. package/dist/connect-node.js.map +1 -0
  9. package/dist/connect.d.ts +135 -0
  10. package/dist/connect.d.ts.map +1 -0
  11. package/dist/connect.js +329 -0
  12. package/dist/connect.js.map +1 -0
  13. package/dist/daemon-cmd.d.ts +11 -2
  14. package/dist/daemon-cmd.d.ts.map +1 -1
  15. package/dist/daemon-cmd.js +20 -6
  16. package/dist/daemon-cmd.js.map +1 -1
  17. package/dist/repo-init.d.ts +36 -0
  18. package/dist/repo-init.d.ts.map +1 -0
  19. package/dist/repo-init.js +61 -0
  20. package/dist/repo-init.js.map +1 -0
  21. package/dist/repo-origin.d.ts +15 -0
  22. package/dist/repo-origin.d.ts.map +1 -0
  23. package/dist/repo-origin.js +28 -0
  24. package/dist/repo-origin.js.map +1 -0
  25. package/dist/repo-requests.d.ts +79 -0
  26. package/dist/repo-requests.d.ts.map +1 -0
  27. package/dist/repo-requests.js +180 -0
  28. package/dist/repo-requests.js.map +1 -0
  29. package/dist/repo-scan.d.ts +47 -0
  30. package/dist/repo-scan.d.ts.map +1 -0
  31. package/dist/repo-scan.js +84 -0
  32. package/dist/repo-scan.js.map +1 -0
  33. package/dist/serve-cmd.d.ts +132 -0
  34. package/dist/serve-cmd.d.ts.map +1 -0
  35. package/dist/serve-cmd.js +532 -0
  36. package/dist/serve-cmd.js.map +1 -0
  37. package/dist/serve-tasks.d.ts +17 -0
  38. package/dist/serve-tasks.d.ts.map +1 -0
  39. package/dist/serve-tasks.js +124 -0
  40. package/dist/serve-tasks.js.map +1 -0
  41. package/dist/serve.d.ts +202 -0
  42. package/dist/serve.d.ts.map +1 -0
  43. package/dist/serve.js +413 -0
  44. package/dist/serve.js.map +1 -0
  45. package/dist/server-client.d.ts +37 -0
  46. package/dist/server-client.d.ts.map +1 -1
  47. package/dist/server-client.js +21 -0
  48. package/dist/server-client.js.map +1 -1
  49. package/dist/sync.d.ts.map +1 -1
  50. package/dist/sync.js +3 -1
  51. package/dist/sync.js.map +1 -1
  52. package/package.json +54 -54
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLONE_TIMEOUT_MS = void 0;
4
+ exports.nodeSupervisorTasks = nodeSupervisorTasks;
5
+ const node_child_process_1 = require("node:child_process");
6
+ const node_fs_1 = require("node:fs");
7
+ const connect_1 = require("./connect");
8
+ const git_driver_1 = require("./git-driver");
9
+ const machine_store_1 = require("./machine-store");
10
+ const repo_origin_1 = require("./repo-origin");
11
+ const repo_init_1 = require("./repo-init");
12
+ const repo_requests_1 = require("./repo-requests");
13
+ const repo_scan_1 = require("./repo-scan");
14
+ const serve_1 = require("./serve");
15
+ const server_client_1 = require("./server-client");
16
+ const trusted_git_1 = require("./trusted-git");
17
+ /**
18
+ * The supervisor's two periodic jobs, wired to the real machine.
19
+ *
20
+ * rescan every five minutes, the recorded code roots are walked and
21
+ * any new repository is set up and served (`connect.ts`);
22
+ * clone requests every minute, the server is asked what this machine
23
+ * should clone (`repo-requests.ts`).
24
+ *
25
+ * The decisions live in those two files; this one only hands them a
26
+ * filesystem, git and the server.
27
+ */
28
+ /** A clone that has not finished in this long is killed and reported as failed. */
29
+ exports.CLONE_TIMEOUT_MS = 15 * 60_000;
30
+ /* istanbul ignore next — real fs, git and network; the logic lives in connect.ts and repo-requests.ts */
31
+ function nodeSupervisorTasks(home, platform, log) {
32
+ const provisionDeps = {
33
+ home,
34
+ platform,
35
+ fs: serve_1.nodeServeFs,
36
+ now: () => new Date(),
37
+ initRepo: (dir) => (0, repo_init_1.initRepoQuietly)(dir),
38
+ configProblem: (dir) => (0, repo_init_1.configProblemOf)(dir),
39
+ };
40
+ const rescanner = new connect_1.RootRescanner({ ...provisionDeps, scanFs: repo_scan_1.nodeScanFs, log });
41
+ const client = () => {
42
+ const identity = new machine_store_1.MachineStore((0, machine_store_1.machineFilePath)(home)).load();
43
+ if (identity === null)
44
+ return null;
45
+ return new server_client_1.ServerClient({ baseUrl: identity.serverUrl, token: identity.token });
46
+ };
47
+ const worker = new repo_requests_1.RepoRequestWorker({
48
+ platform,
49
+ list: async () => {
50
+ const c = client();
51
+ if (c === null)
52
+ return null;
53
+ return (await c.machineRepoRequests()).requests ?? [];
54
+ },
55
+ report: async (id, result) => {
56
+ const c = client();
57
+ if (c === null)
58
+ throw new Error('this machine is no longer paired');
59
+ await c.reportRepoRequest(id, result);
60
+ },
61
+ cloneRoot: () => (0, serve_1.cloneRootOf)((0, serve_1.readRegistry)(serve_1.nodeServeFs, (0, serve_1.servedFilePath)(home)), home, platform),
62
+ exists: (p) => (0, node_fs_1.existsSync)(p),
63
+ mkdirp: (p) => {
64
+ (0, node_fs_1.mkdirSync)(p, { recursive: true });
65
+ },
66
+ originOf: (dir) => (0, repo_origin_1.readOriginUrl)((0, git_driver_1.nodeGitExec)(dir)),
67
+ clone: nodeClone,
68
+ provision: (dir) => (0, connect_1.provisionRepos)([dir], provisionDeps)[0],
69
+ log,
70
+ });
71
+ return [
72
+ { name: 'rescan', everyMs: connect_1.RESCAN_EVERY_MS, run: () => rescanner.run() },
73
+ { name: 'clone requests', everyMs: repo_requests_1.REPO_REQUEST_POLL_MS, run: () => worker.run() },
74
+ ];
75
+ }
76
+ /**
77
+ * `git clone`, with the same trust the rest of the CLI gives git.
78
+ *
79
+ * ⚠️ NOT `nodeGitExec`, AND ON PURPOSE: that refuses to run anywhere that is
80
+ * not already a repository root, and a clone by definition runs where there is
81
+ * none yet. Everything else it does is kept — the absolute git binary (never
82
+ * one the cwd chooses), hooks pointed at an empty directory, signing off, the
83
+ * `GIT_` namespace rebuilt — with no `GIT_DIR` pin, because there is no
84
+ * repository to pin to.
85
+ *
86
+ * ⚠️ NO PROMPTS. `GIT_TERMINAL_PROMPT=0` comes from `trustedGitEnv`, and the
87
+ * credential manager's own window is turned off too: a supervisor at login has
88
+ * nobody to answer it, and a clone waiting on a hidden dialog would hold every
89
+ * later request behind it. A private repository needs credentials the machine
90
+ * already has.
91
+ */
92
+ /* istanbul ignore next — real git */
93
+ function nodeClone(url, target) {
94
+ return new Promise((resolve) => {
95
+ const bin = (0, trusted_git_1.gitBinary)();
96
+ if (bin === null) {
97
+ resolve({ ok: false, error: 'git was not found on PATH' });
98
+ return;
99
+ }
100
+ const env = { ...(0, trusted_git_1.trustedGitEnv)(process.env), GCM_INTERACTIVE: 'never' };
101
+ const child = (0, node_child_process_1.spawn)(bin, [...(0, trusted_git_1.trustedGitArgs)(), 'clone', '--quiet', '--', url, target], {
102
+ env,
103
+ stdio: ['ignore', 'ignore', 'pipe'],
104
+ windowsHide: true,
105
+ });
106
+ const err = [];
107
+ child.stderr.on('data', (d) => err.push(d.toString('utf8')));
108
+ const timer = setTimeout(() => child.kill('SIGKILL'), exports.CLONE_TIMEOUT_MS);
109
+ child.on('error', (e) => {
110
+ clearTimeout(timer);
111
+ resolve({ ok: false, error: e.message });
112
+ });
113
+ child.on('close', (code, signal) => {
114
+ clearTimeout(timer);
115
+ if (code === 0)
116
+ resolve({ ok: true });
117
+ else if (signal)
118
+ resolve({ ok: false, error: `git clone did not finish in ${exports.CLONE_TIMEOUT_MS / 60_000} minutes` });
119
+ else
120
+ resolve({ ok: false, error: err.join('').trim() || `git clone exited with code ${code}` });
121
+ });
122
+ });
123
+ }
124
+ //# sourceMappingURL=serve-tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-tasks.js","sourceRoot":"","sources":["../src/serve-tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,202 @@
1
+ import { type MachineFs } from './machine-store';
2
+ import type { ProbeFn } from './run-lease';
3
+ /**
4
+ * `looply serve` — ONE PROCESS FOR EVERY REPOSITORY ON THE MACHINE.
5
+ *
6
+ * A daemon serves the repository it was started in, and nothing else. A person
7
+ * with five repositories had five terminals open for ever, and the sixth one
8
+ * they forgot about simply went quiet. `serve` keeps a list of repositories in
9
+ * the Looply home and runs one ordinary `looply daemon --yes` per entry, as a
10
+ * child, restarting each one that falls over.
11
+ *
12
+ * ⚠️ IT IS A SUPERVISOR, NOT A SECOND DAEMON. Every child is the same daemon a
13
+ * person would start by hand — same per-repo lock, same registration, same
14
+ * server conversation — so there is exactly one implementation of "serve this
15
+ * repository" and this file only decides which ones are running.
16
+ *
17
+ * This module is the pure half: the registry, the backoff, the diff between
18
+ * what runs and what is listed, the supervisor lock and the startup file. The
19
+ * process wiring lives in `serve-cmd.ts`.
20
+ */
21
+ export declare const SERVED_FILE = "served.json";
22
+ export declare const SERVE_LOCK_FILE = "serve.lock";
23
+ export declare const SERVE_LOG_FILE = "serve.log";
24
+ export declare const STARTUP_FILE = "looply-serve.cmd";
25
+ /** The registry is re-read this often, so `serve add`/`remove` land without a restart. */
26
+ export declare const REGISTRY_POLL_MS = 30000;
27
+ export declare const BACKOFF_FIRST_MS = 2000;
28
+ export declare const BACKOFF_MAX_MS = 60000;
29
+ /** A child that stayed up this long has earned a fresh backoff. */
30
+ export declare const BACKOFF_RESET_AFTER_MS: number;
31
+ export type Platform = NodeJS.Platform;
32
+ export interface ServedRepo {
33
+ /** Absolute and normalised; see `normaliseRepoPath`. */
34
+ readonly path: string;
35
+ readonly addedAt: string;
36
+ }
37
+ /** A folder `looply connect` was told holds code; rescanned for new repositories. */
38
+ export interface ServedRoot {
39
+ readonly path: string;
40
+ readonly addedAt: string;
41
+ }
42
+ /**
43
+ * The served list.
44
+ *
45
+ * Version 2 adds what `looply connect` records — the code roots the supervisor
46
+ * rescans, where requested clones go, and the repositories a person took out
47
+ * of service under a root (so a rescan does not put them straight back).
48
+ * Version 1 files are read as version 2 with none of those; every write is
49
+ * version 2.
50
+ */
51
+ export interface ServedRegistry {
52
+ readonly version: 1 | 2;
53
+ readonly repos: readonly ServedRepo[];
54
+ readonly roots?: readonly ServedRoot[];
55
+ /** Where requested clones go; absent means the first root, then `~/looply-repos`. */
56
+ readonly cloneRoot?: string;
57
+ /**
58
+ * Removed with `serve remove` while under a root. A rescan skips these;
59
+ * `serve add` takes one off the list again.
60
+ */
61
+ readonly ignored?: readonly ServedRoot[];
62
+ }
63
+ export declare const EMPTY_REGISTRY: ServedRegistry;
64
+ /**
65
+ * The machine store's filesystem, plus the three things a supervisor needs that
66
+ * a credential file does not. `write` is the machine store's own temp-then-rename,
67
+ * so the registry is never seen half-written by a supervisor polling it.
68
+ */
69
+ export interface ServeFs extends MachineFs {
70
+ isDir(path: string): boolean;
71
+ remove(path: string): void;
72
+ /** Create only if absent. False when something is already there. */
73
+ createExclusive(path: string, text: string): boolean;
74
+ }
75
+ export declare const nodeServeFs: ServeFs;
76
+ export declare function servedFilePath(home?: string): string;
77
+ export declare function serveLockPath(home?: string): string;
78
+ export declare function serveLogPath(home?: string): string;
79
+ /**
80
+ * One spelling per folder.
81
+ *
82
+ * ⚠️ ON WINDOWS `C:/Code/App/`, `c:\code\app` AND `C:\Code\App` ARE ONE FOLDER.
83
+ * Stored three ways they would be three entries and three children, and two of
84
+ * them would lose the per-repo daemon lock to the first and restart for ever.
85
+ * So the stored path is resolved, backslashed, trailing-separator-free and has
86
+ * an upper-case drive letter; and comparison goes through `repoKey`, which also
87
+ * folds case, because the filesystem does.
88
+ */
89
+ export declare function normaliseRepoPath(input: string, cwd: string, platform: Platform): string;
90
+ export declare function repoKey(path: string, platform: Platform): string;
91
+ /** The folder name a child's output is prefixed with. */
92
+ export declare function repoLabel(path: string, platform: Platform): string;
93
+ export declare function configPathOf(repo: string, platform: Platform): string;
94
+ /**
95
+ * Read the registry. Absent is empty.
96
+ *
97
+ * 🔴 CORRUPT IS LOUD, NOT EMPTY. Reading an unparseable file as "nothing is
98
+ * served" would let the next `serve add` write a one-entry file over it, and
99
+ * every other repository the person listed would vanish without a word. The
100
+ * same rule `MachineStore.load` keeps for the identity.
101
+ */
102
+ export declare function readRegistry(fs: ServeFs, file: string): ServedRegistry;
103
+ /** Always written as version 2, whatever was read. */
104
+ export declare function writeRegistry(fs: ServeFs, file: string, registry: ServedRegistry): void;
105
+ export declare function addRepo(registry: ServedRegistry, path: string, addedAt: string, platform: Platform): {
106
+ registry: ServedRegistry;
107
+ added: boolean;
108
+ };
109
+ export declare function removeRepo(registry: ServedRegistry, path: string, platform: Platform): {
110
+ registry: ServedRegistry;
111
+ removed: boolean;
112
+ };
113
+ /** Whether `path` is one of the recorded roots or inside one. */
114
+ export declare function isUnderRoot(registry: ServedRegistry, path: string, platform: Platform): boolean;
115
+ /** Record code roots; ones already recorded are left as they are. */
116
+ export declare function addRoots(registry: ServedRegistry, paths: readonly string[], addedAt: string, platform: Platform): {
117
+ registry: ServedRegistry;
118
+ added: string[];
119
+ };
120
+ export declare function isIgnored(registry: ServedRegistry, path: string, platform: Platform): boolean;
121
+ /** Where a requested clone goes: the recorded clone root, else the first code root, else `~/looply-repos`. */
122
+ export declare function cloneRootOf(registry: ServedRegistry, home: string, platform: Platform): string;
123
+ /** 2s, 4s, 8s … capped at a minute. `failures` counts consecutive short lives. */
124
+ export declare function backoffDelay(failures: number): number;
125
+ /**
126
+ * The restart schedule for one child.
127
+ *
128
+ * ⚠️ RESET ON UPTIME, NOT ON EXIT CODE. A daemon that ran all night and then
129
+ * lost its network is not the same as one that dies on start, and a clean exit
130
+ * code says nothing about which it was. Five minutes up is the line.
131
+ */
132
+ export declare class Backoff {
133
+ private failures;
134
+ private startedAt;
135
+ started(now: number): void;
136
+ /** The child is gone (or could not start); how long to wait before the next try. */
137
+ exited(now: number): number;
138
+ }
139
+ export interface ReconcilePlan {
140
+ readonly start: readonly ServedRepo[];
141
+ /** Keys of running entries no longer listed. */
142
+ readonly stop: readonly string[];
143
+ readonly keep: readonly string[];
144
+ }
145
+ /** What to start, stop and leave alone, given what runs and what is listed. */
146
+ export declare function reconcile(running: Iterable<string>, wanted: readonly ServedRepo[], platform: Platform): ReconcilePlan;
147
+ /** Splits a stream into whole lines; the tail waits for the next chunk. */
148
+ export declare class LineSplitter {
149
+ private carry;
150
+ push(chunk: string): string[];
151
+ flush(): string[];
152
+ }
153
+ /**
154
+ * ONE SUPERVISOR PER MACHINE.
155
+ *
156
+ * Two supervisors would start two children per repository; the second of each
157
+ * pair loses the per-repo daemon lock, exits, and is restarted for ever. So the
158
+ * supervisor takes `~/.looply/serve.lock` first.
159
+ *
160
+ * ⚠️ STALE-TOLERANT BY PID. A supervisor killed at logoff leaves its lock
161
+ * behind, and a lock that outlived its process must not stop the next login
162
+ * from serving anything. A lock naming a pid that is not alive is taken over;
163
+ * an unreadable one is litter and taken over too. A pid that answers — or that
164
+ * cannot be judged (`unknown`: it exists and belongs to someone else) — is
165
+ * held, and the answer is a refusal.
166
+ */
167
+ export type ServeLockResult = {
168
+ readonly kind: 'taken';
169
+ readonly tookOverFrom?: number;
170
+ release(): void;
171
+ } | {
172
+ readonly kind: 'held';
173
+ readonly pid: number;
174
+ };
175
+ export declare function takeServeLock(fs: ServeFs, path: string, self: {
176
+ pid: number;
177
+ at: string;
178
+ }, probe: ProbeFn): ServeLockResult;
179
+ /** The pid of a supervisor holding the lock that is not known to be dead, or null when there is none. */
180
+ export declare function liveServeLockHolder(fs: ServeFs, path: string, probe: ProbeFn): number | null;
181
+ export declare function windowsStartupFolder(env: NodeJS.ProcessEnv): string | null;
182
+ export interface LaunchPaths {
183
+ /** Absolute path of the node executable (`process.execPath`). */
184
+ readonly node: string;
185
+ /** Absolute path of this CLI's own `bin.js`. */
186
+ readonly bin: string;
187
+ /** Where the supervisor's output is appended. */
188
+ readonly log: string;
189
+ }
190
+ /**
191
+ * The file `serve install` puts in the Startup folder.
192
+ *
193
+ * ⚠️ THE REDIRECT IS INSIDE THE `cmd /c`. Written after `start`, `>>` would
194
+ * capture `start`'s own (empty) output and the supervisor's would go nowhere.
195
+ * `cmd /c ""a" "b" >> "c""` strips the outermost pair of quotes and runs the
196
+ * rest, which keeps paths with spaces intact. And `cmd.exe` is named from
197
+ * `%SystemRoot%`, never barely — the same rule the code keeps for every spawn.
198
+ */
199
+ export declare function windowsStartupScript(p: LaunchPaths): string;
200
+ /** What a person on macOS or Linux puts in a launchd agent or a systemd user unit. */
201
+ export declare function unixServiceHint(p: LaunchPaths, platform: Platform): string;
202
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}