unfairly 0.8.3 → 0.8.4

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 (218) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/LICENSE +15 -0
  3. package/dist/auth/browser-flow.d.ts +0 -1
  4. package/dist/auth/browser-flow.js +5 -81
  5. package/dist/cmd/connect.d.ts +0 -1
  6. package/dist/cmd/connect.js +1 -164
  7. package/dist/cmd/init.d.ts +0 -1
  8. package/dist/cmd/init.js +2 -138
  9. package/dist/cmd/invite.d.ts +0 -1
  10. package/dist/cmd/invite.js +4 -73
  11. package/dist/cmd/join.d.ts +0 -1
  12. package/dist/cmd/join.js +1 -91
  13. package/dist/cmd/login.d.ts +0 -1
  14. package/dist/cmd/login.js +3 -41
  15. package/dist/cmd/mcp.d.ts +0 -1
  16. package/dist/cmd/mcp.js +2 -86
  17. package/dist/cmd/pace.d.ts +2 -2
  18. package/dist/cmd/pace.js +4 -426
  19. package/dist/cmd/parse-share-input.d.ts +0 -1
  20. package/dist/cmd/parse-share-input.js +1 -56
  21. package/dist/cmd/pull.d.ts +0 -1
  22. package/dist/cmd/pull.js +7 -143
  23. package/dist/cmd/push.d.ts +0 -1
  24. package/dist/cmd/push.js +1 -85
  25. package/dist/cmd/resolve.d.ts +0 -1
  26. package/dist/cmd/resolve.js +1 -35
  27. package/dist/cmd/setup.d.ts +0 -1
  28. package/dist/cmd/setup.js +2 -345
  29. package/dist/cmd/status-card.d.ts +0 -1
  30. package/dist/cmd/status-card.js +2 -156
  31. package/dist/cmd/status.d.ts +0 -1
  32. package/dist/cmd/status.js +3 -29
  33. package/dist/cmd/watch.d.ts +0 -1
  34. package/dist/cmd/watch.js +1 -26
  35. package/dist/hosts/claude-code.d.ts +0 -1
  36. package/dist/hosts/claude-code.js +3 -180
  37. package/dist/hosts/claude-desktop.d.ts +0 -1
  38. package/dist/hosts/claude-desktop.js +3 -222
  39. package/dist/hosts/codex.d.ts +0 -1
  40. package/dist/hosts/codex.js +20 -234
  41. package/dist/hosts/shell-env.d.ts +0 -1
  42. package/dist/hosts/shell-env.js +9 -81
  43. package/dist/index.d.ts +0 -1
  44. package/dist/index.js +14 -206
  45. package/dist/lib/client.d.ts +0 -1
  46. package/dist/lib/client.js +1 -58
  47. package/dist/lib/events/emit.d.ts +0 -1
  48. package/dist/lib/events/emit.js +1 -28
  49. package/dist/lib/index.d.ts +0 -1
  50. package/dist/lib/index.js +1 -11
  51. package/dist/lib/transport.d.ts +0 -1
  52. package/dist/lib/transport.js +1 -85
  53. package/dist/lib/types/index.d.ts +0 -1
  54. package/dist/lib/types/index.js +1 -50
  55. package/dist/lib/vault/checkpoint.d.ts +0 -1
  56. package/dist/lib/vault/checkpoint.js +1 -18
  57. package/dist/lib/vault/read.d.ts +0 -1
  58. package/dist/lib/vault/read.js +1 -9
  59. package/dist/lib/vault/search.d.ts +0 -1
  60. package/dist/lib/vault/search.js +1 -10
  61. package/dist/pace/device.d.ts +0 -1
  62. package/dist/pace/device.js +2 -149
  63. package/dist/pace/enroll.d.ts +0 -1
  64. package/dist/pace/enroll.js +2 -138
  65. package/dist/pace/identity.d.ts +0 -1
  66. package/dist/pace/identity.js +1 -17
  67. package/dist/pace/install.d.ts +0 -1
  68. package/dist/pace/install.js +4 -127
  69. package/dist/pace/local-evidence.d.ts +0 -1
  70. package/dist/pace/local-evidence.js +2 -228
  71. package/dist/pace/local-sessions.d.ts +0 -1
  72. package/dist/pace/local-sessions.js +1 -254
  73. package/dist/pace/managed-config.d.ts +0 -1
  74. package/dist/pace/managed-config.js +8 -102
  75. package/dist/pace/normalize.d.ts +0 -1
  76. package/dist/pace/normalize.js +1 -293
  77. package/dist/pace/repository.d.ts +0 -1
  78. package/dist/pace/repository.js +2 -120
  79. package/dist/pace/schema.d.ts +0 -1
  80. package/dist/pace/schema.js +1 -134
  81. package/dist/pace/session-summary.d.ts +0 -1
  82. package/dist/pace/session-summary.js +2 -140
  83. package/dist/pace/spool.d.ts +0 -1
  84. package/dist/pace/spool.js +8 -87
  85. package/dist/pace-runtime.d.ts +0 -1
  86. package/dist/pace-runtime.js +2 -67
  87. package/dist/sync/api.d.ts +0 -1
  88. package/dist/sync/api.js +1 -55
  89. package/dist/sync/autoloader.d.ts +0 -1
  90. package/dist/sync/autoloader.js +5 -127
  91. package/dist/sync/conflict.d.ts +0 -1
  92. package/dist/sync/conflict.js +5 -15
  93. package/dist/sync/credentials.d.ts +0 -1
  94. package/dist/sync/credentials.js +1 -51
  95. package/dist/sync/files.d.ts +0 -1
  96. package/dist/sync/files.js +1 -71
  97. package/dist/sync/manifest.d.ts +0 -1
  98. package/dist/sync/manifest.js +1 -37
  99. package/dist/sync/repo-root.d.ts +0 -1
  100. package/dist/sync/repo-root.js +1 -24
  101. package/dist/sync/stitch-claude-md.d.ts +0 -1
  102. package/dist/sync/stitch-claude-md.js +8 -48
  103. package/dist/ui/colors.d.ts +0 -1
  104. package/dist/ui/colors.js +1 -28
  105. package/dist/ui/screens.d.ts +0 -1
  106. package/dist/ui/screens.js +5 -135
  107. package/package.json +5 -12
  108. package/plugin/.cursor-plugin/plugin.json +0 -2
  109. package/plugin/.mcp.json +1 -1
  110. package/plugin/LICENSE +15 -0
  111. package/plugin/gemini/gemini-extension.json +1 -1
  112. package/plugin/mcp.json +1 -1
  113. package/plugin/plugin.json +0 -2
  114. package/plugin/runtime/unfairly-intelligence.mjs +14 -5512
  115. package/dist/auth/browser-flow.d.ts.map +0 -1
  116. package/dist/auth/browser-flow.js.map +0 -1
  117. package/dist/cmd/connect.d.ts.map +0 -1
  118. package/dist/cmd/connect.js.map +0 -1
  119. package/dist/cmd/init.d.ts.map +0 -1
  120. package/dist/cmd/init.js.map +0 -1
  121. package/dist/cmd/invite.d.ts.map +0 -1
  122. package/dist/cmd/invite.js.map +0 -1
  123. package/dist/cmd/join.d.ts.map +0 -1
  124. package/dist/cmd/join.js.map +0 -1
  125. package/dist/cmd/login.d.ts.map +0 -1
  126. package/dist/cmd/login.js.map +0 -1
  127. package/dist/cmd/mcp.d.ts.map +0 -1
  128. package/dist/cmd/mcp.js.map +0 -1
  129. package/dist/cmd/pace.d.ts.map +0 -1
  130. package/dist/cmd/pace.js.map +0 -1
  131. package/dist/cmd/parse-share-input.d.ts.map +0 -1
  132. package/dist/cmd/parse-share-input.js.map +0 -1
  133. package/dist/cmd/pull.d.ts.map +0 -1
  134. package/dist/cmd/pull.js.map +0 -1
  135. package/dist/cmd/push.d.ts.map +0 -1
  136. package/dist/cmd/push.js.map +0 -1
  137. package/dist/cmd/resolve.d.ts.map +0 -1
  138. package/dist/cmd/resolve.js.map +0 -1
  139. package/dist/cmd/setup.d.ts.map +0 -1
  140. package/dist/cmd/setup.js.map +0 -1
  141. package/dist/cmd/status-card.d.ts.map +0 -1
  142. package/dist/cmd/status-card.js.map +0 -1
  143. package/dist/cmd/status.d.ts.map +0 -1
  144. package/dist/cmd/status.js.map +0 -1
  145. package/dist/cmd/watch.d.ts.map +0 -1
  146. package/dist/cmd/watch.js.map +0 -1
  147. package/dist/hosts/claude-code.d.ts.map +0 -1
  148. package/dist/hosts/claude-code.js.map +0 -1
  149. package/dist/hosts/claude-desktop.d.ts.map +0 -1
  150. package/dist/hosts/claude-desktop.js.map +0 -1
  151. package/dist/hosts/codex.d.ts.map +0 -1
  152. package/dist/hosts/codex.js.map +0 -1
  153. package/dist/hosts/shell-env.d.ts.map +0 -1
  154. package/dist/hosts/shell-env.js.map +0 -1
  155. package/dist/index.d.ts.map +0 -1
  156. package/dist/index.js.map +0 -1
  157. package/dist/lib/client.d.ts.map +0 -1
  158. package/dist/lib/client.js.map +0 -1
  159. package/dist/lib/events/emit.d.ts.map +0 -1
  160. package/dist/lib/events/emit.js.map +0 -1
  161. package/dist/lib/index.d.ts.map +0 -1
  162. package/dist/lib/index.js.map +0 -1
  163. package/dist/lib/transport.d.ts.map +0 -1
  164. package/dist/lib/transport.js.map +0 -1
  165. package/dist/lib/types/index.d.ts.map +0 -1
  166. package/dist/lib/types/index.js.map +0 -1
  167. package/dist/lib/vault/checkpoint.d.ts.map +0 -1
  168. package/dist/lib/vault/checkpoint.js.map +0 -1
  169. package/dist/lib/vault/read.d.ts.map +0 -1
  170. package/dist/lib/vault/read.js.map +0 -1
  171. package/dist/lib/vault/search.d.ts.map +0 -1
  172. package/dist/lib/vault/search.js.map +0 -1
  173. package/dist/pace/device.d.ts.map +0 -1
  174. package/dist/pace/device.js.map +0 -1
  175. package/dist/pace/enroll.d.ts.map +0 -1
  176. package/dist/pace/enroll.js.map +0 -1
  177. package/dist/pace/identity.d.ts.map +0 -1
  178. package/dist/pace/identity.js.map +0 -1
  179. package/dist/pace/install.d.ts.map +0 -1
  180. package/dist/pace/install.js.map +0 -1
  181. package/dist/pace/local-evidence.d.ts.map +0 -1
  182. package/dist/pace/local-evidence.js.map +0 -1
  183. package/dist/pace/local-sessions.d.ts.map +0 -1
  184. package/dist/pace/local-sessions.js.map +0 -1
  185. package/dist/pace/managed-config.d.ts.map +0 -1
  186. package/dist/pace/managed-config.js.map +0 -1
  187. package/dist/pace/normalize.d.ts.map +0 -1
  188. package/dist/pace/normalize.js.map +0 -1
  189. package/dist/pace/repository.d.ts.map +0 -1
  190. package/dist/pace/repository.js.map +0 -1
  191. package/dist/pace/schema.d.ts.map +0 -1
  192. package/dist/pace/schema.js.map +0 -1
  193. package/dist/pace/session-summary.d.ts.map +0 -1
  194. package/dist/pace/session-summary.js.map +0 -1
  195. package/dist/pace/spool.d.ts.map +0 -1
  196. package/dist/pace/spool.js.map +0 -1
  197. package/dist/pace-runtime.d.ts.map +0 -1
  198. package/dist/pace-runtime.js.map +0 -1
  199. package/dist/sync/api.d.ts.map +0 -1
  200. package/dist/sync/api.js.map +0 -1
  201. package/dist/sync/autoloader.d.ts.map +0 -1
  202. package/dist/sync/autoloader.js.map +0 -1
  203. package/dist/sync/conflict.d.ts.map +0 -1
  204. package/dist/sync/conflict.js.map +0 -1
  205. package/dist/sync/credentials.d.ts.map +0 -1
  206. package/dist/sync/credentials.js.map +0 -1
  207. package/dist/sync/files.d.ts.map +0 -1
  208. package/dist/sync/files.js.map +0 -1
  209. package/dist/sync/manifest.d.ts.map +0 -1
  210. package/dist/sync/manifest.js.map +0 -1
  211. package/dist/sync/repo-root.d.ts.map +0 -1
  212. package/dist/sync/repo-root.js.map +0 -1
  213. package/dist/sync/stitch-claude-md.d.ts.map +0 -1
  214. package/dist/sync/stitch-claude-md.js.map +0 -1
  215. package/dist/ui/colors.d.ts.map +0 -1
  216. package/dist/ui/colors.js.map +0 -1
  217. package/dist/ui/screens.d.ts.map +0 -1
  218. package/dist/ui/screens.js.map +0 -1
@@ -1,56 +1 @@
1
- // cli/src/cmd/parse-share-input.ts
2
- //
3
- // Parse the positional arg to `unfairly connect`. Accepts:
4
- // - Full URL: https://app.unfairly.ai/s/<uuid>
5
- // - Short form: s/<uuid>
6
- // - Bare UUID: <uuid>
7
- //
8
- // Returns the share_link UUID + the origin (when present in the input)
9
- // so `connect` can POST to the same host the link came from. A bare UUID
10
- // or short form yields `origin: null` — callers should fall back to the
11
- // production app origin.
12
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
- export function parseShareInput(input) {
14
- if (typeof input !== "string") {
15
- throw new Error("share link is required");
16
- }
17
- const trimmed = input.trim();
18
- if (trimmed.length === 0) {
19
- throw new Error("share link is required");
20
- }
21
- // Full URL form: https://host/s/<uuid> (with optional trailing slash / query).
22
- if (/^https?:\/\//i.test(trimmed)) {
23
- let url;
24
- try {
25
- url = new URL(trimmed);
26
- }
27
- catch {
28
- throw new Error(`not a valid URL: ${trimmed}`);
29
- }
30
- const segments = url.pathname.split("/").filter(Boolean);
31
- // Expect [..., "s", "<uuid>"] — find the "s/" marker.
32
- const sIdx = segments.indexOf("s");
33
- if (sIdx === -1 || sIdx === segments.length - 1) {
34
- throw new Error(`share link URL must include /s/<id>: ${trimmed}`);
35
- }
36
- const candidate = segments[sIdx + 1] ?? "";
37
- if (!UUID_RE.test(candidate)) {
38
- throw new Error(`share id must be a UUID, got "${candidate}"`);
39
- }
40
- return { shareId: candidate.toLowerCase(), origin: url.origin };
41
- }
42
- // Short form: s/<uuid>
43
- if (/^s\//i.test(trimmed)) {
44
- const candidate = trimmed.slice(2);
45
- if (!UUID_RE.test(candidate)) {
46
- throw new Error(`share id must be a UUID, got "${candidate}"`);
47
- }
48
- return { shareId: candidate.toLowerCase(), origin: null };
49
- }
50
- // Bare UUID
51
- if (UUID_RE.test(trimmed)) {
52
- return { shareId: trimmed.toLowerCase(), origin: null };
53
- }
54
- throw new Error(`not a recognized share link: "${input}" — expected a URL like https://app.unfairly.ai/s/<uuid>, "s/<uuid>", or a bare UUID`);
55
- }
56
- //# sourceMappingURL=parse-share-input.js.map
1
+ const o=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function a(t){if(typeof t!="string")throw new Error("share link is required");const r=t.trim();if(r.length===0)throw new Error("share link is required");if(/^https?:\/\//i.test(r)){let e;try{e=new URL(r)}catch{throw new Error(`not a valid URL: ${r}`)}const i=e.pathname.split("/").filter(Boolean),n=i.indexOf("s");if(n===-1||n===i.length-1)throw new Error(`share link URL must include /s/<id>: ${r}`);const s=i[n+1]??"";if(!o.test(s))throw new Error(`share id must be a UUID, got "${s}"`);return{shareId:s.toLowerCase(),origin:e.origin}}if(/^s\//i.test(r)){const e=r.slice(2);if(!o.test(e))throw new Error(`share id must be a UUID, got "${e}"`);return{shareId:e.toLowerCase(),origin:null}}if(o.test(r))return{shareId:r.toLowerCase(),origin:null};throw new Error(`not a recognized share link: "${t}" \u2014 expected a URL like https://app.unfairly.ai/s/<uuid>, "s/<uuid>", or a bare UUID`)}export{a as parseShareInput};
@@ -1,2 +1 @@
1
1
  export declare function runPull(vaultArg?: string): Promise<void>;
2
- //# sourceMappingURL=pull.d.ts.map
package/dist/cmd/pull.js CHANGED
@@ -1,143 +1,7 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import { execFile } from "node:child_process";
4
- import { promisify } from "node:util";
5
- import { SyncClient } from "../sync/api.js";
6
- import { readManifest, writeManifest } from "../sync/manifest.js";
7
- import { buildFileFromServer, writeDocFile } from "../sync/files.js";
8
- import { getToken } from "../sync/credentials.js";
9
- import { writeAutoloader } from "../sync/autoloader.js";
10
- const execFileAsync = promisify(execFile);
11
- async function gitMv(from, to) {
12
- try {
13
- await execFileAsync("git", ["mv", from, to], { cwd: process.cwd() });
14
- return true;
15
- }
16
- catch {
17
- return false;
18
- }
19
- }
20
- async function tryUnlink(p) {
21
- try {
22
- await fs.unlink(p);
23
- }
24
- catch { }
25
- }
26
- export async function runPull(vaultArg) {
27
- const cwd = process.cwd();
28
- const manifest = await readManifest(cwd);
29
- if (!manifest) {
30
- console.error("No .unfairly/ found. Run `unfairly init` first.");
31
- process.exit(1);
32
- }
33
- const token = await getToken();
34
- if (!token) {
35
- console.error("No token. Run `unfairly init` to authenticate.");
36
- process.exit(1);
37
- }
38
- const client = new SyncClient(manifest.api_url, token);
39
- const targets = vaultArg ? [vaultArg] : Object.keys(manifest.vaults);
40
- const failures = [];
41
- for (const slug of targets) {
42
- const v = manifest.vaults[slug];
43
- if (!v) {
44
- console.error(`No vault \`${slug}\` in manifest.`);
45
- continue;
46
- }
47
- process.stdout.write(`pulling ${slug}... `);
48
- let result;
49
- try {
50
- result = await client.pullAll(slug, v.last_pulled_at);
51
- }
52
- catch (err) {
53
- // One vault failing shouldn't kill the rest of the pull. For a single-
54
- // vault target (`unfairly pull <vault>`) we still want to surface and
55
- // exit non-zero — fall through to throw at the end.
56
- const message = err.message.split("\n")[0];
57
- console.log(`FAILED — ${message}`);
58
- failures.push({ slug, message });
59
- continue;
60
- }
61
- // Vault root: track_in_git → .unfairly/{slug}/, else .unfairly/cache/{slug}/
62
- const vaultRoot = v.track_in_git ? slug : path.join("cache", slug);
63
- const absVaultRoot = path.join(cwd, ".unfairly", vaultRoot);
64
- await fs.mkdir(absVaultRoot, { recursive: true });
65
- let upserts = 0;
66
- let deletes = 0;
67
- let renames = 0;
68
- let skipped = 0;
69
- for (const doc of result.docs) {
70
- let file;
71
- try {
72
- file = buildFileFromServer(doc, vaultRoot);
73
- }
74
- catch (err) {
75
- // One bad doc shouldn't kill the whole pull. Most commonly this is
76
- // gray-matter/js-yaml choking on body content that the YAML
77
- // serializer mis-parses (e.g. `*Foo` interpreted as an alias). The
78
- // doc stays on the server; the user can re-pull after the upstream
79
- // doc is sanitized.
80
- process.stderr.write(` · skipped ${doc.slug} — ${err.message.split("\n")[0]}\n`);
81
- skipped++;
82
- continue;
83
- }
84
- const relPath = file.path;
85
- const absPath = path.join(cwd, ".unfairly", relPath);
86
- // Detect rename: same id, different stored path
87
- const prev = v.docs[doc.id];
88
- if (prev && prev.path !== relPath) {
89
- const prevAbs = path.join(cwd, ".unfairly", prev.path);
90
- // Tracked vault → use git mv to preserve history
91
- if (v.track_in_git) {
92
- await fs.mkdir(path.dirname(absPath), { recursive: true });
93
- const ok = await gitMv(prevAbs, absPath);
94
- if (!ok) {
95
- await tryUnlink(prevAbs);
96
- }
97
- }
98
- else {
99
- await tryUnlink(prevAbs);
100
- }
101
- renames++;
102
- }
103
- await writeDocFile(absPath, file.content);
104
- v.docs[doc.id] = {
105
- slug: doc.slug,
106
- path: relPath,
107
- sha: file.sha,
108
- updated_at: doc.updated_at,
109
- source_type: doc.source_type ?? "markdown",
110
- };
111
- upserts++;
112
- }
113
- for (const tomb of result.deleted) {
114
- const prev = v.docs[tomb.id];
115
- if (!prev)
116
- continue;
117
- const absPath = path.join(cwd, ".unfairly", prev.path);
118
- await tryUnlink(absPath);
119
- delete v.docs[tomb.id];
120
- deletes++;
121
- }
122
- v.last_pulled_at = new Date().toISOString();
123
- console.log(`+${upserts} -${deletes}${renames ? ` ~${renames}` : ""}${skipped ? ` (skipped ${skipped})` : ""}`);
124
- }
125
- await writeManifest(cwd, manifest);
126
- // Regenerate .unfairly/CLAUDE.md autoloader + .gitignore from the latest mirror.
127
- // (init prints the @-import paste instruction; pull stays quiet.)
128
- const refreshed = await readManifest(cwd);
129
- if (refreshed) {
130
- const target = path.join(cwd, ".unfairly");
131
- await writeAutoloader(target, refreshed);
132
- }
133
- if (failures.length > 0) {
134
- process.stderr.write(`\n${failures.length} vault${failures.length === 1 ? "" : "s"} failed:\n`);
135
- for (const f of failures) {
136
- process.stderr.write(` · ${f.slug} — ${f.message}\n`);
137
- }
138
- // Single-vault target → propagate so callers/CI see the failure.
139
- // Multi-vault → keep what we got but signal partial failure via exit code.
140
- process.exitCode = 1;
141
- }
142
- }
143
- //# sourceMappingURL=pull.js.map
1
+ import{promises as g}from"node:fs";import r from"node:path";import{execFile as A}from"node:child_process";import{promisify as P}from"node:util";import{SyncClient as R}from"../sync/api.js";import{readManifest as b,writeManifest as S}from"../sync/manifest.js";import{buildFileFromServer as D,writeDocFile as M}from"../sync/files.js";import{getToken as N}from"../sync/credentials.js";import{writeAutoloader as C}from"../sync/autoloader.js";const I=P(A);async function O(n,o){try{return await I("git",["mv",n,o],{cwd:process.cwd()}),!0}catch{return!1}}async function h(n){try{await g.unlink(n)}catch{}}async function J(n){const o=process.cwd(),a=await b(o);a||(console.error("No .unfairly/ found. Run `unfairly init` first."),process.exit(1));const y=await N();y||(console.error("No token. Run `unfairly init` to authenticate."),process.exit(1));const j=new R(a.api_url,y),x=n?[n]:Object.keys(a.vaults),c=[];for(const t of x){const i=a.vaults[t];if(!i){console.error(`No vault \`${t}\` in manifest.`);continue}process.stdout.write(`pulling ${t}... `);let f;try{f=await j.pullAll(t,i.last_pulled_at)}catch(e){const s=e.message.split(`
2
+ `)[0];console.log(`FAILED \u2014 ${s}`),c.push({slug:t,message:s});continue}const $=i.track_in_git?t:r.join("cache",t),F=r.join(o,".unfairly",$);await g.mkdir(F,{recursive:!0});let v=0,_=0,p=0,d=0;for(const e of f.docs){let s;try{s=D(e,$)}catch(u){process.stderr.write(` \xB7 skipped ${e.slug} \u2014 ${u.message.split(`
3
+ `)[0]}
4
+ `),d++;continue}const l=s.path,m=r.join(o,".unfairly",l),w=i.docs[e.id];if(w&&w.path!==l){const u=r.join(o,".unfairly",w.path);i.track_in_git?(await g.mkdir(r.dirname(m),{recursive:!0}),await O(u,m)||await h(u)):await h(u),p++}await M(m,s.content),i.docs[e.id]={slug:e.slug,path:l,sha:s.sha,updated_at:e.updated_at,source_type:e.source_type??"markdown"},v++}for(const e of f.deleted){const s=i.docs[e.id];if(!s)continue;const l=r.join(o,".unfairly",s.path);await h(l),delete i.docs[e.id],_++}i.last_pulled_at=new Date().toISOString(),console.log(`+${v} -${_}${p?` ~${p}`:""}${d?` (skipped ${d})`:""}`)}await S(o,a);const k=await b(o);if(k){const t=r.join(o,".unfairly");await C(t,k)}if(c.length>0){process.stderr.write(`
5
+ ${c.length} vault${c.length===1?"":"s"} failed:
6
+ `);for(const t of c)process.stderr.write(` \xB7 ${t.slug} \u2014 ${t.message}
7
+ `);process.exitCode=1}}export{J as runPull};
@@ -1,2 +1 @@
1
1
  export declare function runPush(vaultArg?: string): Promise<void>;
2
- //# sourceMappingURL=push.d.ts.map
package/dist/cmd/push.js CHANGED
@@ -1,85 +1 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import { SyncClient } from "../sync/api.js";
4
- import { readManifest, writeManifest } from "../sync/manifest.js";
5
- import { computeSha, readDocFile, readDocRaw } from "../sync/files.js";
6
- import { buildConflictFile } from "../sync/conflict.js";
7
- import { getToken } from "../sync/credentials.js";
8
- export async function runPush(vaultArg) {
9
- const cwd = process.cwd();
10
- const manifest = await readManifest(cwd);
11
- if (!manifest) {
12
- console.error("No .unfairly/ found. Run `unfairly init` first.");
13
- process.exit(1);
14
- }
15
- const token = await getToken();
16
- if (!token) {
17
- console.error("No token. Run `unfairly init` to authenticate.");
18
- process.exit(1);
19
- }
20
- const client = new SyncClient(manifest.api_url, token);
21
- const targets = vaultArg ? [vaultArg] : Object.keys(manifest.vaults);
22
- let conflicts = 0;
23
- let pushed = 0;
24
- for (const slug of targets) {
25
- const v = manifest.vaults[slug];
26
- if (!v) {
27
- console.error(`No vault \`${slug}\` in manifest.`);
28
- continue;
29
- }
30
- for (const [docId, entry] of Object.entries(v.docs)) {
31
- const absPath = path.join(cwd, ".unfairly", entry.path);
32
- let raw;
33
- let local;
34
- try {
35
- raw = await readDocRaw(absPath);
36
- local = await readDocFile(absPath);
37
- }
38
- catch {
39
- // File missing locally — Phase 1 punt: skip (delete-locally not handled)
40
- continue;
41
- }
42
- const currentSha = computeSha(raw);
43
- if (currentSha === entry.sha)
44
- continue; // not dirty
45
- const result = await client.push({
46
- vault: slug,
47
- doc_id: docId,
48
- expected_updated_at: entry.updated_at,
49
- body: local.body,
50
- client_frontmatter: local.frontmatter,
51
- });
52
- if (result.status === 200 && "ok" in result.data) {
53
- const { updated_at, rejected_frontmatter } = result.data;
54
- entry.sha = currentSha;
55
- entry.updated_at = updated_at;
56
- pushed++;
57
- if (rejected_frontmatter.length > 0) {
58
- console.warn(`! ${entry.path}: server kept ${rejected_frontmatter.map((r) => r.key).join(", ")} (your changes ignored)`);
59
- }
60
- }
61
- else if (result.status === 409 && "server_body" in result.data) {
62
- const d = result.data;
63
- const conflictContent = buildConflictFile({
64
- serverFrontmatter: d.server_frontmatter,
65
- localBody: local.body,
66
- serverBody: d.server_body,
67
- serverUpdatedAt: d.server_updated_at,
68
- });
69
- await fs.writeFile(absPath, conflictContent, "utf8");
70
- // Update entry's known updated_at to the server's so a resolve+push works
71
- entry.updated_at = d.server_updated_at;
72
- console.warn(`! conflict: ${entry.path} (server updated ${d.server_updated_at}). Edit to remove merge markers, then \`unfairly resolve <path>\` and \`unfairly push\`.`);
73
- conflicts++;
74
- }
75
- else {
76
- const err = "error" in result.data ? result.data.error : "unknown";
77
- console.error(`push failed for ${entry.path}: ${err}`);
78
- }
79
- }
80
- v.last_pushed_at = new Date().toISOString();
81
- }
82
- await writeManifest(cwd, manifest);
83
- console.log(`pushed ${pushed}, conflicts ${conflicts}`);
84
- }
85
- //# sourceMappingURL=push.js.map
1
+ import{promises as b}from"node:fs";import k from"node:path";import{SyncClient as $}from"../sync/api.js";import{readManifest as g,writeManifest as j}from"../sync/manifest.js";import{computeSha as S,readDocFile as x,readDocRaw as F}from"../sync/files.js";import{buildConflictFile as C}from"../sync/conflict.js";import{getToken as D}from"../sync/credentials.js";async function E(l){const s=process.cwd(),r=await g(s);r||(console.error("No .unfairly/ found. Run `unfairly init` first."),process.exit(1));const u=await D();u||(console.error("No token. Run `unfairly init` to authenticate."),process.exit(1));const _=new $(r.api_url,u),y=l?[l]:Object.keys(r.vaults);let f=0,p=0;for(const i of y){const c=r.vaults[i];if(!c){console.error(`No vault \`${i}\` in manifest.`);continue}for(const[w,t]of Object.entries(c.docs)){const d=k.join(s,".unfairly",t.path);let m,a;try{m=await F(d),a=await x(d)}catch{continue}const h=S(m);if(h===t.sha)continue;const o=await _.push({vault:i,doc_id:w,expected_updated_at:t.updated_at,body:a.body,client_frontmatter:a.frontmatter});if(o.status===200&&"ok"in o.data){const{updated_at:e,rejected_frontmatter:n}=o.data;t.sha=h,t.updated_at=e,p++,n.length>0&&console.warn(`! ${t.path}: server kept ${n.map(v=>v.key).join(", ")} (your changes ignored)`)}else if(o.status===409&&"server_body"in o.data){const e=o.data,n=C({serverFrontmatter:e.server_frontmatter,localBody:a.body,serverBody:e.server_body,serverUpdatedAt:e.server_updated_at});await b.writeFile(d,n,"utf8"),t.updated_at=e.server_updated_at,console.warn(`! conflict: ${t.path} (server updated ${e.server_updated_at}). Edit to remove merge markers, then \`unfairly resolve <path>\` and \`unfairly push\`.`),f++}else{const e="error"in o.data?o.data.error:"unknown";console.error(`push failed for ${t.path}: ${e}`)}}c.last_pushed_at=new Date().toISOString()}await j(s,r),console.log(`pushed ${p}, conflicts ${f}`)}export{E as runPush};
@@ -1,2 +1 @@
1
1
  export declare function runResolve(relOrAbsPath: string): Promise<void>;
2
- //# sourceMappingURL=resolve.d.ts.map
@@ -1,35 +1 @@
1
- import path from "node:path";
2
- import { promises as fs } from "node:fs";
3
- import { readManifest } from "../sync/manifest.js";
4
- export async function runResolve(relOrAbsPath) {
5
- const cwd = process.cwd();
6
- const manifest = await readManifest(cwd);
7
- if (!manifest) {
8
- console.error("No .unfairly/ found.");
9
- process.exit(1);
10
- }
11
- const abs = path.isAbsolute(relOrAbsPath)
12
- ? relOrAbsPath
13
- : path.join(cwd, relOrAbsPath);
14
- const raw = await fs.readFile(abs, "utf8");
15
- if (raw.includes("<<<<<<< local") || raw.includes(">>>>>>> cloud")) {
16
- console.error("Merge markers still present. Edit the file to remove them, then re-run resolve.");
17
- process.exit(1);
18
- }
19
- // Verify the file is in the manifest. We do NOT mutate entry.sha — leaving
20
- // the pre-conflict sha lets `push` detect the resolved content as dirty
21
- // and ship it. entry.updated_at was already advanced to server's during
22
- // the conflict, so the next push's expected_updated_at will match.
23
- const relPath = path.relative(path.join(cwd, ".unfairly"), abs);
24
- for (const v of Object.values(manifest.vaults)) {
25
- for (const [, entry] of Object.entries(v.docs)) {
26
- if (entry.path === relPath) {
27
- console.log(`markers cleared. Run \`unfairly push\` to broadcast.`);
28
- return;
29
- }
30
- }
31
- }
32
- console.error("path not found in manifest");
33
- process.exit(1);
34
- }
35
- //# sourceMappingURL=resolve.js.map
1
+ import e from"node:path";import{promises as l}from"node:fs";import{readManifest as f}from"../sync/manifest.js";async function m(o){const r=process.cwd(),s=await f(r);s||(console.error("No .unfairly/ found."),process.exit(1));const t=e.isAbsolute(o)?o:e.join(r,o),n=await l.readFile(t,"utf8");(n.includes("<<<<<<< local")||n.includes(">>>>>>> cloud"))&&(console.error("Merge markers still present. Edit the file to remove them, then re-run resolve."),process.exit(1));const i=e.relative(e.join(r,".unfairly"),t);for(const c of Object.values(s.vaults))for(const[,a]of Object.entries(c.docs))if(a.path===i){console.log("markers cleared. Run `unfairly push` to broadcast.");return}console.error("path not found in manifest"),process.exit(1)}export{m as runResolve};
@@ -19,4 +19,3 @@ export type SetupOutcome = {
19
19
  };
20
20
  };
21
21
  export declare function runSetup(opts?: SetupOptions): Promise<SetupOutcome>;
22
- //# sourceMappingURL=setup.d.ts.map