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,140 +1,2 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { z } from "zod";
5
- import { collectLocalSessions } from "./local-sessions.js";
6
- import { approvedRepositoryId, readRepositoryPolicy, sessionCommitShas } from "./repository.js";
7
- function readPolicySkills() {
8
- return readRepositoryPolicy()?.skills ?? [];
9
- }
10
- /**
11
- * One content-free summary per AI coding session, uploaded to Unfairly
12
- * Intelligence. This is the unit the server joins to GitHub pull requests and
13
- * prices into spend. Keep in sync with src/lib/ai-pace/sessions.ts.
14
- */
15
- const MODEL_ID = /^(claude-[a-z0-9.-]{1,48}|gpt-[a-z0-9.-]{1,32}|o[1-9][a-z0-9-]{0,20}|gemini-[a-z0-9.-]{1,40}|codex-[a-z0-9.-]{1,32})$/;
16
- const Count = z.number().int().nonnegative();
17
- const SKILL_ID = /^[a-z0-9][a-z0-9:_.-]{0,119}$/;
18
- /** Published plugin skills are namespaced (`superpowers:brainstorming`); a public name, not content. */
19
- const PUBLIC_SKILL = /^[a-z0-9][a-z0-9-]{0,40}:[a-z0-9][a-z0-9-]{0,78}$/;
20
- /**
21
- * Splits a session's skills into names that may be reported (published plugin
22
- * skills and the organization's own catalog) and a count of everything else,
23
- * such as personal or project skills, whose names never leave the device.
24
- */
25
- export function reportableSkills(skills, orgCatalog = []) {
26
- const catalog = new Set(orgCatalog);
27
- const reportable = [...new Set(skills)].filter((skill) => SKILL_ID.test(skill) && (PUBLIC_SKILL.test(skill) || catalog.has(skill)));
28
- return { skills: reportable.slice(0, 20), unlisted: new Set(skills).size - reportable.length };
29
- }
30
- export const SessionSummaryUploadSchema = z.object({
31
- schema_version: z.literal(1),
32
- collection_policy_version: z.literal("metadata-v3"),
33
- installation_id: z.string().uuid(),
34
- session_id: z.string().regex(/^[A-Za-z0-9_-]{1,128}$/),
35
- source: z.enum(["claude_code", "codex"]),
36
- collected_via: z.enum(["hook", "backfill"]),
37
- started_at: z.string().datetime(),
38
- ended_at: z.string().datetime(),
39
- model_id: z.string().regex(MODEL_ID).nullable(),
40
- tokens: z.object({ input: Count, output: Count, cache_read: Count, cache_write: Count }).strict(),
41
- user_turns: Count,
42
- tool_calls: Count,
43
- tool_errors: Count,
44
- approval_denials: Count,
45
- delegated: z.boolean(),
46
- launched_by: z.enum(["claude_code"]).nullable(),
47
- repository_id: z.string().uuid().nullable(),
48
- commit_shas: z.array(z.string().regex(/^[a-f0-9]{40}$/)).max(50),
49
- skills: z.array(z.string().regex(SKILL_ID)).max(20),
50
- unlisted_skills: Count,
51
- }).strict().superRefine((summary, context) => {
52
- if (summary.commit_shas.length && !summary.repository_id) {
53
- context.addIssue({ code: z.ZodIssueCode.custom, path: ["commit_shas"], message: "commit_shas require an approved repository_id" });
54
- }
55
- });
56
- /** Only recognizable model identifiers leave the device; anything else is reported as unknown. */
57
- export function allowlistedModelId(model) {
58
- if (!model)
59
- return null;
60
- const normalized = model.trim().toLowerCase();
61
- return MODEL_ID.test(normalized) ? normalized : null;
62
- }
63
- export function toSessionUpload(session, context) {
64
- const repositoryId = approvedRepositoryId(session.cwd, context.policy) ?? null;
65
- const commits = repositoryId && session.cwd ? sessionCommitShas(session.cwd, session).map((sha) => sha.toLowerCase()) : [];
66
- const skillReport = reportableSkills(session.skills ?? [], context.policy?.skills ?? readPolicySkills());
67
- return SessionSummaryUploadSchema.parse({
68
- schema_version: 1,
69
- collection_policy_version: "metadata-v3",
70
- installation_id: context.installationId,
71
- session_id: session.sessionId.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 128) || "unknown",
72
- source: session.client,
73
- collected_via: context.collectedVia,
74
- started_at: new Date(session.startedAt).toISOString(),
75
- ended_at: new Date(session.endedAt).toISOString(),
76
- model_id: allowlistedModelId(session.primaryModel),
77
- tokens: { input: session.tokens.input, output: session.tokens.output, cache_read: session.tokens.cacheRead, cache_write: session.tokens.cacheWrite },
78
- user_turns: session.userTurns,
79
- tool_calls: session.toolCalls,
80
- tool_errors: session.toolErrors,
81
- approval_denials: session.approvalDenials,
82
- delegated: session.delegated,
83
- launched_by: session.launchedBy,
84
- repository_id: repositoryId,
85
- commit_shas: commits,
86
- skills: skillReport.skills,
87
- unlisted_skills: skillReport.unlisted,
88
- });
89
- }
90
- // Pending uploads, keyed by source and session so a later, fuller summary of a
91
- // still-running session replaces the earlier one.
92
- export function sessionSpoolPath() {
93
- return process.env.UNFAIRLY_PACE_SESSION_SPOOL ?? path.join(os.homedir(), ".unfairly", "pace", "sessions.json");
94
- }
95
- const spoolKey = (summary) => `${summary.source}:${summary.session_id}`;
96
- export async function readSessionSpool() {
97
- try {
98
- const parsed = JSON.parse(await fs.readFile(sessionSpoolPath(), "utf8"));
99
- const valid = {};
100
- for (const [key, value] of Object.entries(parsed)) {
101
- const summary = SessionSummaryUploadSchema.safeParse(value);
102
- if (summary.success)
103
- valid[key] = summary.data;
104
- }
105
- return valid;
106
- }
107
- catch {
108
- return {};
109
- }
110
- }
111
- async function writeSessionSpool(spool) {
112
- const file = sessionSpoolPath();
113
- await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
114
- await fs.writeFile(file, `${JSON.stringify(spool)}\n`, { encoding: "utf8", mode: 0o600 });
115
- }
116
- export async function queueSessionSummaries(summaries) {
117
- const spool = await readSessionSpool();
118
- for (const summary of summaries)
119
- spool[spoolKey(summary)] = summary;
120
- await writeSessionSpool(spool);
121
- return Object.keys(spool).length;
122
- }
123
- export async function removeSessionSummaries(sent) {
124
- const spool = await readSessionSpool();
125
- for (const summary of sent) {
126
- const current = spool[spoolKey(summary)];
127
- // Keep a newer summary that arrived while this batch was uploading.
128
- if (current && current.ended_at === summary.ended_at)
129
- delete spool[spoolKey(summary)];
130
- }
131
- await writeSessionSpool(spool);
132
- }
133
- /** Summarizes every local session whose transcript changed since `since` and queues it for upload. */
134
- export async function sweepLocalSessions(input) {
135
- const sessions = await collectLocalSessions({ since: input.since, home: input.home });
136
- const summaries = sessions.map((session) => toSessionUpload(session, input));
137
- const queued = summaries.length ? await queueSessionSummaries(summaries) : (Object.keys(await readSessionSpool()).length);
138
- return { summarized: summaries.length, queued };
139
- }
140
- //# sourceMappingURL=session-summary.js.map
1
+ import n from"node:fs/promises";import y from"node:os";import d from"node:path";import{z as o}from"zod";import{collectLocalSessions as S}from"./local-sessions.js";import{approvedRepositoryId as g,readRepositoryPolicy as w,sessionCommitShas as k}from"./repository.js";function v(){return w()?.skills??[]}const u=/^(claude-[a-z0-9.-]{1,48}|gpt-[a-z0-9.-]{1,32}|o[1-9][a-z0-9-]{0,20}|gemini-[a-z0-9.-]{1,40}|codex-[a-z0-9.-]{1,32})$/,i=o.number().int().nonnegative(),m=/^[a-z0-9][a-z0-9:_.-]{0,119}$/,z=/^[a-z0-9][a-z0-9-]{0,40}:[a-z0-9][a-z0-9-]{0,78}$/;function I(e,t=[]){const a=new Set(t),r=[...new Set(e)].filter(s=>m.test(s)&&(z.test(s)||a.has(s)));return{skills:r.slice(0,20),unlisted:new Set(e).size-r.length}}const p=o.object({schema_version:o.literal(1),collection_policy_version:o.literal("metadata-v3"),installation_id:o.string().uuid(),session_id:o.string().regex(/^[A-Za-z0-9_-]{1,128}$/),source:o.enum(["claude_code","codex"]),collected_via:o.enum(["hook","backfill"]),started_at:o.string().datetime(),ended_at:o.string().datetime(),model_id:o.string().regex(u).nullable(),tokens:o.object({input:i,output:i,cache_read:i,cache_write:i}).strict(),user_turns:i,tool_calls:i,tool_errors:i,approval_denials:i,delegated:o.boolean(),launched_by:o.enum(["claude_code"]).nullable(),repository_id:o.string().uuid().nullable(),commit_shas:o.array(o.string().regex(/^[a-f0-9]{40}$/)).max(50),skills:o.array(o.string().regex(m)).max(20),unlisted_skills:i}).strict().superRefine((e,t)=>{e.commit_shas.length&&!e.repository_id&&t.addIssue({code:o.ZodIssueCode.custom,path:["commit_shas"],message:"commit_shas require an approved repository_id"})});function b(e){if(!e)return null;const t=e.trim().toLowerCase();return u.test(t)?t:null}function x(e,t){const a=g(e.cwd,t.policy)??null,r=a&&e.cwd?k(e.cwd,e).map(h=>h.toLowerCase()):[],s=I(e.skills??[],t.policy?.skills??v());return p.parse({schema_version:1,collection_policy_version:"metadata-v3",installation_id:t.installationId,session_id:e.sessionId.replace(/[^A-Za-z0-9_-]/g,"").slice(0,128)||"unknown",source:e.client,collected_via:t.collectedVia,started_at:new Date(e.startedAt).toISOString(),ended_at:new Date(e.endedAt).toISOString(),model_id:b(e.primaryModel),tokens:{input:e.tokens.input,output:e.tokens.output,cache_read:e.tokens.cacheRead,cache_write:e.tokens.cacheWrite},user_turns:e.userTurns,tool_calls:e.toolCalls,tool_errors:e.toolErrors,approval_denials:e.approvalDenials,delegated:e.delegated,launched_by:e.launchedBy,repository_id:a,commit_shas:r,skills:s.skills,unlisted_skills:s.unlisted})}function _(){return process.env.UNFAIRLY_PACE_SESSION_SPOOL??d.join(y.homedir(),".unfairly","pace","sessions.json")}const l=e=>`${e.source}:${e.session_id}`;async function c(){try{const e=JSON.parse(await n.readFile(_(),"utf8")),t={};for(const[a,r]of Object.entries(e)){const s=p.safeParse(r);s.success&&(t[a]=s.data)}return t}catch{return{}}}async function f(e){const t=_();await n.mkdir(d.dirname(t),{recursive:!0,mode:448}),await n.writeFile(t,`${JSON.stringify(e)}
2
+ `,{encoding:"utf8",mode:384})}async function L(e){const t=await c();for(const a of e)t[l(a)]=a;return await f(t),Object.keys(t).length}async function D(e){const t=await c();for(const a of e){const r=t[l(a)];r&&r.ended_at===a.ended_at&&delete t[l(a)]}await f(t)}async function R(e){const a=(await S({since:e.since,home:e.home})).map(s=>x(s,e)),r=a.length?await L(a):Object.keys(await c()).length;return{summarized:a.length,queued:r}}export{p as SessionSummaryUploadSchema,b as allowlistedModelId,L as queueSessionSummaries,c as readSessionSpool,D as removeSessionSummaries,I as reportableSkills,_ as sessionSpoolPath,R as sweepLocalSessions,x as toSessionUpload};
@@ -3,4 +3,3 @@ export declare function paceSpoolPath(): string;
3
3
  export declare function appendPaceEvent(event: PaceEvent): Promise<string>;
4
4
  export declare function readPaceEvents(limit?: number): Promise<PaceEvent[]>;
5
5
  export declare function removePaceEvents(ids: Set<string>): Promise<void>;
6
- //# sourceMappingURL=spool.d.ts.map
@@ -1,87 +1,8 @@
1
- import fs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { migrateLegacyEvent } from "./normalize.js";
5
- import { PaceEventSchema, PaceLegacyEventSchema } from "./schema.js";
6
- export function paceSpoolPath() {
7
- return process.env.UNFAIRLY_PACE_SPOOL ?? path.join(os.homedir(), ".unfairly", "pace", "events.jsonl");
8
- }
9
- export async function appendPaceEvent(event) {
10
- const parsed = PaceEventSchema.parse(event);
11
- const spool = paceSpoolPath();
12
- await fs.mkdir(path.dirname(spool), { recursive: true, mode: 0o700 });
13
- await fs.appendFile(spool, `${JSON.stringify(parsed)}\n`, { encoding: "utf8", mode: 0o600 });
14
- await fs.chmod(spool, 0o600).catch(() => undefined);
15
- return spool;
16
- }
17
- export async function readPaceEvents(limit = 250) {
18
- let raw;
19
- try {
20
- raw = await fs.readFile(paceSpoolPath(), "utf8");
21
- }
22
- catch (error) {
23
- if (error.code === "ENOENT")
24
- return [];
25
- throw error;
26
- }
27
- const events = [];
28
- let needsMigration = false;
29
- for (const line of raw.split("\n")) {
30
- if (!line.trim())
31
- continue;
32
- let candidate;
33
- try {
34
- candidate = JSON.parse(line);
35
- }
36
- catch {
37
- needsMigration = true;
38
- continue;
39
- }
40
- const parsed = PaceEventSchema.safeParse(candidate);
41
- if (parsed.success) {
42
- events.push(parsed.data);
43
- }
44
- else {
45
- const legacy = PaceLegacyEventSchema.safeParse(candidate);
46
- if (legacy.success)
47
- events.push(migrateLegacyEvent(legacy.data));
48
- needsMigration = true;
49
- }
50
- }
51
- if (needsMigration) {
52
- const spool = paceSpoolPath();
53
- await fs.writeFile(spool, events.length ? `${events.map((event) => JSON.stringify(event)).join("\n")}\n` : "", {
54
- encoding: "utf8",
55
- mode: 0o600,
56
- });
57
- }
58
- return events.slice(0, limit);
59
- }
60
- export async function removePaceEvents(ids) {
61
- const spool = paceSpoolPath();
62
- let raw;
63
- try {
64
- raw = await fs.readFile(spool, "utf8");
65
- }
66
- catch (error) {
67
- if (error.code === "ENOENT")
68
- return;
69
- throw error;
70
- }
71
- const remaining = raw.split("\n").filter((line) => {
72
- if (!line.trim())
73
- return false;
74
- try {
75
- const event = JSON.parse(line);
76
- return !event.event_id || !ids.has(event.event_id);
77
- }
78
- catch {
79
- return true;
80
- }
81
- });
82
- await fs.writeFile(spool, remaining.length ? `${remaining.join("\n")}\n` : "", {
83
- encoding: "utf8",
84
- mode: 0o600,
85
- });
86
- }
87
- //# sourceMappingURL=spool.js.map
1
+ import o from"node:fs/promises";import d from"node:os";import p from"node:path";import{migrateLegacyEvent as m}from"./normalize.js";import{PaceEventSchema as u,PaceLegacyEventSchema as l}from"./schema.js";function c(){return process.env.UNFAIRLY_PACE_SPOOL??p.join(d.homedir(),".unfairly","pace","events.jsonl")}async function E(i){const r=u.parse(i),e=c();return await o.mkdir(p.dirname(e),{recursive:!0,mode:448}),await o.appendFile(e,`${JSON.stringify(r)}
2
+ `,{encoding:"utf8",mode:384}),await o.chmod(e,384).catch(()=>{}),e}async function P(i=250){let r;try{r=await o.readFile(c(),"utf8")}catch(t){if(t.code==="ENOENT")return[];throw t}const e=[];let a=!1;for(const t of r.split(`
3
+ `)){if(!t.trim())continue;let n;try{n=JSON.parse(t)}catch{a=!0;continue}const s=u.safeParse(n);if(s.success)e.push(s.data);else{const f=l.safeParse(n);f.success&&e.push(m(f.data)),a=!0}}if(a){const t=c();await o.writeFile(t,e.length?`${e.map(n=>JSON.stringify(n)).join(`
4
+ `)}
5
+ `:"",{encoding:"utf8",mode:384})}return e.slice(0,i)}async function N(i){const r=c();let e;try{e=await o.readFile(r,"utf8")}catch(t){if(t.code==="ENOENT")return;throw t}const a=e.split(`
6
+ `).filter(t=>{if(!t.trim())return!1;try{const n=JSON.parse(t);return!n.event_id||!i.has(n.event_id)}catch{return!0}});await o.writeFile(r,a.length?`${a.join(`
7
+ `)}
8
+ `:"",{encoding:"utf8",mode:384})}export{E as appendPaceEvent,c as paceSpoolPath,P as readPaceEvents,N as removePaceEvents};
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=pace-runtime.d.ts.map
@@ -1,67 +1,2 @@
1
- /**
2
- * The runtime bundled inside the Unfairly plugin (plugins/ai-pace/runtime), so
3
- * installing the plugin from an AI client's plugin store is the whole install:
4
- * no npm, no terminal. Hooks call it as `node unfairly-intelligence.mjs <command>`.
5
- *
6
- * connect <source> <event> first-run connection; prints the SessionStart message
7
- * await-approval background: claims the credential once the person approves, then syncs
8
- * capture <source> <event> record a hook event (reads the hook payload on stdin)
9
- */
10
- import { spawn } from "node:child_process";
11
- import os from "node:os";
12
- import { runPaceCapture, runPaceSync } from "./cmd/pace.js";
13
- import { readOrCreatePaceDevice } from "./pace/device.js";
14
- import { acquireApprovalLock, awaitApproval, ensureEnrolled, releaseApprovalLock, sessionStartOutput } from "./pace/enroll.js";
15
- const DEFAULT_API = "https://app.unfairly.ai";
16
- const apiUrl = () => (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
17
- async function connect(source, event) {
18
- const device = await readOrCreatePaceDevice();
19
- if (device.paused)
20
- return;
21
- const platform = os.platform();
22
- const arch = os.arch();
23
- if ((platform !== "darwin" && platform !== "linux" && platform !== "win32") || (arch !== "x64" && arch !== "arm64"))
24
- return;
25
- const state = await ensureEnrolled({
26
- apiUrl: apiUrl(),
27
- installationId: device.installation_id,
28
- platform,
29
- arch,
30
- // The calling client identifies itself; probing every installed tool is too slow for a session start.
31
- clients: [source],
32
- orgHint: process.env.UNFAIRLY_ORG,
33
- });
34
- const output = sessionStartOutput(state, event === "sessionStart" ? "sessionStart" : "SessionStart");
35
- if (output)
36
- process.stdout.write(`${output}\n`);
37
- if (state.status === "waiting") {
38
- // Watch for the approval in the background so the machine connects while
39
- // this session is still open. Detached, so the session never waits on it.
40
- const child = spawn(process.execPath, [process.argv[1], "await-approval"], { detached: true, stdio: "ignore", env: process.env });
41
- child.on("error", () => undefined);
42
- child.unref();
43
- }
44
- }
45
- async function awaitApprovalAndSync() {
46
- if (!(await acquireApprovalLock()))
47
- return;
48
- try {
49
- if (await awaitApproval({ apiUrl: apiUrl() }))
50
- await runPaceSync();
51
- }
52
- finally {
53
- await releaseApprovalLock();
54
- }
55
- }
56
- async function main() {
57
- const [command, source = "unknown", event = "Unknown"] = process.argv.slice(2);
58
- if (command === "connect")
59
- await connect(source, event);
60
- else if (command === "await-approval")
61
- await awaitApprovalAndSync();
62
- else if (command === "capture")
63
- await runPaceCapture(source, event);
64
- }
65
- // Telemetry must never break the person's AI session.
66
- main().catch(() => undefined).finally(() => process.exit(0));
67
- //# sourceMappingURL=pace-runtime.js.map
1
+ import{spawn as l}from"node:child_process";import c from"node:os";import{runPaceCapture as u,runPaceSync as f}from"./cmd/pace.js";import{readOrCreatePaceDevice as w}from"./pace/device.js";import{acquireApprovalLock as d,awaitApproval as m,ensureEnrolled as v,releaseApprovalLock as A,sessionStartOutput as y}from"./pace/enroll.js";const U="https://app.unfairly.ai",p=()=>(process.env.UNFAIRLY_API_URL??U).replace(/\/$/,"");async function h(a,n){const t=await w();if(t.paused)return;const r=c.platform(),e=c.arch();if(r!=="darwin"&&r!=="linux"&&r!=="win32"||e!=="x64"&&e!=="arm64")return;const i=await v({apiUrl:p(),installationId:t.installation_id,platform:r,arch:e,clients:[a],orgHint:process.env.UNFAIRLY_ORG}),o=y(i,n==="sessionStart"?"sessionStart":"SessionStart");if(o&&process.stdout.write(`${o}
2
+ `),i.status==="waiting"){const s=l(process.execPath,[process.argv[1],"await-approval"],{detached:!0,stdio:"ignore",env:process.env});s.on("error",()=>{}),s.unref()}}async function S(){if(await d())try{await m({apiUrl:p()})&&await f()}finally{await A()}}async function L(){const[a,n="unknown",t="Unknown"]=process.argv.slice(2);a==="connect"?await h(n,t):a==="await-approval"?await S():a==="capture"&&await u(n,t)}L().catch(()=>{}).finally(()=>process.exit(0));
@@ -77,4 +77,3 @@ export declare class SyncClient {
77
77
  }>;
78
78
  status(): Promise<StatusResponse>;
79
79
  }
80
- //# sourceMappingURL=api.d.ts.map
package/dist/sync/api.js CHANGED
@@ -1,55 +1 @@
1
- export class SyncClient {
2
- apiUrl;
3
- token;
4
- constructor(apiUrl, token) {
5
- this.apiUrl = apiUrl;
6
- this.token = token;
7
- }
8
- headers() {
9
- return {
10
- Authorization: `Bearer ${this.token}`,
11
- "Content-Type": "application/json",
12
- };
13
- }
14
- async pull(vault, since) {
15
- const url = new URL(`${this.apiUrl}/api/sync/pull`);
16
- url.searchParams.set("vault", vault);
17
- if (since)
18
- url.searchParams.set("since", since);
19
- const res = await fetch(url, { headers: this.headers() });
20
- if (!res.ok) {
21
- throw new Error(`pull failed: ${res.status} ${await res.text()}`);
22
- }
23
- return res.json();
24
- }
25
- /** Loop pull until has_more=false, merging pages. */
26
- async pullAll(vault, since) {
27
- const merged = await this.pull(vault, since);
28
- while (merged.has_more && merged.next_cursor) {
29
- const next = await this.pull(vault, merged.next_cursor);
30
- merged.docs.push(...next.docs);
31
- merged.deleted.push(...next.deleted);
32
- merged.next_cursor = next.next_cursor;
33
- merged.has_more = next.has_more;
34
- }
35
- return merged;
36
- }
37
- async push(args) {
38
- const res = await fetch(`${this.apiUrl}/api/sync/push`, {
39
- method: "POST",
40
- headers: this.headers(),
41
- body: JSON.stringify(args),
42
- });
43
- return { status: res.status, data: (await res.json()) };
44
- }
45
- async status() {
46
- const res = await fetch(`${this.apiUrl}/api/sync/status`, {
47
- headers: this.headers(),
48
- });
49
- if (!res.ok) {
50
- throw new Error(`status failed: ${res.status} ${await res.text()}`);
51
- }
52
- return res.json();
53
- }
54
- }
55
- //# sourceMappingURL=api.js.map
1
+ class n{apiUrl;token;constructor(t,e){this.apiUrl=t,this.token=e}headers(){return{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"}}async pull(t,e){const s=new URL(`${this.apiUrl}/api/sync/pull`);s.searchParams.set("vault",t),e&&s.searchParams.set("since",e);const a=await fetch(s,{headers:this.headers()});if(!a.ok)throw new Error(`pull failed: ${a.status} ${await a.text()}`);return a.json()}async pullAll(t,e){const s=await this.pull(t,e);for(;s.has_more&&s.next_cursor;){const a=await this.pull(t,s.next_cursor);s.docs.push(...a.docs),s.deleted.push(...a.deleted),s.next_cursor=a.next_cursor,s.has_more=a.has_more}return s}async push(t){const e=await fetch(`${this.apiUrl}/api/sync/push`,{method:"POST",headers:this.headers(),body:JSON.stringify(t)});return{status:e.status,data:await e.json()}}async status(){const t=await fetch(`${this.apiUrl}/api/sync/status`,{headers:this.headers()});if(!t.ok)throw new Error(`status failed: ${t.status} ${await t.text()}`);return t.json()}}export{n as SyncClient};
@@ -4,4 +4,3 @@ import type { Manifest } from "./manifest.js";
4
4
  * frontmatter, build the autoloader index, and write CLAUDE.md + .gitignore.
5
5
  */
6
6
  export declare function writeAutoloader(unfairlyRoot: string, manifest: Manifest): Promise<void>;
7
- //# sourceMappingURL=autoloader.d.ts.map
@@ -1,128 +1,8 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import matter from "gray-matter";
4
- const SENTINEL_HEADER = "# Unfairly Vault Context (auto-generated by `unfairly`)";
5
- const GENERATED_CLAUDE_MD = "CLAUDE.md";
6
- /**
7
- * After `unfairly pull` populates .unfairly/, scan local files, parse
8
- * frontmatter, build the autoloader index, and write CLAUDE.md + .gitignore.
9
- */
10
- export async function writeAutoloader(unfairlyRoot, manifest) {
11
- const docs = await scanLocalDocs(unfairlyRoot, manifest);
12
- const goldPrimitives = docs
13
- .filter((d) => d.trust_tier === "gold" && d.role === "primitive")
14
- .sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""))
15
- .slice(0, 8);
16
- const recentSilver = docs
17
- .filter((d) => d.trust_tier === "silver")
18
- .sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""))
19
- .slice(0, 5);
20
- const claudeMd = renderClaudeMd(goldPrimitives, recentSilver);
21
- await fs.writeFile(path.join(unfairlyRoot, GENERATED_CLAUDE_MD), claudeMd, "utf8");
22
- // .gitignore: write only if missing. The generated file invites users to
23
- // uncomment !vault/ re-includes — regenerating on every pull would wipe them.
24
- const gitignorePath = path.join(unfairlyRoot, ".gitignore");
25
- try {
26
- await fs.access(gitignorePath);
27
- }
28
- catch {
29
- await fs.writeFile(gitignorePath, renderGitignore(), "utf8");
30
- }
31
- }
32
- async function scanLocalDocs(root, manifest) {
33
- const entries = [];
34
- // manifest.vaults is Record<string, ManifestVault> keyed by slug.
35
- for (const vault of Object.values(manifest.vaults)) {
36
- // Repo-bound vaults live at the root; non-tracked vaults live under cache/.
37
- const vaultRoot = vault.track_in_git
38
- ? path.join(root, vault.slug)
39
- : path.join(root, "cache", vault.slug);
40
- try {
41
- await walkAndParse(vaultRoot, root, vault.slug, entries);
42
- }
43
- catch {
44
- // Vault dir doesn't exist yet (e.g., empty vault) — fine, skip.
45
- }
46
- }
47
- return entries;
48
- }
49
- async function walkAndParse(dir, unfairlyRoot, vaultSlug, out) {
50
- const generatedRootPath = path.join(unfairlyRoot, GENERATED_CLAUDE_MD);
51
- const items = await fs.readdir(dir, { withFileTypes: true });
52
- for (const item of items) {
53
- const full = path.join(dir, item.name);
54
- if (item.isDirectory()) {
55
- await walkAndParse(full, unfairlyRoot, vaultSlug, out);
56
- }
57
- else if (item.name.endsWith(".md")) {
58
- // Skip ONLY our own generated file (not user docs that happen to be named CLAUDE.md).
59
- if (path.resolve(full) === path.resolve(generatedRootPath))
60
- continue;
61
- try {
62
- const raw = await fs.readFile(full, "utf8");
63
- const { data } = matter(raw);
64
- // Normalize to forward slashes — @-import processors expect POSIX-style paths.
65
- const relative = path.relative(unfairlyRoot, full).split(path.sep).join("/");
66
- // gray-matter / js-yaml auto-coerces ISO 8601 timestamps to Date — normalize to string.
67
- const updatedAtRaw = data.updated_at;
68
- const updated_at = updatedAtRaw instanceof Date
69
- ? updatedAtRaw.toISOString()
70
- : typeof updatedAtRaw === "string"
71
- ? updatedAtRaw
72
- : undefined;
73
- out.push({
74
- relativePath: relative,
75
- trust_tier: typeof data.trust_tier === "string" ? data.trust_tier : undefined,
76
- role: typeof data.role === "string" ? data.role : undefined,
77
- updated_at,
78
- vaultSlug,
79
- });
80
- }
81
- catch {
82
- // Unparseable (malformed YAML, encoding) — skip.
83
- }
84
- }
85
- }
86
- }
87
- function renderClaudeMd(gold, silver) {
88
- const lines = [];
89
- lines.push(SENTINEL_HEADER);
90
- lines.push("");
91
- lines.push("This directory mirrors your accessible Unfairly vaults. Files below are");
92
- lines.push("the active canon — read them before answering questions about this team's");
93
- lines.push("architecture, decisions, brand, or in-flight work.");
94
- lines.push("");
95
- lines.push("## Canon (gold, top primitives)");
96
- if (gold.length === 0) {
97
- lines.push("(No gold primitives in your accessible vaults yet. Run `unfairly pull` after the team seeds canon docs.)");
98
- }
99
- else {
100
- for (const d of gold)
101
- lines.push(`@${d.relativePath}`);
102
- }
103
- lines.push("");
104
- lines.push("## Working knowledge (silver, recently touched)");
105
- if (silver.length === 0) {
106
- lines.push("(No recent silver docs.)");
107
- }
108
- else {
109
- for (const d of silver)
110
- lines.push(`@${d.relativePath}`);
111
- }
112
- lines.push("");
113
- lines.push("## Conventions");
114
- lines.push("- Trust tier = audience signal, not visibility filter. All tiers are working memory.");
115
- lines.push("- This file is regenerated on every `unfairly init` or `unfairly pull`.");
116
- lines.push(" Don't hand-edit — your changes will be overwritten.");
117
- lines.push("- For real-time multiplayer state (current checkpoints, applied edits,");
118
- lines.push(" pending proposals, online teammates), call `enter_org` via MCP.");
119
- lines.push(" This filesystem mirror is static-as-of-last-pull.");
120
- return lines.join("\n") + "\n";
121
- }
122
- function renderGitignore() {
123
- return `# Default: ignore everything in this directory.
1
+ import{promises as c}from"node:fs";import n from"node:path";import y from"gray-matter";const v="# Unfairly Vault Context (auto-generated by `unfairly`)",h="CLAUDE.md";async function E(t,r){const e=await w(t,r),i=e.filter(s=>s.trust_tier==="gold"&&s.role==="primitive").sort((s,l)=>(l.updated_at??"").localeCompare(s.updated_at??"")).slice(0,8),o=e.filter(s=>s.trust_tier==="silver").sort((s,l)=>(l.updated_at??"").localeCompare(s.updated_at??"")).slice(0,5),p=_(i,o);await c.writeFile(n.join(t,h),p,"utf8");const a=n.join(t,".gitignore");try{await c.access(a)}catch{await c.writeFile(a,b(),"utf8")}}async function w(t,r){const e=[];for(const i of Object.values(r.vaults)){const o=i.track_in_git?n.join(t,i.slug):n.join(t,"cache",i.slug);try{await f(o,t,i.slug,e)}catch{}}return e}async function f(t,r,e,i){const o=n.join(r,h),p=await c.readdir(t,{withFileTypes:!0});for(const a of p){const s=n.join(t,a.name);if(a.isDirectory())await f(s,r,e,i);else if(a.name.endsWith(".md")){if(n.resolve(s)===n.resolve(o))continue;try{const l=await c.readFile(s,"utf8"),{data:u}=y(l),g=n.relative(r,s).split(n.sep).join("/"),d=u.updated_at,m=d instanceof Date?d.toISOString():typeof d=="string"?d:void 0;i.push({relativePath:g,trust_tier:typeof u.trust_tier=="string"?u.trust_tier:void 0,role:typeof u.role=="string"?u.role:void 0,updated_at:m,vaultSlug:e})}catch{}}}}function _(t,r){const e=[];if(e.push(v),e.push(""),e.push("This directory mirrors your accessible Unfairly vaults. Files below are"),e.push("the active canon \u2014 read them before answering questions about this team's"),e.push("architecture, decisions, brand, or in-flight work."),e.push(""),e.push("## Canon (gold, top primitives)"),t.length===0)e.push("(No gold primitives in your accessible vaults yet. Run `unfairly pull` after the team seeds canon docs.)");else for(const i of t)e.push(`@${i.relativePath}`);if(e.push(""),e.push("## Working knowledge (silver, recently touched)"),r.length===0)e.push("(No recent silver docs.)");else for(const i of r)e.push(`@${i.relativePath}`);return e.push(""),e.push("## Conventions"),e.push("- Trust tier = audience signal, not visibility filter. All tiers are working memory."),e.push("- This file is regenerated on every `unfairly init` or `unfairly pull`."),e.push(" Don't hand-edit \u2014 your changes will be overwritten."),e.push("- For real-time multiplayer state (current checkpoints, applied edits,"),e.push(" pending proposals, online teammates), call `enter_org` via MCP."),e.push(" This filesystem mirror is static-as-of-last-pull."),e.join(`
2
+ `)+`
3
+ `}function b(){return`# Default: ignore everything in this directory.
124
4
  # .unfairly/ is a local cache of vault content. By default, none of it is
125
- # committed — agents read from disk; the source of truth is in cloud vaults.
5
+ # committed \u2014 agents read from disk; the source of truth is in cloud vaults.
126
6
  *
127
7
 
128
8
  # Re-include the autoloader and this gitignore so Claude Code finds them:
@@ -133,6 +13,4 @@ function renderGitignore() {
133
13
  # whose docs should be versioned alongside code), add explicit re-includes:
134
14
  # !unfairly-app/
135
15
  # !unfairly-app/**
136
- `;
137
- }
138
- //# sourceMappingURL=autoloader.js.map
16
+ `}export{E as writeAutoloader};
@@ -9,4 +9,3 @@ export declare function buildConflictFile(args: {
9
9
  serverBody: string;
10
10
  serverUpdatedAt: string;
11
11
  }): string;
12
- //# sourceMappingURL=conflict.d.ts.map
@@ -1,16 +1,6 @@
1
- import matter from "gray-matter";
2
- /**
3
- * Build a three-way merge file: server frontmatter (always wins for
4
- * authoritative keys), then a conflict-marked body section. The user
5
- * (or their agent) edits to remove markers, then runs `unfairly resolve`.
6
- */
7
- export function buildConflictFile(args) {
8
- const conflictBody = `<<<<<<< local
9
- ${args.localBody}
1
+ import e from"gray-matter";function d(t){const o=`<<<<<<< local
2
+ ${t.localBody}
10
3
  =======
11
- ${args.serverBody}
12
- >>>>>>> cloud (updated_at ${args.serverUpdatedAt})
13
- `;
14
- return matter.stringify(conflictBody, args.serverFrontmatter);
15
- }
16
- //# sourceMappingURL=conflict.js.map
4
+ ${t.serverBody}
5
+ >>>>>>> cloud (updated_at ${t.serverUpdatedAt})
6
+ `;return e.stringify(o,t.serverFrontmatter)}export{d as buildConflictFile};
@@ -5,4 +5,3 @@
5
5
  export declare function getToken(): Promise<string | null>;
6
6
  export declare function setToken(token: string): Promise<void>;
7
7
  export declare function clearToken(): Promise<void>;
8
- //# sourceMappingURL=credentials.d.ts.map
@@ -1,51 +1 @@
1
- import { promises as fs } from "node:fs";
2
- import path from "node:path";
3
- import os from "node:os";
4
- const SERVICE = "unfairly";
5
- const ACCOUNT = "default";
6
- const FILE_FALLBACK = path.join(os.homedir(), ".unfairly", "credentials");
7
- /**
8
- * Try OS keychain via keytar; if unavailable (Linux without libsecret, CI,
9
- * etc.), fall back to a 0600 file in ~/.unfairly/credentials.
10
- */
11
- export async function getToken() {
12
- try {
13
- const keytar = (await import("keytar")).default;
14
- const t = await keytar.getPassword(SERVICE, ACCOUNT);
15
- if (t)
16
- return t;
17
- }
18
- catch {
19
- // keytar unavailable; fall through
20
- }
21
- try {
22
- return (await fs.readFile(FILE_FALLBACK, "utf8")).trim();
23
- }
24
- catch {
25
- return null;
26
- }
27
- }
28
- export async function setToken(token) {
29
- try {
30
- const keytar = (await import("keytar")).default;
31
- await keytar.setPassword(SERVICE, ACCOUNT, token);
32
- return;
33
- }
34
- catch {
35
- // fall through
36
- }
37
- await fs.mkdir(path.dirname(FILE_FALLBACK), { recursive: true });
38
- await fs.writeFile(FILE_FALLBACK, token, { mode: 0o600 });
39
- }
40
- export async function clearToken() {
41
- try {
42
- const keytar = (await import("keytar")).default;
43
- await keytar.deletePassword(SERVICE, ACCOUNT);
44
- }
45
- catch { }
46
- try {
47
- await fs.unlink(FILE_FALLBACK);
48
- }
49
- catch { }
50
- }
51
- //# sourceMappingURL=credentials.js.map
1
+ import{promises as a}from"node:fs";import n from"node:path";import c from"node:os";const i="unfairly",o="default",r=n.join(c.homedir(),".unfairly","credentials");async function f(){try{const e=await(await import("keytar")).default.getPassword(i,o);if(e)return e}catch{}try{return(await a.readFile(r,"utf8")).trim()}catch{return null}}async function m(t){try{await(await import("keytar")).default.setPassword(i,o,t);return}catch{}await a.mkdir(n.dirname(r),{recursive:!0}),await a.writeFile(r,t,{mode:384})}async function d(){try{await(await import("keytar")).default.deletePassword(i,o)}catch{}try{await a.unlink(r)}catch{}}export{d as clearToken,f as getToken,m as setToken};
@@ -40,4 +40,3 @@ export declare function readDocRaw(absPath: string): Promise<string>;
40
40
  export declare function readDocFile(absPath: string): Promise<DocFile>;
41
41
  /** Atomic write (tmp + rename). */
42
42
  export declare function writeDocFile(absPath: string, content: string): Promise<void>;
43
- //# sourceMappingURL=files.d.ts.map