sidecarsync 1.0.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.
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "sidecarsync",
3
+ "version": "1.0.0",
4
+ "description": "A Git-backed sidecar inbox for repo-local development metadata.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/anteprojector/sidecar.git",
9
+ "directory": "packages/sidecar"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "sidecar": "dist/cli.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "scripts/postinstall.js",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "build": "rm -rf dist && bun build src/bin.ts --target=node --format=esm --external chokidar --outfile=dist/cli.js",
26
+ "check": "tsc --noEmit -p tsconfig.json",
27
+ "dev": "bun build src/bin.ts --target=node --format=esm --external chokidar --outfile=dist/cli.js --watch",
28
+ "postinstall": "node scripts/postinstall.js",
29
+ "prepack": "cp ../../README.md README.md && cp ../../LICENSE LICENSE && bun run build",
30
+ "prepublishOnly": "bun run check",
31
+ "test": "bun run build && vitest run"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.13.10",
38
+ "typescript": "^5.8.2",
39
+ "vitest": "^3.0.8"
40
+ },
41
+ "dependencies": {
42
+ "chokidar": "^4.0.3",
43
+ "smol-toml": "^1.7.0"
44
+ }
45
+ }
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const LABEL = "com.anteprojector.sidecar";
9
+ const BIN_NAME = process.platform === "win32" ? "sidecar.cmd" : "sidecar";
10
+
11
+ try {
12
+ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
13
+ if (isGlobalInstall()) {
14
+ enableDaemon(packageRoot);
15
+ recordInstallSource(packageRoot);
16
+ } else {
17
+ cloneIfMissing(packageRoot);
18
+ registerWithGlobalSidecar(packageRoot);
19
+ }
20
+ } catch (error) {
21
+ console.warn(`sidecar: postinstall failed: ${error instanceof Error ? error.message : String(error)}`);
22
+ }
23
+
24
+ function isGlobalInstall() {
25
+ if (process.env.npm_config_global === "true" || process.env.npm_config_global === "1") return true;
26
+
27
+ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
28
+ return isInside(realpath(packageRoot), realpath(bunGlobalNodeModules()));
29
+ }
30
+
31
+ function bunGlobalNodeModules() {
32
+ return path.join(process.env.BUN_INSTALL || path.join(os.homedir(), ".bun"), "install", "global", "node_modules");
33
+ }
34
+
35
+ function enableDaemon(packageRoot) {
36
+ const cliPath = path.join(packageRoot, "dist", "cli.js");
37
+ if (!fs.existsSync(cliPath)) {
38
+ console.warn(`sidecar: skipping daemon install; missing ${cliPath}`);
39
+ return;
40
+ }
41
+
42
+ const result = spawnSync(process.execPath, [cliPath, "daemon", "enable"], {
43
+ encoding: "utf8",
44
+ });
45
+ if (result.status !== 0) {
46
+ console.warn(`sidecar: daemon enable failed: ${result.stderr.trim() || result.stdout.trim()}`);
47
+ return;
48
+ }
49
+ console.warn(result.stdout.trim() || `sidecar: enabled daemon ${LABEL}`);
50
+ }
51
+
52
+ // --if-unset keeps a source that owns the install (like the curl script's
53
+ // "curl") from being clobbered when its underlying npm install re-runs.
54
+ function recordInstallSource(packageRoot) {
55
+ const cliPath = path.join(packageRoot, "dist", "cli.js");
56
+ if (!fs.existsSync(cliPath)) return;
57
+
58
+ const usesBun = isInside(realpath(packageRoot), realpath(bunGlobalNodeModules()));
59
+ spawnSync(process.execPath, [cliPath, "set-install-source", usesBun ? "bun" : "npm", "--if-unset"], {
60
+ encoding: "utf8",
61
+ });
62
+ }
63
+
64
+ function cloneIfMissing(packageRoot) {
65
+ const projectRoot = findInstallProjectRoot();
66
+ if (!projectRoot || !fs.existsSync(path.join(projectRoot, ".sidecar"))) return;
67
+
68
+ const cliPath = path.join(packageRoot, "dist", "cli.js");
69
+ if (!fs.existsSync(cliPath)) return;
70
+
71
+ const result = spawnSync(process.execPath, [cliPath, "clone", "--if-missing"], {
72
+ cwd: projectRoot,
73
+ encoding: "utf8",
74
+ });
75
+ if (result.status !== 0) {
76
+ console.warn(`sidecar: clone failed: ${result.stderr.trim() || result.stdout.trim()}`);
77
+ return;
78
+ }
79
+ const output = result.stdout.trim();
80
+ if (output) console.warn(output);
81
+ }
82
+
83
+ function registerWithGlobalSidecar(packageRoot) {
84
+ const projectRoot = findInstallProjectRoot();
85
+ if (!projectRoot || !fs.existsSync(path.join(projectRoot, ".sidecar"))) return;
86
+
87
+ const globalSidecar = findGlobalSidecar(packageRoot);
88
+ if (!globalSidecar) return;
89
+
90
+ const result = spawnSync(globalSidecar, ["register-install"], {
91
+ cwd: projectRoot,
92
+ encoding: "utf8",
93
+ env: process.env,
94
+ });
95
+ if (result.status !== 0) {
96
+ console.warn(`sidecar: install registration failed: ${result.stderr.trim() || result.stdout.trim()}`);
97
+ return;
98
+ }
99
+ const output = result.stdout.trim();
100
+ if (output) console.warn(output);
101
+ }
102
+
103
+ function findInstallProjectRoot() {
104
+ const candidates = [
105
+ process.env.INIT_CWD,
106
+ process.env.npm_config_local_prefix,
107
+ process.env.PROJECT_CWD,
108
+ process.cwd(),
109
+ ].filter(Boolean);
110
+
111
+ for (const candidate of candidates) {
112
+ const root = findConfigRoot(candidate);
113
+ if (root) return root;
114
+ }
115
+ return undefined;
116
+ }
117
+
118
+ function findConfigRoot(start) {
119
+ let current = path.resolve(start);
120
+ while (true) {
121
+ if (fs.existsSync(path.join(current, ".sidecar"))) return current;
122
+ const parent = path.dirname(current);
123
+ if (parent === current) return undefined;
124
+ current = parent;
125
+ }
126
+ }
127
+
128
+ function findGlobalSidecar(packageRoot) {
129
+ const pathEntries = (process.env.PATH || "").split(path.delimiter).filter(Boolean);
130
+ for (const entry of pathEntries) {
131
+ const candidate = path.join(entry, BIN_NAME);
132
+ if (isUsableGlobalSidecar(candidate, packageRoot)) return candidate;
133
+ if (process.platform === "win32") {
134
+ const psCandidate = path.join(entry, "sidecar.ps1");
135
+ if (isUsableGlobalSidecar(psCandidate, packageRoot)) return psCandidate;
136
+ }
137
+ }
138
+ return undefined;
139
+ }
140
+
141
+ function isUsableGlobalSidecar(candidate, packageRoot) {
142
+ if (!isFile(candidate)) return false;
143
+ const realCandidate = realpath(candidate);
144
+ const realPackageRoot = realpath(packageRoot);
145
+ if (realCandidate === path.join(realPackageRoot, "dist", "cli.js")) return false;
146
+ if (isInside(realCandidate, realPackageRoot)) return false;
147
+ return true;
148
+ }
149
+
150
+ function isFile(filePath) {
151
+ try {
152
+ return fs.statSync(filePath).isFile();
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
157
+
158
+ function realpath(filePath) {
159
+ try {
160
+ return fs.realpathSync(filePath);
161
+ } catch {
162
+ return path.resolve(filePath);
163
+ }
164
+ }
165
+
166
+ function isInside(child, parent) {
167
+ const relative = path.relative(parent, child);
168
+ return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
169
+ }