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
package/dist/cmd/login.js CHANGED
@@ -1,41 +1,3 @@
1
- import { browserAuthFlow } from "../auth/browser-flow.js";
2
- import { setToken } from "../sync/credentials.js";
3
- const DEFAULT_API = "https://app.unfairly.ai";
4
- /**
5
- * `unfairly login` — runs the OAuth handoff and prints a shell snippet
6
- * the user can paste into their shell config. Useful for setting up
7
- * Codex CLI's UNFAIRLY_MCP_TOKEN env var without copy-pasting tokens.
8
- *
9
- * For most users `unfairly setup` is the better path — it handles env +
10
- * agent host config in one shot. `login` is preserved for scripting use
11
- * cases where you want just the export line on stdout.
12
- *
13
- * Stdout = the export line (only). Stderr = friendly hints.
14
- *
15
- * NOTE: do NOT pipe `unfairly login >> ~/.zshenv` via npx — npx's first-run
16
- * "Ok to proceed?" confirmation goes to stdout and corrupts the file.
17
- * Install globally first (`npm i -g unfairly`) or use `unfairly setup`.
18
- */
19
- export async function runLogin(opts = {}) {
20
- const apiUrl = process.env.UNFAIRLY_API_URL ?? DEFAULT_API;
21
- const token = await browserAuthFlow(apiUrl, { tokenName: "unfairly login" });
22
- if (!token) {
23
- console.error("Login failed — browser flow timed out or was cancelled.");
24
- process.exit(1);
25
- }
26
- await setToken(token);
27
- if (!opts.quiet) {
28
- console.error("Authorized.");
29
- console.error("");
30
- console.error("Add this line to your shell config (~/.zshenv on macOS / Linux):");
31
- console.error("");
32
- }
33
- // The export line goes to STDOUT so it can be piped.
34
- process.stdout.write(`export UNFAIRLY_MCP_TOKEN=${token}\n`);
35
- process.stdout.write(`export UNFAIRLY_TOKEN=${token}\n`);
36
- if (!opts.quiet) {
37
- console.error("");
38
- console.error("Tip: `unfairly setup` handles this AND patches your Codex / Claude Code config in one shot.");
39
- }
40
- }
41
- //# sourceMappingURL=login.js.map
1
+ import{browserAuthFlow as n}from"../auth/browser-flow.js";import{setToken as t}from"../sync/credentials.js";const s="https://app.unfairly.ai";async function l(e={}){const r=process.env.UNFAIRLY_API_URL??s,o=await n(r,{tokenName:"unfairly login"});o||(console.error("Login failed \u2014 browser flow timed out or was cancelled."),process.exit(1)),await t(o),e.quiet||(console.error("Authorized."),console.error(""),console.error("Add this line to your shell config (~/.zshenv on macOS / Linux):"),console.error("")),process.stdout.write(`export UNFAIRLY_MCP_TOKEN=${o}
2
+ `),process.stdout.write(`export UNFAIRLY_TOKEN=${o}
3
+ `),e.quiet||(console.error(""),console.error("Tip: `unfairly setup` handles this AND patches your Codex / Claude Code config in one shot."))}export{l as runLogin};
package/dist/cmd/mcp.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export declare function runMcp(): Promise<void>;
2
- //# sourceMappingURL=mcp.d.ts.map
package/dist/cmd/mcp.js CHANGED
@@ -1,86 +1,2 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
- import { z } from "zod";
5
- import { runPull } from "./pull.js";
6
- import { runPush } from "./push.js";
7
- import { runStatus } from "./status.js";
8
- const PullSchema = z.object({ room: z.string().optional() }).strict();
9
- const PushSchema = z.object({ room: z.string().optional() }).strict();
10
- const StatusSchema = z.object({}).strict();
11
- export async function runMcp() {
12
- const server = new Server({ name: "unfairly-sync", version: "0.1.0" }, { capabilities: { tools: {} } });
13
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
14
- tools: [
15
- {
16
- name: "pull_vault",
17
- description: "Sync local .unfairly/ from cloud. Pass `room` to scope to one vault, omit for all accessible vaults. Reports docs added/updated/deleted.",
18
- inputSchema: {
19
- type: "object",
20
- properties: {
21
- room: {
22
- type: "string",
23
- description: "Vault slug to scope to (optional)",
24
- },
25
- },
26
- },
27
- },
28
- {
29
- name: "push_vault",
30
- description: "Push locally-modified markdown to cloud. Stale-check via expected_updated_at. Returns 409s for conflicts; CLI rewrites those files with merge markers.",
31
- inputSchema: {
32
- type: "object",
33
- properties: {
34
- room: {
35
- type: "string",
36
- description: "Vault slug to scope to (optional)",
37
- },
38
- },
39
- },
40
- },
41
- {
42
- name: "sync_status",
43
- description: "Show local sync status: per-vault last-pulled timestamp + modified-locally count.",
44
- inputSchema: { type: "object", properties: {} },
45
- },
46
- ],
47
- }));
48
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
49
- const { name, arguments: args } = req.params;
50
- const captured = [];
51
- const origLog = console.log;
52
- const origWarn = console.warn;
53
- const origError = console.error;
54
- console.log = (...a) => captured.push(a.map(String).join(" "));
55
- console.warn = (...a) => captured.push("! " + a.map(String).join(" "));
56
- console.error = (...a) => captured.push("ERR " + a.map(String).join(" "));
57
- try {
58
- if (name === "pull_vault") {
59
- const { room } = PullSchema.parse(args ?? {});
60
- await runPull(room);
61
- }
62
- else if (name === "push_vault") {
63
- const { room } = PushSchema.parse(args ?? {});
64
- await runPush(room);
65
- }
66
- else if (name === "sync_status") {
67
- StatusSchema.parse(args ?? {});
68
- await runStatus();
69
- }
70
- else {
71
- throw new Error(`unknown tool: ${name}`);
72
- }
73
- }
74
- finally {
75
- console.log = origLog;
76
- console.warn = origWarn;
77
- console.error = origError;
78
- }
79
- return {
80
- content: [{ type: "text", text: captured.join("\n") || "ok" }],
81
- };
82
- });
83
- const transport = new StdioServerTransport();
84
- await server.connect(transport);
85
- }
86
- //# sourceMappingURL=mcp.js.map
1
+ import{Server as u}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as m}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as d,ListToolsRequestSchema as f}from"@modelcontextprotocol/sdk/types.js";import{z as t}from"zod";import{runPull as h}from"./pull.js";import{runPush as y}from"./push.js";import{runStatus as g}from"./status.js";const S=t.object({room:t.string().optional()}).strict(),w=t.object({room:t.string().optional()}).strict(),v=t.object({}).strict();async function x(){const s=new u({name:"unfairly-sync",version:"0.1.0"},{capabilities:{tools:{}}});s.setRequestHandler(f,async()=>({tools:[{name:"pull_vault",description:"Sync local .unfairly/ from cloud. Pass `room` to scope to one vault, omit for all accessible vaults. Reports docs added/updated/deleted.",inputSchema:{type:"object",properties:{room:{type:"string",description:"Vault slug to scope to (optional)"}}}},{name:"push_vault",description:"Push locally-modified markdown to cloud. Stale-check via expected_updated_at. Returns 409s for conflicts; CLI rewrites those files with merge markers.",inputSchema:{type:"object",properties:{room:{type:"string",description:"Vault slug to scope to (optional)"}}}},{name:"sync_status",description:"Show local sync status: per-vault last-pulled timestamp + modified-locally count.",inputSchema:{type:"object",properties:{}}}]})),s.setRequestHandler(d,async c=>{const{name:e,arguments:n}=c.params,r=[],l=console.log,i=console.warn,p=console.error;console.log=(...o)=>r.push(o.map(String).join(" ")),console.warn=(...o)=>r.push("! "+o.map(String).join(" ")),console.error=(...o)=>r.push("ERR "+o.map(String).join(" "));try{if(e==="pull_vault"){const{room:o}=S.parse(n??{});await h(o)}else if(e==="push_vault"){const{room:o}=w.parse(n??{});await y(o)}else if(e==="sync_status")v.parse(n??{}),await g();else throw new Error(`unknown tool: ${e}`)}finally{console.log=l,console.warn=i,console.error=p}return{content:[{type:"text",text:r.join(`
2
+ `)||"ok"}]}});const a=new m;await s.connect(a)}export{x as runMcp};
@@ -1,5 +1,5 @@
1
1
  import { detectPaceClients } from "../pace/device.js";
2
- export declare const RUNTIME_VERSION = "0.8.3";
2
+ export declare const RUNTIME_VERSION = "0.8.4";
3
3
  export declare const INTELLIGENCE_POLICY: {
4
4
  version: "metadata-v3";
5
5
  mode: "metadata_only";
@@ -41,6 +41,7 @@ export declare function refreshRepositoryPolicy(options?: {
41
41
  force?: boolean;
42
42
  }): Promise<{
43
43
  repositories: number;
44
+ changed: boolean;
44
45
  } | null>;
45
46
  /** Summarizes and uploads the last `days` of local sessions so Intelligence has history from day one. */
46
47
  export declare function runPaceBackfill(days: number): Promise<{
@@ -98,4 +99,3 @@ export declare function runPacePlaybookUse(playbookId: string, playbookVersion:
98
99
  workflow_id: string;
99
100
  queued: number;
100
101
  }>;
101
- //# sourceMappingURL=pace.d.ts.map
package/dist/cmd/pace.js CHANGED
@@ -1,426 +1,4 @@
1
- import { getToken } from "../sync/credentials.js";
2
- import fs from "node:fs/promises";
3
- import { randomUUID } from "node:crypto";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { PaceEventV3Schema } from "../pace/schema.js";
7
- import { normalizeHookEvent } from "../pace/normalize.js";
8
- import { appendPaceEvent, paceSpoolPath, readPaceEvents, removePaceEvents } from "../pace/spool.js";
9
- import { currentDevicePlatform, detectPaceClients, paceDevicePath, readOrCreatePaceDevice, writePaceDevice, } from "../pace/device.js";
10
- const DEFAULT_API = "https://app.unfairly.ai";
11
- export const RUNTIME_VERSION = "0.8.3";
12
- const PLUGIN_VERSION = "0.1.0";
13
- export const INTELLIGENCE_POLICY = {
14
- version: "metadata-v3",
15
- mode: "metadata_only",
16
- permitted_fields: [
17
- "event_id", "installation_id", "session_id", "workflow_id", "occurred_at",
18
- "source", "event_type", "evidence_level", "model_family", "tool_category",
19
- "blocker_category", "approval_mode", "permission_mode", "duration_bucket",
20
- "exit_code_bucket", "token_counts", "playbook_id", "playbook_version",
21
- "repository_id", "commit_sha", "artifact_type", "outcome_state",
22
- ],
23
- prohibited_content: [
24
- "prompts", "responses", "source code", "file contents", "screenshots",
25
- "keystrokes", "clipboard", "raw commands", "arguments", "tool output",
26
- "stdout", "stderr", "paths", "filenames", "repository names", "branches",
27
- "URLs", "titles", "secrets", "arbitrary errors",
28
- ],
29
- };
30
- async function readStdin() {
31
- if (process.stdin.isTTY)
32
- return {};
33
- let raw = "";
34
- for await (const chunk of process.stdin)
35
- raw += String(chunk);
36
- if (!raw.trim())
37
- return {};
38
- try {
39
- return JSON.parse(raw);
40
- }
41
- catch {
42
- return {};
43
- }
44
- }
45
- export async function runPaceCapture(source, hookEvent) {
46
- const device = await readOrCreatePaceDevice();
47
- if (device.paused)
48
- return;
49
- const event = normalizeHookEvent({
50
- source,
51
- hookEvent,
52
- payload: await readStdin(),
53
- installationId: device.installation_id,
54
- });
55
- await appendPaceEvent(event);
56
- const boundary = event.event_type === "session.started" || event.event_type === "session.ended" || event.event_type === "workflow.stopped";
57
- // A failure here must never break the AI workflow.
58
- if (boundary)
59
- await runPaceSync({ flush: process.env.UNFAIRLY_PACE_NO_FLUSH !== "1" }).catch(() => undefined);
60
- }
61
- const SWEEP_OVERLAP_MS = 15 * 60_000;
62
- const FIRST_SWEEP_LOOKBACK_MS = 2 * 86_400_000;
63
- /** How far back the first sync after connecting reads local transcripts. Matches the report's window. */
64
- export const HISTORY_BACKFILL_DAYS = 90;
65
- /**
66
- * Summarizes local sessions and uploads them. Does nothing until this machine
67
- * is connected: before that there is nowhere to send summaries and no
68
- * approved-repository list, and a session summarized without that list would
69
- * lose its commits for good. The first sync after connecting reads the last
70
- * {@link HISTORY_BACKFILL_DAYS} days so the report has history from day one.
71
- */
72
- export async function runPaceSync(options = {}) {
73
- if (!(await paceAuthorization()))
74
- return null;
75
- const device = await readOrCreatePaceDevice();
76
- const firstSync = !device.history_backfilled_at;
77
- // Session summaries carry repository IDs and commit SHAs only for approved
78
- // repositories, so the policy has to be current before sessions are summarized.
79
- await refreshRepositoryPolicy({ force: firstSync }).catch(() => null);
80
- const sweep = firstSync
81
- ? await runPaceSweep({ sinceMs: Date.now() - HISTORY_BACKFILL_DAYS * 86_400_000, collectedVia: "backfill" })
82
- : await runPaceSweep();
83
- if (firstSync)
84
- await writePaceDevice({ ...(await readOrCreatePaceDevice()), history_backfilled_at: new Date().toISOString() });
85
- // A telemetry outage must never break the employee's AI workflow. Keep the
86
- // durable queue and retry on the next session boundary or explicit flush.
87
- if (options.flush !== false)
88
- await runPaceFlush().catch(() => undefined);
89
- return { summarized: sweep.summarized, backfilled: firstSync };
90
- }
91
- /** Summarizes local sessions changed since the last sweep and queues them for upload. */
92
- export async function runPaceSweep(options = {}) {
93
- const { sweepLocalSessions } = await import("../pace/session-summary.js");
94
- const device = await readOrCreatePaceDevice();
95
- const startedAt = Date.now();
96
- const last = device.last_session_sweep_at ? Date.parse(device.last_session_sweep_at) : startedAt - FIRST_SWEEP_LOOKBACK_MS;
97
- const result = await sweepLocalSessions({
98
- since: options.sinceMs ?? last - SWEEP_OVERLAP_MS,
99
- installationId: device.installation_id,
100
- collectedVia: options.collectedVia ?? "hook",
101
- });
102
- await writePaceDevice({ ...(await readOrCreatePaceDevice()), last_session_sweep_at: new Date(startedAt).toISOString() });
103
- return result;
104
- }
105
- /**
106
- * The Authorization header for Intelligence uploads: this installation's own
107
- * collector credential (plugin install path) or, failing that, the signed-in
108
- * CLI token.
109
- */
110
- export async function paceAuthorization() {
111
- const { collectorAuthorization, readCollectorCredential } = await import("../pace/enroll.js");
112
- const credential = await readCollectorCredential();
113
- if (credential)
114
- return collectorAuthorization(credential);
115
- const token = await getToken();
116
- return token ? `Bearer ${token}` : null;
117
- }
118
- function apiBase() {
119
- return (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
120
- }
121
- /** Pulls the organization's approved repositories so sessions can carry repository IDs and commit SHAs. */
122
- export async function refreshRepositoryPolicy(options = {}) {
123
- const device = await readOrCreatePaceDevice();
124
- const fresh = device.repository_policy_refreshed_at && Date.now() - Date.parse(device.repository_policy_refreshed_at) < 3_600_000;
125
- if (fresh && !options.force)
126
- return null;
127
- const authorization = await paceAuthorization();
128
- if (!authorization)
129
- return null;
130
- const response = await fetch(`${apiBase()}/api/ai-pace/repository-policy`, { headers: { Authorization: authorization } });
131
- if (!response.ok)
132
- return null;
133
- const { parseRepositoryPolicy, repositoryPolicyPath } = await import("../pace/repository.js");
134
- const policy = parseRepositoryPolicy(await response.json());
135
- const file = repositoryPolicyPath();
136
- await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
137
- await fs.writeFile(file, `${JSON.stringify(policy, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
138
- await writePaceDevice({ ...(await readOrCreatePaceDevice()), repository_policy_refreshed_at: new Date().toISOString() });
139
- return { repositories: policy.mappings.length };
140
- }
141
- async function uploadSessionSummaries(authorization) {
142
- const { readSessionSpool, removeSessionSummaries } = await import("../pace/session-summary.js");
143
- let sent = 0;
144
- for (let round = 0; round < 50; round += 1) {
145
- const pending = Object.values(await readSessionSpool()).slice(0, 100);
146
- if (!pending.length)
147
- break;
148
- const response = await fetch(`${apiBase()}/api/ai-pace/sessions`, {
149
- method: "POST",
150
- headers: { Authorization: authorization, "Content-Type": "application/json" },
151
- body: JSON.stringify({ sessions: pending }),
152
- });
153
- if (!response.ok)
154
- throw new Error(`Unfairly Intelligence session upload failed (${response.status})`);
155
- await removeSessionSummaries(pending);
156
- sent += pending.length;
157
- }
158
- return { sent, remaining: Object.keys(await readSessionSpool()).length };
159
- }
160
- /** Summarizes and uploads the last `days` of local sessions so Intelligence has history from day one. */
161
- export async function runPaceBackfill(days) {
162
- const policy = await refreshRepositoryPolicy({ force: true }).catch(() => null);
163
- // Uploads are only accepted from a registered, active installation.
164
- await runPaceHeartbeat();
165
- const sweep = await runPaceSweep({ sinceMs: Date.now() - days * 86_400_000, collectedVia: "backfill" });
166
- const authorization = await paceAuthorization();
167
- if (!authorization)
168
- throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
169
- const upload = await uploadSessionSummaries(authorization);
170
- return { summarized: sweep.summarized, uploaded: upload.sent, remaining: upload.remaining, approved_repositories: policy?.repositories ?? null };
171
- }
172
- export async function runPaceFlush() {
173
- const authorization = await paceAuthorization();
174
- if (!authorization)
175
- throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
176
- await refreshRepositoryPolicy().catch(() => null);
177
- const sessions = await uploadSessionSummaries(authorization);
178
- const events = await readPaceEvents();
179
- if (!events.length)
180
- return { sent: 0, remaining: 0, sessions_sent: sessions.sent };
181
- const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
182
- const device = await readOrCreatePaceDevice();
183
- const attributedEvents = events.map((event) => ({
184
- ...event,
185
- installation_id: event.installation_id ?? device.installation_id,
186
- }));
187
- const response = await fetch(`${apiUrl}/api/ai-pace/events`, {
188
- method: "POST",
189
- headers: {
190
- Authorization: authorization,
191
- "Content-Type": "application/json",
192
- ...(device.attestation ? { "X-AI-Pace-Attestation": device.attestation } : {}),
193
- },
194
- body: JSON.stringify({ events: attributedEvents }),
195
- });
196
- if (!response.ok)
197
- throw new Error(`Unfairly Intelligence upload failed (${response.status})`);
198
- await removePaceEvents(new Set(events.map((event) => event.event_id)));
199
- const remaining = (await readPaceEvents()).length;
200
- await writePaceDevice({ ...(await readOrCreatePaceDevice()), last_upload_at: new Date().toISOString(), collection_policy_version: "metadata-v3" });
201
- return { sent: events.length, remaining, sessions_sent: sessions.sent };
202
- }
203
- export async function runPaceHeartbeat() {
204
- const authorization = await paceAuthorization();
205
- if (!authorization)
206
- throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
207
- const device = await readOrCreatePaceDevice();
208
- const clients = await detectPaceClients();
209
- const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
210
- const response = await fetch(`${apiUrl}/api/ai-pace/heartbeat`, {
211
- method: "POST",
212
- headers: {
213
- Authorization: authorization,
214
- "Content-Type": "application/json",
215
- },
216
- body: JSON.stringify({
217
- installation_id: device.installation_id,
218
- agent_version: RUNTIME_VERSION,
219
- runtime_version: RUNTIME_VERSION,
220
- plugin_version: PLUGIN_VERSION,
221
- collection_policy_version: "metadata-v3",
222
- ...currentDevicePlatform(),
223
- clients,
224
- }),
225
- });
226
- if (!response.ok)
227
- throw new Error(`Unfairly Intelligence heartbeat failed (${response.status})`);
228
- const body = await response.json();
229
- if (!body.attestation || !body.expires_at)
230
- throw new Error("Unfairly Intelligence heartbeat returned no attestation");
231
- await writePaceDevice({
232
- ...device,
233
- attestation: body.attestation,
234
- attestation_expires_at: body.expires_at,
235
- collection_policy_version: "metadata-v3",
236
- });
237
- return { installation_id: device.installation_id, clients };
238
- }
239
- export async function runPaceInstall() {
240
- const clients = await detectPaceClients();
241
- const { installIntelligence } = await import("../pace/install.js");
242
- const installed = await installIntelligence({ version: RUNTIME_VERSION, clients });
243
- const device = await readOrCreatePaceDevice();
244
- await writePaceDevice({
245
- ...device,
246
- collection_policy_version: "metadata-v3",
247
- intelligence_install: {
248
- version: RUNTIME_VERSION,
249
- runtime_entrypoint: installed.runtime_entrypoint,
250
- marketplace_root: installed.marketplace_root,
251
- owned_paths: installed.owned_paths,
252
- activated_clients: installed.activations.filter((item) => item.status === "activated").map((item) => item.client),
253
- installed_at: new Date().toISOString(),
254
- },
255
- });
256
- await runPaceHeartbeat();
257
- return {
258
- runtime_entrypoint: installed.runtime_entrypoint,
259
- marketplace_root: installed.marketplace_root,
260
- activations: installed.activations,
261
- verification: { heartbeat: "ok", live_events: "pending_first_session" },
262
- };
263
- }
264
- export async function runPaceStatus() {
265
- const queued = (await readPaceEvents()).length;
266
- const device = await readOrCreatePaceDevice();
267
- const clients = await detectPaceClients();
268
- process.stdout.write(JSON.stringify({
269
- enabled: !device.paused,
270
- paused: Boolean(device.paused),
271
- installation_id: device.installation_id,
272
- attestation_expires_at: device.attestation_expires_at ?? null,
273
- last_upload_at: device.last_upload_at ?? null,
274
- runtime_version: RUNTIME_VERSION,
275
- plugin_version: PLUGIN_VERSION,
276
- collection_policy_version: device.collection_policy_version ?? INTELLIGENCE_POLICY.version,
277
- queued,
278
- spool: paceSpoolPath(),
279
- clients,
280
- }, null, 2) + "\n");
281
- }
282
- export async function runPaceConfig(input) {
283
- const { writeManagedConfigs } = await import("../pace/managed-config.js");
284
- const files = await writeManagedConfigs(input);
285
- const device = await readOrCreatePaceDevice();
286
- await writePaceDevice({ ...device, managed_files: files, collection_policy_version: "metadata-v3" });
287
- return files;
288
- }
289
- export function runPacePolicy() {
290
- return INTELLIGENCE_POLICY;
291
- }
292
- export async function runPacePause(paused) {
293
- const device = await readOrCreatePaceDevice();
294
- await writePaceDevice({ ...device, paused });
295
- return { paused };
296
- }
297
- export async function runPaceUninstall() {
298
- const device = await readOrCreatePaceDevice();
299
- const activated = new Set(device.intelligence_install?.activated_clients ?? []);
300
- const { spawnSync } = await import("node:child_process");
301
- if (activated.has("codex")) {
302
- spawnSync("codex", ["plugin", "remove", "ai-pace@unfairly-intelligence", "--json"], { encoding: "utf8", timeout: 30_000 });
303
- spawnSync("codex", ["plugin", "marketplace", "remove", "unfairly-intelligence"], { encoding: "utf8", timeout: 30_000 });
304
- }
305
- if (activated.has("claude_code")) {
306
- spawnSync("claude", ["plugin", "uninstall", "ai-pace@unfairly-intelligence", "--scope", "user"], { encoding: "utf8", timeout: 30_000 });
307
- spawnSync("claude", ["plugin", "marketplace", "remove", "unfairly-intelligence", "--scope", "user"], { encoding: "utf8", timeout: 30_000 });
308
- }
309
- const permitted = new Set([
310
- "codex-requirements.toml", "claude-settings.json", "gemini-settings.json",
311
- "evidence-policy.json", "repository-policy.json",
312
- ]);
313
- let removed = 0;
314
- for (const file of device.managed_files ?? []) {
315
- const name = file.split(/[\\/]/).at(-1);
316
- if (!name || !permitted.has(name))
317
- continue;
318
- await fs.unlink(file).then(() => { removed += 1; }).catch((error) => {
319
- if (error.code !== "ENOENT")
320
- throw error;
321
- });
322
- }
323
- const safeOwnedRoots = [
324
- path.join(os.homedir(), ".unfairly"),
325
- path.join(os.homedir(), ".cursor", "plugins", "local", "ai-pace"),
326
- path.join(os.homedir(), ".gemini", "extensions", "unfairly-intelligence"),
327
- ];
328
- for (const ownedPath of device.intelligence_install?.owned_paths ?? []) {
329
- const resolved = path.resolve(ownedPath);
330
- if (!safeOwnedRoots.some((root) => resolved === root || resolved.startsWith(`${root}${path.sep}`)))
331
- continue;
332
- await fs.rm(resolved, { recursive: true, force: true });
333
- removed += 1;
334
- }
335
- await fs.unlink(paceSpoolPath()).catch((error) => {
336
- if (error.code !== "ENOENT")
337
- throw error;
338
- });
339
- await fs.unlink(paceDevicePath()).catch((error) => {
340
- if (error.code !== "ENOENT" && error.code !== "EISDIR")
341
- throw error;
342
- });
343
- return { removed_files: removed, retained_global_credentials: true };
344
- }
345
- export async function runPaceEvidenceScan(policyPath) {
346
- const { scanLocalEvidence } = await import("../pace/local-evidence.js");
347
- const device = await readOrCreatePaceDevice();
348
- const result = await scanLocalEvidence({ policyPath, installationId: device.installation_id });
349
- await Promise.all(result.events.map((event) => appendPaceEvent(event)));
350
- return { observed: result.events.length, baseline_roots: result.baselineRoots, queued: (await readPaceEvents()).length };
351
- }
352
- export async function runPaceEvidenceWatch(policyPath) {
353
- const { readLocalEvidencePolicy } = await import("../pace/local-evidence.js");
354
- const policy = await readLocalEvidencePolicy(policyPath);
355
- process.stdout.write(`Unfairly Intelligence is watching ${policy.roots.length} approved local evidence root(s).\n`);
356
- const scan = async () => {
357
- const result = await runPaceEvidenceScan(policyPath);
358
- if (result.observed)
359
- await runPaceFlush().catch(() => undefined);
360
- };
361
- await scan();
362
- await new Promise((resolve) => {
363
- const timer = setInterval(() => { void scan(); }, policy.scan_interval_seconds * 1000);
364
- const stop = () => { clearInterval(timer); resolve(); };
365
- process.once("SIGINT", stop);
366
- process.once("SIGTERM", stop);
367
- });
368
- }
369
- export async function runPacePlaybooks(query) {
370
- const token = await getToken();
371
- if (!token)
372
- throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
373
- const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
374
- const params = query?.trim() ? `?q=${encodeURIComponent(query.trim())}` : "";
375
- const response = await fetch(`${apiUrl}/api/ai-pace/playbooks${params}`, {
376
- headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
377
- });
378
- if (!response.ok)
379
- throw new Error(`Unfairly Intelligence playbooks failed (${response.status})`);
380
- const body = await response.json();
381
- return body.playbooks ?? [];
382
- }
383
- export async function runPacePlaybookUse(playbookId, playbookVersion) {
384
- const token = await getToken();
385
- if (!token)
386
- throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
387
- const device = await readOrCreatePaceDevice();
388
- if (device.paused)
389
- throw new Error("Unfairly Intelligence is paused. Run `unfairly intelligence resume` first.");
390
- const workflowId = randomUUID();
391
- const sourceRecordId = randomUUID();
392
- const event = PaceEventV3Schema.parse({
393
- schema_version: 3,
394
- collection_policy_version: "metadata-v3",
395
- event_id: randomUUID(),
396
- installation_id: device.installation_id,
397
- occurred_at: new Date().toISOString(),
398
- source: "unknown",
399
- event_type: "session.started",
400
- evidence_level: "exact",
401
- session_id: sourceRecordId,
402
- workflow_id: workflowId,
403
- playbook_id: playbookId,
404
- playbook_version: playbookVersion,
405
- attributes: { client_event: "session_start" },
406
- });
407
- await appendPaceEvent(event);
408
- const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
409
- const response = await fetch(`${apiUrl}/api/ai-pace/playbooks`, {
410
- method: "POST",
411
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
412
- body: JSON.stringify({
413
- action: "record",
414
- playbook_id: playbookId,
415
- playbook_version: Number(playbookVersion),
416
- event_type: "started",
417
- source_record_id: sourceRecordId,
418
- workflow_id: workflowId,
419
- }),
420
- });
421
- // The event remains safely spooled when the marketplace endpoint is offline.
422
- if (!response.ok && response.status < 500)
423
- throw new Error(`Unfairly Intelligence playbook use failed (${response.status})`);
424
- return { workflow_id: workflowId, queued: (await readPaceEvents()).length };
425
- }
426
- //# sourceMappingURL=pace.js.map
1
+ import{getToken as v}from"../sync/credentials.js";import u from"node:fs/promises";import{randomUUID as h}from"node:crypto";import g from"node:os";import d from"node:path";import{PaceEventV3Schema as D}from"../pace/schema.js";import{normalizeHookEvent as j}from"../pace/normalize.js";import{appendPaceEvent as k,paceSpoolPath as x,readPaceEvents as _,removePaceEvents as z}from"../pace/spool.js";import{currentDevicePlatform as C,detectPaceClients as I,paceDevicePath as F,readOrCreatePaceDevice as c,writePaceDevice as p}from"../pace/device.js";const f="https://app.unfairly.ai",w="0.8.4",O="0.1.0",R={version:"metadata-v3",mode:"metadata_only",permitted_fields:["event_id","installation_id","session_id","workflow_id","occurred_at","source","event_type","evidence_level","model_family","tool_category","blocker_category","approval_mode","permission_mode","duration_bucket","exit_code_bucket","token_counts","playbook_id","playbook_version","repository_id","commit_sha","artifact_type","outcome_state"],prohibited_content:["prompts","responses","source code","file contents","screenshots","keystrokes","clipboard","raw commands","arguments","tool output","stdout","stderr","paths","filenames","repository names","branches","URLs","titles","secrets","arbitrary errors"]};async function Y(){if(process.stdin.isTTY)return{};let n="";for await(const t of process.stdin)n+=String(t);if(!n.trim())return{};try{return JSON.parse(n)}catch{return{}}}async function ne(n,t){const e=await c();if(e.paused)return;const a=j({source:n,hookEvent:t,payload:await Y(),installationId:e.installation_id});await k(a),(a.event_type==="session.started"||a.event_type==="session.ended"||a.event_type==="workflow.stopped")&&await V({flush:process.env.UNFAIRLY_PACE_NO_FLUSH!=="1"}).catch(()=>{})}const B=15*6e4,J=2*864e5,M=90;async function V(n={}){if(!await m())return null;const t=await c(),e=await P({force:!t.history_backfilled_at}).catch(()=>null),a=!t.history_backfilled_at||!!e?.changed,i=a?await S({sinceMs:Date.now()-M*864e5,collectedVia:"backfill"}):await S();return a&&await p({...await c(),history_backfilled_at:new Date().toISOString()}),n.flush!==!1&&await $().catch(()=>{}),{summarized:i.summarized,backfilled:a}}async function S(n={}){const{sweepLocalSessions:t}=await import("../pace/session-summary.js"),e=await c(),a=Date.now(),i=e.last_session_sweep_at?Date.parse(e.last_session_sweep_at):a-J,o=await t({since:n.sinceMs??i-B,installationId:e.installation_id,collectedVia:n.collectedVia??"hook"});return await p({...await c(),last_session_sweep_at:new Date(a).toISOString()}),o}async function m(){const{collectorAuthorization:n,readCollectorCredential:t}=await import("../pace/enroll.js"),e=await t();if(e)return n(e);const a=await v();return a?`Bearer ${a}`:null}function A(){return(process.env.UNFAIRLY_API_URL??f).replace(/\/$/,"")}async function P(n={}){const t=await c();if(t.repository_policy_refreshed_at&&Date.now()-Date.parse(t.repository_policy_refreshed_at)<36e5&&!n.force)return null;const a=await m();if(!a)return null;const i=await fetch(`${A()}/api/ai-pace/repository-policy`,{headers:{Authorization:a}});if(!i.ok)return null;const{parseRepositoryPolicy:o,repositoryPolicyPath:s}=await import("../pace/repository.js"),l=o(await i.json()),r=s(),b=await u.readFile(r,"utf8").then(y=>o(JSON.parse(y)),()=>null),E=y=>(y?.mappings??[]).map(U=>`${U.repository_id}:${U.remote_fingerprint}`).sort().join(","),T=b!==null&&E(b)!==E(l);return await u.mkdir(d.dirname(r),{recursive:!0,mode:448}),await u.writeFile(r,`${JSON.stringify(l,null,2)}
2
+ `,{encoding:"utf8",mode:384}),await p({...await c(),repository_policy_refreshed_at:new Date().toISOString()}),{repositories:l.mappings.length,changed:T}}async function N(n){const{readSessionSpool:t,removeSessionSummaries:e}=await import("../pace/session-summary.js");let a=0;for(let i=0;i<50;i+=1){const o=Object.values(await t()).slice(0,100);if(!o.length)break;const s=await fetch(`${A()}/api/ai-pace/sessions`,{method:"POST",headers:{Authorization:n,"Content-Type":"application/json"},body:JSON.stringify({sessions:o})});if(!s.ok)throw new Error(`Unfairly Intelligence session upload failed (${s.status})`);await e(o),a+=o.length}return{sent:a,remaining:Object.keys(await t()).length}}async function ae(n){const t=await P({force:!0}).catch(()=>null);await L();const e=await S({sinceMs:Date.now()-n*864e5,collectedVia:"backfill"}),a=await m();if(!a)throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");const i=await N(a);return{summarized:e.summarized,uploaded:i.sent,remaining:i.remaining,approved_repositories:t?.repositories??null}}async function $(){const n=await m();if(!n)throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");await P().catch(()=>null);const t=await N(n),e=await _();if(!e.length)return{sent:0,remaining:0,sessions_sent:t.sent};const a=(process.env.UNFAIRLY_API_URL??f).replace(/\/$/,""),i=await c(),o=e.map(r=>({...r,installation_id:r.installation_id??i.installation_id})),s=await fetch(`${a}/api/ai-pace/events`,{method:"POST",headers:{Authorization:n,"Content-Type":"application/json",...i.attestation?{"X-AI-Pace-Attestation":i.attestation}:{}},body:JSON.stringify({events:o})});if(!s.ok)throw new Error(`Unfairly Intelligence upload failed (${s.status})`);await z(new Set(e.map(r=>r.event_id)));const l=(await _()).length;return await p({...await c(),last_upload_at:new Date().toISOString(),collection_policy_version:"metadata-v3"}),{sent:e.length,remaining:l,sessions_sent:t.sent}}async function L(){const n=await m();if(!n)throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");const t=await c(),e=await I(),a=(process.env.UNFAIRLY_API_URL??f).replace(/\/$/,""),i=await fetch(`${a}/api/ai-pace/heartbeat`,{method:"POST",headers:{Authorization:n,"Content-Type":"application/json"},body:JSON.stringify({installation_id:t.installation_id,agent_version:w,runtime_version:w,plugin_version:O,collection_policy_version:"metadata-v3",...C(),clients:e})});if(!i.ok)throw new Error(`Unfairly Intelligence heartbeat failed (${i.status})`);const o=await i.json();if(!o.attestation||!o.expires_at)throw new Error("Unfairly Intelligence heartbeat returned no attestation");return await p({...t,attestation:o.attestation,attestation_expires_at:o.expires_at,collection_policy_version:"metadata-v3"}),{installation_id:t.installation_id,clients:e}}async function ie(){const n=await I(),{installIntelligence:t}=await import("../pace/install.js"),e=await t({version:w,clients:n}),a=await c();return await p({...a,collection_policy_version:"metadata-v3",intelligence_install:{version:w,runtime_entrypoint:e.runtime_entrypoint,marketplace_root:e.marketplace_root,owned_paths:e.owned_paths,activated_clients:e.activations.filter(i=>i.status==="activated").map(i=>i.client),installed_at:new Date().toISOString()}}),await L(),{runtime_entrypoint:e.runtime_entrypoint,marketplace_root:e.marketplace_root,activations:e.activations,verification:{heartbeat:"ok",live_events:"pending_first_session"}}}async function oe(){const n=(await _()).length,t=await c(),e=await I();process.stdout.write(JSON.stringify({enabled:!t.paused,paused:!!t.paused,installation_id:t.installation_id,attestation_expires_at:t.attestation_expires_at??null,last_upload_at:t.last_upload_at??null,runtime_version:w,plugin_version:O,collection_policy_version:t.collection_policy_version??R.version,queued:n,spool:x(),clients:e},null,2)+`
3
+ `)}async function se(n){const{writeManagedConfigs:t}=await import("../pace/managed-config.js"),e=await t(n),a=await c();return await p({...a,managed_files:e,collection_policy_version:"metadata-v3"}),e}function re(){return R}async function ce(n){const t=await c();return await p({...t,paused:n}),{paused:n}}async function le(){const n=await c(),t=new Set(n.intelligence_install?.activated_clients??[]),{spawnSync:e}=await import("node:child_process");t.has("codex")&&(e("codex",["plugin","remove","ai-pace@unfairly-intelligence","--json"],{encoding:"utf8",timeout:3e4}),e("codex",["plugin","marketplace","remove","unfairly-intelligence"],{encoding:"utf8",timeout:3e4})),t.has("claude_code")&&(e("claude",["plugin","uninstall","ai-pace@unfairly-intelligence","--scope","user"],{encoding:"utf8",timeout:3e4}),e("claude",["plugin","marketplace","remove","unfairly-intelligence","--scope","user"],{encoding:"utf8",timeout:3e4}));const a=new Set(["codex-requirements.toml","claude-settings.json","gemini-settings.json","evidence-policy.json","repository-policy.json"]);let i=0;for(const s of n.managed_files??[]){const l=s.split(/[\\/]/).at(-1);!l||!a.has(l)||await u.unlink(s).then(()=>{i+=1}).catch(r=>{if(r.code!=="ENOENT")throw r})}const o=[d.join(g.homedir(),".unfairly"),d.join(g.homedir(),".cursor","plugins","local","ai-pace"),d.join(g.homedir(),".gemini","extensions","unfairly-intelligence")];for(const s of n.intelligence_install?.owned_paths??[]){const l=d.resolve(s);o.some(r=>l===r||l.startsWith(`${r}${d.sep}`))&&(await u.rm(l,{recursive:!0,force:!0}),i+=1)}return await u.unlink(x()).catch(s=>{if(s.code!=="ENOENT")throw s}),await u.unlink(F()).catch(s=>{if(s.code!=="ENOENT"&&s.code!=="EISDIR")throw s}),{removed_files:i,retained_global_credentials:!0}}async function q(n){const{scanLocalEvidence:t}=await import("../pace/local-evidence.js"),e=await c(),a=await t({policyPath:n,installationId:e.installation_id});return await Promise.all(a.events.map(i=>k(i))),{observed:a.events.length,baseline_roots:a.baselineRoots,queued:(await _()).length}}async function pe(n){const{readLocalEvidencePolicy:t}=await import("../pace/local-evidence.js"),e=await t(n);process.stdout.write(`Unfairly Intelligence is watching ${e.roots.length} approved local evidence root(s).
4
+ `);const a=async()=>{(await q(n)).observed&&await $().catch(()=>{})};await a(),await new Promise(i=>{const o=setInterval(()=>{a()},e.scan_interval_seconds*1e3),s=()=>{clearInterval(o),i()};process.once("SIGINT",s),process.once("SIGTERM",s)})}async function ue(n){const t=await v();if(!t)throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");const e=(process.env.UNFAIRLY_API_URL??f).replace(/\/$/,""),a=n?.trim()?`?q=${encodeURIComponent(n.trim())}`:"",i=await fetch(`${e}/api/ai-pace/playbooks${a}`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});if(!i.ok)throw new Error(`Unfairly Intelligence playbooks failed (${i.status})`);return(await i.json()).playbooks??[]}async function de(n,t){const e=await v();if(!e)throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");const a=await c();if(a.paused)throw new Error("Unfairly Intelligence is paused. Run `unfairly intelligence resume` first.");const i=h(),o=h(),s=D.parse({schema_version:3,collection_policy_version:"metadata-v3",event_id:h(),installation_id:a.installation_id,occurred_at:new Date().toISOString(),source:"unknown",event_type:"session.started",evidence_level:"exact",session_id:o,workflow_id:i,playbook_id:n,playbook_version:t,attributes:{client_event:"session_start"}});await k(s);const l=(process.env.UNFAIRLY_API_URL??f).replace(/\/$/,""),r=await fetch(`${l}/api/ai-pace/playbooks`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({action:"record",playbook_id:n,playbook_version:Number(t),event_type:"started",source_record_id:o,workflow_id:i})});if(!r.ok&&r.status<500)throw new Error(`Unfairly Intelligence playbook use failed (${r.status})`);return{workflow_id:i,queued:(await _()).length}}export{M as HISTORY_BACKFILL_DAYS,R as INTELLIGENCE_POLICY,w as RUNTIME_VERSION,m as paceAuthorization,P as refreshRepositoryPolicy,ae as runPaceBackfill,ne as runPaceCapture,se as runPaceConfig,q as runPaceEvidenceScan,pe as runPaceEvidenceWatch,$ as runPaceFlush,L as runPaceHeartbeat,ie as runPaceInstall,ce as runPacePause,de as runPacePlaybookUse,ue as runPacePlaybooks,re as runPacePolicy,oe as runPaceStatus,S as runPaceSweep,V as runPaceSync,le as runPaceUninstall};
@@ -3,4 +3,3 @@ export type ParsedShareInput = {
3
3
  origin: string | null;
4
4
  };
5
5
  export declare function parseShareInput(input: string): ParsedShareInput;
6
- //# sourceMappingURL=parse-share-input.d.ts.map