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.
- package/CHANGELOG.md +4 -0
- package/LICENSE +15 -0
- package/dist/auth/browser-flow.d.ts +0 -1
- package/dist/auth/browser-flow.js +5 -81
- package/dist/cmd/connect.d.ts +0 -1
- package/dist/cmd/connect.js +1 -164
- package/dist/cmd/init.d.ts +0 -1
- package/dist/cmd/init.js +2 -138
- package/dist/cmd/invite.d.ts +0 -1
- package/dist/cmd/invite.js +4 -73
- package/dist/cmd/join.d.ts +0 -1
- package/dist/cmd/join.js +1 -91
- package/dist/cmd/login.d.ts +0 -1
- package/dist/cmd/login.js +3 -41
- package/dist/cmd/mcp.d.ts +0 -1
- package/dist/cmd/mcp.js +2 -86
- package/dist/cmd/pace.d.ts +2 -2
- package/dist/cmd/pace.js +4 -426
- package/dist/cmd/parse-share-input.d.ts +0 -1
- package/dist/cmd/parse-share-input.js +1 -56
- package/dist/cmd/pull.d.ts +0 -1
- package/dist/cmd/pull.js +7 -143
- package/dist/cmd/push.d.ts +0 -1
- package/dist/cmd/push.js +1 -85
- package/dist/cmd/resolve.d.ts +0 -1
- package/dist/cmd/resolve.js +1 -35
- package/dist/cmd/setup.d.ts +0 -1
- package/dist/cmd/setup.js +2 -345
- package/dist/cmd/status-card.d.ts +0 -1
- package/dist/cmd/status-card.js +2 -156
- package/dist/cmd/status.d.ts +0 -1
- package/dist/cmd/status.js +3 -29
- package/dist/cmd/watch.d.ts +0 -1
- package/dist/cmd/watch.js +1 -26
- package/dist/hosts/claude-code.d.ts +0 -1
- package/dist/hosts/claude-code.js +3 -180
- package/dist/hosts/claude-desktop.d.ts +0 -1
- package/dist/hosts/claude-desktop.js +3 -222
- package/dist/hosts/codex.d.ts +0 -1
- package/dist/hosts/codex.js +20 -234
- package/dist/hosts/shell-env.d.ts +0 -1
- package/dist/hosts/shell-env.js +9 -81
- package/dist/index.d.ts +0 -1
- package/dist/index.js +14 -206
- package/dist/lib/client.d.ts +0 -1
- package/dist/lib/client.js +1 -58
- package/dist/lib/events/emit.d.ts +0 -1
- package/dist/lib/events/emit.js +1 -28
- package/dist/lib/index.d.ts +0 -1
- package/dist/lib/index.js +1 -11
- package/dist/lib/transport.d.ts +0 -1
- package/dist/lib/transport.js +1 -85
- package/dist/lib/types/index.d.ts +0 -1
- package/dist/lib/types/index.js +1 -50
- package/dist/lib/vault/checkpoint.d.ts +0 -1
- package/dist/lib/vault/checkpoint.js +1 -18
- package/dist/lib/vault/read.d.ts +0 -1
- package/dist/lib/vault/read.js +1 -9
- package/dist/lib/vault/search.d.ts +0 -1
- package/dist/lib/vault/search.js +1 -10
- package/dist/pace/device.d.ts +0 -1
- package/dist/pace/device.js +2 -149
- package/dist/pace/enroll.d.ts +0 -1
- package/dist/pace/enroll.js +2 -138
- package/dist/pace/identity.d.ts +0 -1
- package/dist/pace/identity.js +1 -17
- package/dist/pace/install.d.ts +0 -1
- package/dist/pace/install.js +4 -127
- package/dist/pace/local-evidence.d.ts +0 -1
- package/dist/pace/local-evidence.js +2 -228
- package/dist/pace/local-sessions.d.ts +0 -1
- package/dist/pace/local-sessions.js +1 -254
- package/dist/pace/managed-config.d.ts +0 -1
- package/dist/pace/managed-config.js +8 -102
- package/dist/pace/normalize.d.ts +0 -1
- package/dist/pace/normalize.js +1 -293
- package/dist/pace/repository.d.ts +0 -1
- package/dist/pace/repository.js +2 -120
- package/dist/pace/schema.d.ts +0 -1
- package/dist/pace/schema.js +1 -134
- package/dist/pace/session-summary.d.ts +0 -1
- package/dist/pace/session-summary.js +2 -140
- package/dist/pace/spool.d.ts +0 -1
- package/dist/pace/spool.js +8 -87
- package/dist/pace-runtime.d.ts +0 -1
- package/dist/pace-runtime.js +2 -67
- package/dist/sync/api.d.ts +0 -1
- package/dist/sync/api.js +1 -55
- package/dist/sync/autoloader.d.ts +0 -1
- package/dist/sync/autoloader.js +5 -127
- package/dist/sync/conflict.d.ts +0 -1
- package/dist/sync/conflict.js +5 -15
- package/dist/sync/credentials.d.ts +0 -1
- package/dist/sync/credentials.js +1 -51
- package/dist/sync/files.d.ts +0 -1
- package/dist/sync/files.js +1 -71
- package/dist/sync/manifest.d.ts +0 -1
- package/dist/sync/manifest.js +1 -37
- package/dist/sync/repo-root.d.ts +0 -1
- package/dist/sync/repo-root.js +1 -24
- package/dist/sync/stitch-claude-md.d.ts +0 -1
- package/dist/sync/stitch-claude-md.js +8 -48
- package/dist/ui/colors.d.ts +0 -1
- package/dist/ui/colors.js +1 -28
- package/dist/ui/screens.d.ts +0 -1
- package/dist/ui/screens.js +5 -135
- package/package.json +5 -12
- package/plugin/.cursor-plugin/plugin.json +0 -2
- package/plugin/.mcp.json +1 -1
- package/plugin/LICENSE +15 -0
- package/plugin/gemini/gemini-extension.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/plugin.json +0 -2
- package/plugin/runtime/unfairly-intelligence.mjs +14 -5512
- package/dist/auth/browser-flow.d.ts.map +0 -1
- package/dist/auth/browser-flow.js.map +0 -1
- package/dist/cmd/connect.d.ts.map +0 -1
- package/dist/cmd/connect.js.map +0 -1
- package/dist/cmd/init.d.ts.map +0 -1
- package/dist/cmd/init.js.map +0 -1
- package/dist/cmd/invite.d.ts.map +0 -1
- package/dist/cmd/invite.js.map +0 -1
- package/dist/cmd/join.d.ts.map +0 -1
- package/dist/cmd/join.js.map +0 -1
- package/dist/cmd/login.d.ts.map +0 -1
- package/dist/cmd/login.js.map +0 -1
- package/dist/cmd/mcp.d.ts.map +0 -1
- package/dist/cmd/mcp.js.map +0 -1
- package/dist/cmd/pace.d.ts.map +0 -1
- package/dist/cmd/pace.js.map +0 -1
- package/dist/cmd/parse-share-input.d.ts.map +0 -1
- package/dist/cmd/parse-share-input.js.map +0 -1
- package/dist/cmd/pull.d.ts.map +0 -1
- package/dist/cmd/pull.js.map +0 -1
- package/dist/cmd/push.d.ts.map +0 -1
- package/dist/cmd/push.js.map +0 -1
- package/dist/cmd/resolve.d.ts.map +0 -1
- package/dist/cmd/resolve.js.map +0 -1
- package/dist/cmd/setup.d.ts.map +0 -1
- package/dist/cmd/setup.js.map +0 -1
- package/dist/cmd/status-card.d.ts.map +0 -1
- package/dist/cmd/status-card.js.map +0 -1
- package/dist/cmd/status.d.ts.map +0 -1
- package/dist/cmd/status.js.map +0 -1
- package/dist/cmd/watch.d.ts.map +0 -1
- package/dist/cmd/watch.js.map +0 -1
- package/dist/hosts/claude-code.d.ts.map +0 -1
- package/dist/hosts/claude-code.js.map +0 -1
- package/dist/hosts/claude-desktop.d.ts.map +0 -1
- package/dist/hosts/claude-desktop.js.map +0 -1
- package/dist/hosts/codex.d.ts.map +0 -1
- package/dist/hosts/codex.js.map +0 -1
- package/dist/hosts/shell-env.d.ts.map +0 -1
- package/dist/hosts/shell-env.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/client.d.ts.map +0 -1
- package/dist/lib/client.js.map +0 -1
- package/dist/lib/events/emit.d.ts.map +0 -1
- package/dist/lib/events/emit.js.map +0 -1
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/transport.d.ts.map +0 -1
- package/dist/lib/transport.js.map +0 -1
- package/dist/lib/types/index.d.ts.map +0 -1
- package/dist/lib/types/index.js.map +0 -1
- package/dist/lib/vault/checkpoint.d.ts.map +0 -1
- package/dist/lib/vault/checkpoint.js.map +0 -1
- package/dist/lib/vault/read.d.ts.map +0 -1
- package/dist/lib/vault/read.js.map +0 -1
- package/dist/lib/vault/search.d.ts.map +0 -1
- package/dist/lib/vault/search.js.map +0 -1
- package/dist/pace/device.d.ts.map +0 -1
- package/dist/pace/device.js.map +0 -1
- package/dist/pace/enroll.d.ts.map +0 -1
- package/dist/pace/enroll.js.map +0 -1
- package/dist/pace/identity.d.ts.map +0 -1
- package/dist/pace/identity.js.map +0 -1
- package/dist/pace/install.d.ts.map +0 -1
- package/dist/pace/install.js.map +0 -1
- package/dist/pace/local-evidence.d.ts.map +0 -1
- package/dist/pace/local-evidence.js.map +0 -1
- package/dist/pace/local-sessions.d.ts.map +0 -1
- package/dist/pace/local-sessions.js.map +0 -1
- package/dist/pace/managed-config.d.ts.map +0 -1
- package/dist/pace/managed-config.js.map +0 -1
- package/dist/pace/normalize.d.ts.map +0 -1
- package/dist/pace/normalize.js.map +0 -1
- package/dist/pace/repository.d.ts.map +0 -1
- package/dist/pace/repository.js.map +0 -1
- package/dist/pace/schema.d.ts.map +0 -1
- package/dist/pace/schema.js.map +0 -1
- package/dist/pace/session-summary.d.ts.map +0 -1
- package/dist/pace/session-summary.js.map +0 -1
- package/dist/pace/spool.d.ts.map +0 -1
- package/dist/pace/spool.js.map +0 -1
- package/dist/pace-runtime.d.ts.map +0 -1
- package/dist/pace-runtime.js.map +0 -1
- package/dist/sync/api.d.ts.map +0 -1
- package/dist/sync/api.js.map +0 -1
- package/dist/sync/autoloader.d.ts.map +0 -1
- package/dist/sync/autoloader.js.map +0 -1
- package/dist/sync/conflict.d.ts.map +0 -1
- package/dist/sync/conflict.js.map +0 -1
- package/dist/sync/credentials.d.ts.map +0 -1
- package/dist/sync/credentials.js.map +0 -1
- package/dist/sync/files.d.ts.map +0 -1
- package/dist/sync/files.js.map +0 -1
- package/dist/sync/manifest.d.ts.map +0 -1
- package/dist/sync/manifest.js.map +0 -1
- package/dist/sync/repo-root.d.ts.map +0 -1
- package/dist/sync/repo-root.js.map +0 -1
- package/dist/sync/stitch-claude-md.d.ts.map +0 -1
- package/dist/sync/stitch-claude-md.js.map +0 -1
- package/dist/ui/colors.d.ts.map +0 -1
- package/dist/ui/colors.js.map +0 -1
- package/dist/ui/screens.d.ts.map +0 -1
- package/dist/ui/screens.js.map +0 -1
package/dist/cmd/setup.js
CHANGED
|
@@ -1,345 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { writeShellEnv } from "../hosts/shell-env.js";
|
|
4
|
-
import { configureCodex } from "../hosts/codex.js";
|
|
5
|
-
import { configureClaudeCode } from "../hosts/claude-code.js";
|
|
6
|
-
import { configureClaudeDesktop } from "../hosts/claude-desktop.js";
|
|
7
|
-
import { banner, stepHeader, result, divider, defaultOpts } from "../ui/screens.js";
|
|
8
|
-
import { brand } from "../ui/colors.js";
|
|
9
|
-
const DEFAULT_API = "https://app.unfairly.ai";
|
|
10
|
-
export async function runSetup(opts = {}) {
|
|
11
|
-
const apiUrl = process.env.UNFAIRLY_API_URL ?? DEFAULT_API;
|
|
12
|
-
const ui = defaultOpts();
|
|
13
|
-
const c = brand({ enabled: ui.color });
|
|
14
|
-
const write = (s) => process.stderr.write(`${s}\n`);
|
|
15
|
-
write(banner(ui));
|
|
16
|
-
write("");
|
|
17
|
-
write(`${c.coral("▶")} player 2 booting…`);
|
|
18
|
-
write("");
|
|
19
|
-
// [1/5] AUTH
|
|
20
|
-
write(stepHeader(1, 6, "AUTH", ui));
|
|
21
|
-
write(result("progress", "browser opening — click to authorize", ui));
|
|
22
|
-
const token = await browserAuthFlow(apiUrl, { tokenName: "unfairly setup", org: opts.org });
|
|
23
|
-
if (!token) {
|
|
24
|
-
write(result("err", "browser flow cancelled or timed out. Run `unfairly` again.", ui));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
await setToken(token);
|
|
28
|
-
write(result("ok", `token minted (${token.slice(0, 8)}…) · cached in keychain`, ui));
|
|
29
|
-
write("");
|
|
30
|
-
// [2/5] SHELL ENV
|
|
31
|
-
write(stepHeader(2, 6, "SHELL ENV", ui));
|
|
32
|
-
const env = await writeShellEnv(token);
|
|
33
|
-
if (env.shell === "unknown") {
|
|
34
|
-
write(result("warn", "couldn't detect $SHELL — add manually: export UNFAIRLY_MCP_TOKEN=…", ui));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
write(result("ok", `${env.path} · added UNFAIRLY_MCP_TOKEN`, ui));
|
|
38
|
-
}
|
|
39
|
-
write("");
|
|
40
|
-
// [3/5] CLAUDE DESKTOP
|
|
41
|
-
// We track host-write errors but DON'T early-exit — a user with a malformed
|
|
42
|
-
// Claude Desktop config should still get Claude Code + Codex wired (the
|
|
43
|
-
// partial success path is far better than zero hosts). We exit non-zero
|
|
44
|
-
// at the end of setup so the user knows something needs attention.
|
|
45
|
-
write(stepHeader(3, 6, "CLAUDE DESKTOP", ui));
|
|
46
|
-
const cd = await configureClaudeDesktop(apiUrl, token);
|
|
47
|
-
write(formatClaudeDesktop(cd, ui));
|
|
48
|
-
write("");
|
|
49
|
-
// [4/5] CLAUDE CODE · CODEX
|
|
50
|
-
write(stepHeader(4, 6, "CLAUDE CODE · CODEX", ui));
|
|
51
|
-
const cc = await configureClaudeCode(apiUrl, token);
|
|
52
|
-
write(formatClaudeCode(cc, ui));
|
|
53
|
-
const codex = await configureCodex(apiUrl);
|
|
54
|
-
write(formatCodex(codex, ui));
|
|
55
|
-
write("");
|
|
56
|
-
// [5/5] HANDSHAKE — verify each wired host independently by reading the
|
|
57
|
-
// auth value it will actually send and POSTing through it. Catching this
|
|
58
|
-
// per-host (rather than just trusting the freshly-minted token) is how we
|
|
59
|
-
// make sure host-wiring bugs don't silently ship to users.
|
|
60
|
-
write(stepHeader(5, 6, "HANDSHAKE", ui));
|
|
61
|
-
const hsErrors = [];
|
|
62
|
-
let hsRan = 0;
|
|
63
|
-
if (cd.status === "configured") {
|
|
64
|
-
hsRan++;
|
|
65
|
-
const r = await verifyClaudeDesktopHost(apiUrl);
|
|
66
|
-
write(formatHandshake("claude desktop", r, ui));
|
|
67
|
-
if (!r.ok)
|
|
68
|
-
hsErrors.push(`Claude Desktop: ${r.message}`);
|
|
69
|
-
}
|
|
70
|
-
if (cc.status === "configured") {
|
|
71
|
-
hsRan++;
|
|
72
|
-
const r = await verifyClaudeCodeHost(apiUrl);
|
|
73
|
-
write(formatHandshake("claude code", r, ui));
|
|
74
|
-
if (!r.ok)
|
|
75
|
-
hsErrors.push(`Claude Code: ${r.message}`);
|
|
76
|
-
}
|
|
77
|
-
if (codex.status === "configured") {
|
|
78
|
-
hsRan++;
|
|
79
|
-
const r = await verifyCodexTokenAlive(apiUrl, token);
|
|
80
|
-
write(formatHandshake("codex (token alive)", r, ui));
|
|
81
|
-
if (!r.ok)
|
|
82
|
-
hsErrors.push(`Codex: ${r.message}`);
|
|
83
|
-
}
|
|
84
|
-
if (hsRan === 0) {
|
|
85
|
-
// No hosts were wired — silent step would imply success. Be explicit
|
|
86
|
-
// and fail the run so a misinstall (e.g., Claude Desktop config malformed,
|
|
87
|
-
// Claude Code binary missing, no Codex) doesn't read as "all good."
|
|
88
|
-
write(result("warn", "no hosts wired — nothing to verify. Install Claude Desktop / Code / Codex and re-run `unfairly setup`.", ui));
|
|
89
|
-
process.exit(2);
|
|
90
|
-
}
|
|
91
|
-
if (hsErrors.length > 0) {
|
|
92
|
-
write(result("err", `${hsErrors.length} host(s) failed handshake — see above`, ui));
|
|
93
|
-
process.exit(2);
|
|
94
|
-
}
|
|
95
|
-
write("");
|
|
96
|
-
write(stepHeader(6, 6, "UNFAIRLY INTELLIGENCE", ui));
|
|
97
|
-
try {
|
|
98
|
-
const { runPaceInstall } = await import("./pace.js");
|
|
99
|
-
const intelligence = await runPaceInstall();
|
|
100
|
-
for (const activation of intelligence.activations) {
|
|
101
|
-
write(result(activation.status === "activated" ? "ok" : "skip", `${activation.client} · ${activation.detail}`, ui));
|
|
102
|
-
}
|
|
103
|
-
write(result("ok", "metadata collector enrolled · live verification completes on the first AI session", ui));
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
write(result("err", `collector install failed · ${error.message}`, ui));
|
|
107
|
-
process.exit(2);
|
|
108
|
-
}
|
|
109
|
-
write("");
|
|
110
|
-
write(divider(ui));
|
|
111
|
-
write("");
|
|
112
|
-
// If any host write surfaced a real error (not "not-installed"), warn the
|
|
113
|
-
// user clearly and defer a non-zero exit until after init has had a chance
|
|
114
|
-
// to run — that way they at least get a wired token + workspace bind even
|
|
115
|
-
// if one host is busted. The exit happens at the very end of runSetup.
|
|
116
|
-
const hostErrors = [];
|
|
117
|
-
if (cd.status === "error")
|
|
118
|
-
hostErrors.push(`Claude Desktop: ${cd.message}`);
|
|
119
|
-
if (cc.status === "error")
|
|
120
|
-
hostErrors.push(`Claude Code: ${cc.message}`);
|
|
121
|
-
if (codex.status === "error")
|
|
122
|
-
hostErrors.push(`Codex: ${codex.message}`);
|
|
123
|
-
if (hostErrors.length > 0) {
|
|
124
|
-
write(result("warn", `${hostErrors.length} host(s) failed to wire — fix and re-run \`unfairly setup\`:`, ui));
|
|
125
|
-
for (const m of hostErrors)
|
|
126
|
-
write(result("warn", ` ${m}`, ui));
|
|
127
|
-
write("");
|
|
128
|
-
}
|
|
129
|
-
// Chain into init when caller asked AND cwd is a repo.
|
|
130
|
-
let workspaceBound = null;
|
|
131
|
-
if (opts.chainInit) {
|
|
132
|
-
const { findRepoRoot } = await import("../sync/repo-root.js");
|
|
133
|
-
const repoRoot = await findRepoRoot(process.cwd());
|
|
134
|
-
if (repoRoot) {
|
|
135
|
-
write(`${c.coral("▶")} repo detected: ${c.dim(repoRoot)}`);
|
|
136
|
-
let shouldBind = Boolean(opts.yes);
|
|
137
|
-
if (!opts.yes) {
|
|
138
|
-
const { createInterface } = await import("node:readline/promises");
|
|
139
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
140
|
-
const ans = await rl.question(` ${c.dim("◆")} Bind .unfairly/ here? [Y/n] `);
|
|
141
|
-
rl.close();
|
|
142
|
-
const v = ans.trim().toLowerCase();
|
|
143
|
-
shouldBind = v === "" || v === "y" || v === "yes";
|
|
144
|
-
}
|
|
145
|
-
if (shouldBind) {
|
|
146
|
-
const { runInit } = await import("./init.js");
|
|
147
|
-
await runInit({ assumeAuthed: true, yes: true, token });
|
|
148
|
-
try {
|
|
149
|
-
const { readManifest } = await import("../sync/manifest.js");
|
|
150
|
-
const m = await readManifest(process.cwd());
|
|
151
|
-
if (m) {
|
|
152
|
-
const vaultCount = Object.keys(m.vaults).length;
|
|
153
|
-
const docCount = Object.values(m.vaults).reduce((s, v) => s + Object.keys(v.docs).length, 0);
|
|
154
|
-
workspaceBound = { vaultCount, docCount };
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
// best-effort summary
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
write(result("skip", "skipped workspace bind — re-run `unfairly` in any repo to bind later", ui));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Hand-off card
|
|
167
|
-
const { handoffCard } = await import("../ui/screens.js");
|
|
168
|
-
const card = handoffCard({
|
|
169
|
-
vaultCount: workspaceBound?.vaultCount ?? 0,
|
|
170
|
-
docCount: workspaceBound?.docCount ?? 0,
|
|
171
|
-
recentMove: null,
|
|
172
|
-
hint: "open Claude Desktop — say anything",
|
|
173
|
-
}, ui);
|
|
174
|
-
write(card);
|
|
175
|
-
write("");
|
|
176
|
-
write(` ${c.dim("re-run")} ${c.bold("unfairly")} ${c.dim("anytime to see what your team's up to.")}`);
|
|
177
|
-
// Deferred non-zero exit if any host failed to write. The user got the
|
|
178
|
-
// partial-success card with errors listed above; CI/scripts can still
|
|
179
|
-
// detect the failure via the exit code.
|
|
180
|
-
if (hostErrors.length > 0) {
|
|
181
|
-
process.exit(2);
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
ok: true,
|
|
185
|
-
token,
|
|
186
|
-
hosts: { claudeDesktop: cd, claudeCode: cc, codex },
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
// --- formatters ---
|
|
190
|
-
function formatClaudeDesktop(r, ui) {
|
|
191
|
-
switch (r.status) {
|
|
192
|
-
case "configured":
|
|
193
|
-
return result("ok", `${r.path}`, ui);
|
|
194
|
-
case "not-installed":
|
|
195
|
-
return result("skip", `Claude Desktop not detected (${r.path}) — install and re-run`, ui);
|
|
196
|
-
case "error":
|
|
197
|
-
return result("err", `${r.path} — ${r.message}`, ui);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
function formatClaudeCode(r, ui) {
|
|
201
|
-
switch (r.status) {
|
|
202
|
-
case "configured":
|
|
203
|
-
return result("ok", `${r.path}`, ui);
|
|
204
|
-
case "not-installed":
|
|
205
|
-
return result("skip", "Claude Code (`claude` binary) not detected", ui);
|
|
206
|
-
case "error":
|
|
207
|
-
return result("err", r.message, ui);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
function formatCodex(r, ui) {
|
|
211
|
-
switch (r.status) {
|
|
212
|
-
case "configured":
|
|
213
|
-
return result("ok", `${r.path}`, ui);
|
|
214
|
-
case "not-installed":
|
|
215
|
-
return result("skip", `Codex not detected at ${r.path}`, ui);
|
|
216
|
-
case "error":
|
|
217
|
-
return result("err", `${r.path} — ${r.message}`, ui);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function formatHandshake(host, r, ui) {
|
|
221
|
-
if (r.ok)
|
|
222
|
-
return result("ok", `${host} · /api/mcp accepted the auth`, ui);
|
|
223
|
-
return result("err", `${host} · ${r.message}`, ui);
|
|
224
|
-
}
|
|
225
|
-
/** POST `initialize` to /api/mcp with the given Authorization header. */
|
|
226
|
-
async function pingMcp(apiUrl, authorization) {
|
|
227
|
-
const url = `${apiUrl.replace(/\/$/, "")}/api/mcp`;
|
|
228
|
-
try {
|
|
229
|
-
const res = await fetch(url, {
|
|
230
|
-
method: "POST",
|
|
231
|
-
headers: {
|
|
232
|
-
Authorization: authorization,
|
|
233
|
-
"Content-Type": "application/json",
|
|
234
|
-
Accept: "application/json, text/event-stream",
|
|
235
|
-
},
|
|
236
|
-
body: JSON.stringify({
|
|
237
|
-
jsonrpc: "2.0",
|
|
238
|
-
id: 1,
|
|
239
|
-
method: "initialize",
|
|
240
|
-
params: {
|
|
241
|
-
protocolVersion: "2024-11-05",
|
|
242
|
-
capabilities: {},
|
|
243
|
-
clientInfo: { name: "unfairly-setup", version: "0.6.1" },
|
|
244
|
-
},
|
|
245
|
-
}),
|
|
246
|
-
});
|
|
247
|
-
if (res.status !== 200)
|
|
248
|
-
return { ok: false, message: `HTTP ${res.status}` };
|
|
249
|
-
const text = await res.text();
|
|
250
|
-
if (!text.includes('"result"') || !text.includes("protocolVersion")) {
|
|
251
|
-
return { ok: false, message: "unexpected response shape" };
|
|
252
|
-
}
|
|
253
|
-
return { ok: true };
|
|
254
|
-
}
|
|
255
|
-
catch (e) {
|
|
256
|
-
return { ok: false, message: e.message };
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Re-read Claude Desktop's config and replay the Authorization header it will
|
|
261
|
-
* actually send. Catches bugs like literal `${UNFAIRLY_MCP_TOKEN}` placeholder
|
|
262
|
-
* (macOS GUI apps don't inherit `~/.zshenv` so the env interpolation fails).
|
|
263
|
-
*/
|
|
264
|
-
async function verifyClaudeDesktopHost(apiUrl) {
|
|
265
|
-
const { configPath } = await import("../hosts/claude-desktop.js");
|
|
266
|
-
const fs = await import("node:fs/promises");
|
|
267
|
-
const cfgPath = configPath();
|
|
268
|
-
if (!cfgPath)
|
|
269
|
-
return { ok: false, message: "config path unavailable" };
|
|
270
|
-
let raw;
|
|
271
|
-
try {
|
|
272
|
-
raw = await fs.readFile(cfgPath, "utf8");
|
|
273
|
-
}
|
|
274
|
-
catch (e) {
|
|
275
|
-
return { ok: false, message: `read failed: ${e.message}` };
|
|
276
|
-
}
|
|
277
|
-
let cfg;
|
|
278
|
-
try {
|
|
279
|
-
cfg = JSON.parse(raw);
|
|
280
|
-
}
|
|
281
|
-
catch (e) {
|
|
282
|
-
return { ok: false, message: `parse failed: ${e.message}` };
|
|
283
|
-
}
|
|
284
|
-
const auth = cfg.mcpServers?.unfairly?.headers?.Authorization;
|
|
285
|
-
if (!auth)
|
|
286
|
-
return { ok: false, message: "no Authorization header in wired entry" };
|
|
287
|
-
if (auth.includes("${")) {
|
|
288
|
-
return {
|
|
289
|
-
ok: false,
|
|
290
|
-
message: "config still uses ${VAR} placeholder — re-run setup",
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
return pingMcp(apiUrl, auth);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Re-read Claude Code's user-level config (`~/.claude.json`) and replay the
|
|
297
|
-
* `unfairly` entry's Authorization header. We write to the root `mcpServers`
|
|
298
|
-
* (user scope) — the root entry is the source of truth. We still check
|
|
299
|
-
* `projects[<cwd>]` as a defensive fallback so an older install that landed
|
|
300
|
-
* in project-local scope doesn't silently fail this handshake.
|
|
301
|
-
*/
|
|
302
|
-
async function verifyClaudeCodeHost(apiUrl) {
|
|
303
|
-
const { configPath } = await import("../hosts/claude-code.js");
|
|
304
|
-
const fs = await import("node:fs/promises");
|
|
305
|
-
const cfgPath = configPath();
|
|
306
|
-
let raw;
|
|
307
|
-
try {
|
|
308
|
-
raw = await fs.readFile(cfgPath, "utf8");
|
|
309
|
-
}
|
|
310
|
-
catch (e) {
|
|
311
|
-
return { ok: false, message: `read failed: ${e.message}` };
|
|
312
|
-
}
|
|
313
|
-
let cfg;
|
|
314
|
-
try {
|
|
315
|
-
cfg = JSON.parse(raw);
|
|
316
|
-
}
|
|
317
|
-
catch (e) {
|
|
318
|
-
return { ok: false, message: `parse failed: ${e.message}` };
|
|
319
|
-
}
|
|
320
|
-
const rootEntry = cfg.mcpServers?.unfairly;
|
|
321
|
-
const projectEntry = cfg.projects?.[process.cwd()]?.mcpServers?.unfairly;
|
|
322
|
-
const auth = (rootEntry ?? projectEntry)?.headers?.Authorization;
|
|
323
|
-
if (!auth) {
|
|
324
|
-
return {
|
|
325
|
-
ok: false,
|
|
326
|
-
message: "no Authorization header on unfairly entry — re-run setup",
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
return pingMcp(apiUrl, auth);
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Verify the just-minted token is alive on /api/mcp. Codex reads its bearer
|
|
333
|
-
* from the env var declared in `~/.codex/config.toml` (we set
|
|
334
|
-
* `bearer_token_env_var = "UNFAIRLY_MCP_TOKEN"`), and a *fresh login shell*
|
|
335
|
-
* will pick that up from `~/.zshenv`. We can't faithfully reproduce that
|
|
336
|
-
* fresh-shell env from inside this Node process (`urun`/Conductor/etc. may
|
|
337
|
-
* have shaded the var), so we deliberately check only that the token we
|
|
338
|
-
* just wrote to disk in step [2/5] is accepted by the server. Catches a
|
|
339
|
-
* revoked/expired/wrong token without pretending to verify Codex's runtime
|
|
340
|
-
* resolution chain.
|
|
341
|
-
*/
|
|
342
|
-
async function verifyCodexTokenAlive(apiUrl, token) {
|
|
343
|
-
return pingMcp(apiUrl, `Bearer ${token}`);
|
|
344
|
-
}
|
|
345
|
-
//# sourceMappingURL=setup.js.map
|
|
1
|
+
import{browserAuthFlow as N}from"../auth/browser-flow.js";import{setToken as O}from"../sync/credentials.js";import{writeShellEnv as S}from"../hosts/shell-env.js";import{configureCodex as P}from"../hosts/codex.js";import{configureClaudeCode as T}from"../hosts/claude-code.js";import{configureClaudeDesktop as R}from"../hosts/claude-desktop.js";import{banner as H,stepHeader as m,result as s,divider as U,defaultOpts as j}from"../ui/screens.js";import{brand as F}from"../ui/colors.js";const z="https://app.unfairly.ai";async function te(a={}){const o=process.env.UNFAIRLY_API_URL??z,t=j(),n=F({enabled:t.color}),e=r=>process.stderr.write(`${r}
|
|
2
|
+
`);e(H(t)),e(""),e(`${n.coral("\u25B6")} player 2 booting\u2026`),e(""),e(m(1,6,"AUTH",t)),e(s("progress","browser opening \u2014 click to authorize",t));const i=await N(o,{tokenName:"unfairly setup",org:a.org});i||(e(s("err","browser flow cancelled or timed out. Run `unfairly` again.",t)),process.exit(1)),await O(i),e(s("ok",`token minted (${i.slice(0,8)}\u2026) \xB7 cached in keychain`,t)),e(""),e(m(2,6,"SHELL ENV",t));const l=await S(i);l.shell==="unknown"?e(s("warn","couldn't detect $SHELL \u2014 add manually: export UNFAIRLY_MCP_TOKEN=\u2026",t)):e(s("ok",`${l.path} \xB7 added UNFAIRLY_MCP_TOKEN`,t)),e(""),e(m(3,6,"CLAUDE DESKTOP",t));const c=await R(o,i);e(_(c,t)),e(""),e(m(4,6,"CLAUDE CODE \xB7 CODEX",t));const d=await T(o,i);e(B(d,t));const u=await P(o);e(M(u,t)),e(""),e(m(5,6,"HANDSHAKE",t));const h=[];let w=0;if(c.status==="configured"){w++;const r=await Y(o);e(A("claude desktop",r,t)),r.ok||h.push(`Claude Desktop: ${r.message}`)}if(d.status==="configured"){w++;const r=await K(o);e(A("claude code",r,t)),r.ok||h.push(`Claude Code: ${r.message}`)}if(u.status==="configured"){w++;const r=await V(o,i);e(A("codex (token alive)",r,t)),r.ok||h.push(`Codex: ${r.message}`)}w===0&&(e(s("warn","no hosts wired \u2014 nothing to verify. Install Claude Desktop / Code / Codex and re-run `unfairly setup`.",t)),process.exit(2)),h.length>0&&(e(s("err",`${h.length} host(s) failed handshake \u2014 see above`,t)),process.exit(2)),e(""),e(m(6,6,"UNFAIRLY INTELLIGENCE",t));try{const{runPaceInstall:r}=await import("./pace.js"),y=await r();for(const p of y.activations)e(s(p.status==="activated"?"ok":"skip",`${p.client} \xB7 ${p.detail}`,t));e(s("ok","metadata collector enrolled \xB7 live verification completes on the first AI session",t))}catch(r){e(s("err",`collector install failed \xB7 ${r.message}`,t)),process.exit(2)}e(""),e(U(t)),e("");const f=[];if(c.status==="error"&&f.push(`Claude Desktop: ${c.message}`),d.status==="error"&&f.push(`Claude Code: ${d.message}`),u.status==="error"&&f.push(`Codex: ${u.message}`),f.length>0){e(s("warn",`${f.length} host(s) failed to wire \u2014 fix and re-run \`unfairly setup\`:`,t));for(const r of f)e(s("warn",` ${r}`,t));e("")}let $=null;if(a.chainInit){const{findRepoRoot:r}=await import("../sync/repo-root.js"),y=await r(process.cwd());if(y){e(`${n.coral("\u25B6")} repo detected: ${n.dim(y)}`);let p=!!a.yes;if(!a.yes){const{createInterface:v}=await import("node:readline/promises"),C=v({input:process.stdin,output:process.stdout}),g=await C.question(` ${n.dim("\u25C6")} Bind .unfairly/ here? [Y/n] `);C.close();const k=g.trim().toLowerCase();p=k===""||k==="y"||k==="yes"}if(p){const{runInit:v}=await import("./init.js");await v({assumeAuthed:!0,yes:!0,token:i});try{const{readManifest:C}=await import("../sync/manifest.js"),g=await C(process.cwd());if(g){const k=Object.keys(g.vaults).length,D=Object.values(g.vaults).reduce((I,L)=>I+Object.keys(L.docs).length,0);$={vaultCount:k,docCount:D}}}catch{}}else e(s("skip","skipped workspace bind \u2014 re-run `unfairly` in any repo to bind later",t))}}const{handoffCard:E}=await import("../ui/screens.js"),b=E({vaultCount:$?.vaultCount??0,docCount:$?.docCount??0,recentMove:null,hint:"open Claude Desktop \u2014 say anything"},t);return e(b),e(""),e(` ${n.dim("re-run")} ${n.bold("unfairly")} ${n.dim("anytime to see what your team's up to.")}`),f.length>0&&process.exit(2),{ok:!0,token:i,hosts:{claudeDesktop:c,claudeCode:d,codex:u}}}function _(a,o){switch(a.status){case"configured":return s("ok",`${a.path}`,o);case"not-installed":return s("skip",`Claude Desktop not detected (${a.path}) \u2014 install and re-run`,o);case"error":return s("err",`${a.path} \u2014 ${a.message}`,o)}}function B(a,o){switch(a.status){case"configured":return s("ok",`${a.path}`,o);case"not-installed":return s("skip","Claude Code (`claude` binary) not detected",o);case"error":return s("err",a.message,o)}}function M(a,o){switch(a.status){case"configured":return s("ok",`${a.path}`,o);case"not-installed":return s("skip",`Codex not detected at ${a.path}`,o);case"error":return s("err",`${a.path} \u2014 ${a.message}`,o)}}function A(a,o,t){return o.ok?s("ok",`${a} \xB7 /api/mcp accepted the auth`,t):s("err",`${a} \xB7 ${o.message}`,t)}async function x(a,o){const t=`${a.replace(/\/$/,"")}/api/mcp`;try{const n=await fetch(t,{method:"POST",headers:{Authorization:o,"Content-Type":"application/json",Accept:"application/json, text/event-stream"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"unfairly-setup",version:"0.6.1"}}})});if(n.status!==200)return{ok:!1,message:`HTTP ${n.status}`};const e=await n.text();return!e.includes('"result"')||!e.includes("protocolVersion")?{ok:!1,message:"unexpected response shape"}:{ok:!0}}catch(n){return{ok:!1,message:n.message}}}async function Y(a){const{configPath:o}=await import("../hosts/claude-desktop.js"),t=await import("node:fs/promises"),n=o();if(!n)return{ok:!1,message:"config path unavailable"};let e;try{e=await t.readFile(n,"utf8")}catch(c){return{ok:!1,message:`read failed: ${c.message}`}}let i;try{i=JSON.parse(e)}catch(c){return{ok:!1,message:`parse failed: ${c.message}`}}const l=i.mcpServers?.unfairly?.headers?.Authorization;return l?l.includes("${")?{ok:!1,message:"config still uses ${VAR} placeholder \u2014 re-run setup"}:x(a,l):{ok:!1,message:"no Authorization header in wired entry"}}async function K(a){const{configPath:o}=await import("../hosts/claude-code.js"),t=await import("node:fs/promises"),n=o();let e;try{e=await t.readFile(n,"utf8")}catch(u){return{ok:!1,message:`read failed: ${u.message}`}}let i;try{i=JSON.parse(e)}catch(u){return{ok:!1,message:`parse failed: ${u.message}`}}const l=i.mcpServers?.unfairly,c=i.projects?.[process.cwd()]?.mcpServers?.unfairly,d=(l??c)?.headers?.Authorization;return d?x(a,d):{ok:!1,message:"no Authorization header on unfairly entry \u2014 re-run setup"}}async function V(a,o){return x(a,`Bearer ${o}`)}export{te as runSetup};
|
package/dist/cmd/status-card.js
CHANGED
|
@@ -1,156 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import { readManifest } from "../sync/manifest.js";
|
|
5
|
-
import { getToken } from "../sync/credentials.js";
|
|
6
|
-
import { configPath as claudeDesktopPath } from "../hosts/claude-desktop.js";
|
|
7
|
-
import { banner, statusCard, defaultOpts } from "../ui/screens.js";
|
|
8
|
-
const DEFAULT_API = "https://app.unfairly.ai";
|
|
9
|
-
export async function runStatusCard() {
|
|
10
|
-
const cwd = process.cwd();
|
|
11
|
-
const ui = defaultOpts();
|
|
12
|
-
const write = (s) => process.stdout.write(`${s}\n`);
|
|
13
|
-
const apiUrl = process.env.UNFAIRLY_API_URL ?? DEFAULT_API;
|
|
14
|
-
const hosts = {
|
|
15
|
-
claudeDesktop: await checkClaudeDesktop(),
|
|
16
|
-
claudeCode: await checkClaudeCode(),
|
|
17
|
-
codex: await checkCodex(),
|
|
18
|
-
};
|
|
19
|
-
const manifest = await readManifest(cwd);
|
|
20
|
-
const workspace = manifest
|
|
21
|
-
? {
|
|
22
|
-
cwd,
|
|
23
|
-
vaultCount: Object.keys(manifest.vaults).length,
|
|
24
|
-
docCount: Object.values(manifest.vaults).reduce((s, v) => s + Object.keys(v.docs).length, 0),
|
|
25
|
-
lastPulledAt: latestPullTimestamp(manifest),
|
|
26
|
-
}
|
|
27
|
-
: null;
|
|
28
|
-
// Recent moves: best-effort enter_org. Silent on failure.
|
|
29
|
-
const recent = await fetchRecentMoves(apiUrl).catch(() => []);
|
|
30
|
-
const state = { hosts, workspace, recent };
|
|
31
|
-
write(banner(ui));
|
|
32
|
-
write("");
|
|
33
|
-
write(statusCard(state, ui));
|
|
34
|
-
write("");
|
|
35
|
-
}
|
|
36
|
-
async function checkClaudeDesktop() {
|
|
37
|
-
// "wired" requires the full canonical block: type=http, url ending in
|
|
38
|
-
// /api/mcp, and a bearer-style Authorization header. A stale entry with
|
|
39
|
-
// the right URL but wrong transport or missing auth is "rotted", not wired.
|
|
40
|
-
const p = claudeDesktopPath();
|
|
41
|
-
if (!p)
|
|
42
|
-
return "missing";
|
|
43
|
-
try {
|
|
44
|
-
const raw = await fs.readFile(p, "utf8");
|
|
45
|
-
const parsed = JSON.parse(raw);
|
|
46
|
-
const u = parsed?.mcpServers?.unfairly;
|
|
47
|
-
if (!u)
|
|
48
|
-
return "missing";
|
|
49
|
-
const typeOk = u.type === "http";
|
|
50
|
-
const urlOk = typeof u.url === "string" && u.url.endsWith("/api/mcp");
|
|
51
|
-
const auth = u.headers?.Authorization;
|
|
52
|
-
const authOk = typeof auth === "string" && /^Bearer\s+\S+/.test(auth);
|
|
53
|
-
if (typeOk && urlOk && authOk)
|
|
54
|
-
return "wired";
|
|
55
|
-
return "rotted";
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return "missing";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Claude Code stores MCP servers in a per-user JSON config.
|
|
63
|
-
* We do NOT shell out to the claude binary — file inspection is faster and
|
|
64
|
-
* does not depend on the claude binary being on PATH.
|
|
65
|
-
*/
|
|
66
|
-
async function checkClaudeCode() {
|
|
67
|
-
// Inspect only:
|
|
68
|
-
// 1. Global mcpServers.unfairly (machine-wide registration)
|
|
69
|
-
// 2. projects[cwd].mcpServers.unfairly (this-workspace registration)
|
|
70
|
-
// Do NOT walk every project entry — a sibling project's "unfairly" entry
|
|
71
|
-
// is not evidence the current workspace is wired.
|
|
72
|
-
const home = os.homedir();
|
|
73
|
-
const cwd = process.cwd();
|
|
74
|
-
const candidates = [
|
|
75
|
-
path.join(home, ".claude.json"),
|
|
76
|
-
path.join(home, ".config", "claude", "config.json"),
|
|
77
|
-
];
|
|
78
|
-
for (const p of candidates) {
|
|
79
|
-
try {
|
|
80
|
-
const raw = await fs.readFile(p, "utf8");
|
|
81
|
-
const parsed = JSON.parse(raw);
|
|
82
|
-
// Global scope
|
|
83
|
-
if (parsed?.mcpServers?.unfairly)
|
|
84
|
-
return "wired";
|
|
85
|
-
// Workspace scope: keyed by absolute path
|
|
86
|
-
const workspace = parsed?.projects?.[cwd];
|
|
87
|
-
if (workspace?.mcpServers?.unfairly)
|
|
88
|
-
return "wired";
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// file missing or unparseable; try next candidate
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return "missing";
|
|
95
|
-
}
|
|
96
|
-
async function checkCodex() {
|
|
97
|
-
const p = path.join(os.homedir(), ".codex", "config.toml");
|
|
98
|
-
try {
|
|
99
|
-
const raw = await fs.readFile(p, "utf8");
|
|
100
|
-
return /^\[mcp_servers\.unfairly\]/m.test(raw) ? "wired" : "missing";
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return "missing";
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function latestPullTimestamp(manifest) {
|
|
107
|
-
let latest = null;
|
|
108
|
-
for (const v of Object.values(manifest.vaults)) {
|
|
109
|
-
if (v.last_pulled_at && (!latest || v.last_pulled_at > latest))
|
|
110
|
-
latest = v.last_pulled_at;
|
|
111
|
-
}
|
|
112
|
-
return latest;
|
|
113
|
-
}
|
|
114
|
-
async function fetchRecentMoves(apiUrl) {
|
|
115
|
-
const token = await getToken();
|
|
116
|
-
if (!token)
|
|
117
|
-
return [];
|
|
118
|
-
const url = `${apiUrl.replace(/\/$/, "")}/api/mcp`;
|
|
119
|
-
try {
|
|
120
|
-
const res = await fetch(url, {
|
|
121
|
-
method: "POST",
|
|
122
|
-
headers: {
|
|
123
|
-
Authorization: `Bearer ${token}`,
|
|
124
|
-
"Content-Type": "application/json",
|
|
125
|
-
Accept: "application/json, text/event-stream",
|
|
126
|
-
},
|
|
127
|
-
body: JSON.stringify({
|
|
128
|
-
jsonrpc: "2.0",
|
|
129
|
-
id: 1,
|
|
130
|
-
method: "tools/call",
|
|
131
|
-
params: { name: "enter_org", arguments: {} },
|
|
132
|
-
}),
|
|
133
|
-
});
|
|
134
|
-
if (res.status !== 200)
|
|
135
|
-
return [];
|
|
136
|
-
const text = await res.text();
|
|
137
|
-
// The response is text-formatted. Parse the "RECENT MOVES" block lines like:
|
|
138
|
-
// **bob** · `unfairly-growth` · 1m ago · other
|
|
139
|
-
const moves = [];
|
|
140
|
-
const re = /\*\*(?<who>[^*]+)\*\*\s*·\s*`(?<room>[^`]+)`\s*·\s*(?<ago>[^·\n]+)·/g;
|
|
141
|
-
let m;
|
|
142
|
-
while ((m = re.exec(text)) && moves.length < 5) {
|
|
143
|
-
moves.push({
|
|
144
|
-
who: m.groups.who.trim(),
|
|
145
|
-
room: m.groups.room.trim(),
|
|
146
|
-
ago: m.groups.ago.trim(),
|
|
147
|
-
title: "—",
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return moves;
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return [];
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
//# sourceMappingURL=status-card.js.map
|
|
1
|
+
import{promises as u}from"node:fs";import p from"node:path";import l from"node:os";import{readManifest as f}from"../sync/manifest.js";import{getToken as h}from"../sync/credentials.js";import{configPath as w}from"../hosts/claude-desktop.js";import{banner as g,statusCard as y,defaultOpts as k}from"../ui/screens.js";const v="https://app.unfairly.ai";async function N(){const e=process.cwd(),t=k(),s=i=>process.stdout.write(`${i}
|
|
2
|
+
`),r=process.env.UNFAIRLY_API_URL??v,a={claudeDesktop:await j(),claudeCode:await O(),codex:await _()},o=await f(e),c=o?{cwd:e,vaultCount:Object.keys(o.vaults).length,docCount:Object.values(o.vaults).reduce((i,m)=>i+Object.keys(m.docs).length,0),lastPulledAt:C(o)}:null,n=await S(r).catch(()=>[]),d={hosts:a,workspace:c,recent:n};s(g(t)),s(""),s(y(d,t)),s("")}async function j(){const e=w();if(!e)return"missing";try{const t=await u.readFile(e,"utf8"),r=JSON.parse(t)?.mcpServers?.unfairly;if(!r)return"missing";const a=r.type==="http",o=typeof r.url=="string"&&r.url.endsWith("/api/mcp"),c=r.headers?.Authorization,n=typeof c=="string"&&/^Bearer\s+\S+/.test(c);return a&&o&&n?"wired":"rotted"}catch{return"missing"}}async function O(){const e=l.homedir(),t=process.cwd(),s=[p.join(e,".claude.json"),p.join(e,".config","claude","config.json")];for(const r of s)try{const a=await u.readFile(r,"utf8"),o=JSON.parse(a);if(o?.mcpServers?.unfairly||o?.projects?.[t]?.mcpServers?.unfairly)return"wired"}catch{}return"missing"}async function _(){const e=p.join(l.homedir(),".codex","config.toml");try{const t=await u.readFile(e,"utf8");return/^\[mcp_servers\.unfairly\]/m.test(t)?"wired":"missing"}catch{return"missing"}}function C(e){let t=null;for(const s of Object.values(e.vaults))s.last_pulled_at&&(!t||s.last_pulled_at>t)&&(t=s.last_pulled_at);return t}async function S(e){const t=await h();if(!t)return[];const s=`${e.replace(/\/$/,"")}/api/mcp`;try{const r=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json",Accept:"application/json, text/event-stream"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"tools/call",params:{name:"enter_org",arguments:{}}})});if(r.status!==200)return[];const a=await r.text(),o=[],c=/\*\*(?<who>[^*]+)\*\*\s*·\s*`(?<room>[^`]+)`\s*·\s*(?<ago>[^·\n]+)·/g;let n;for(;(n=c.exec(a))&&o.length<5;)o.push({who:n.groups.who.trim(),room:n.groups.room.trim(),ago:n.groups.ago.trim(),title:"\u2014"});return o}catch{return[]}}export{N as runStatusCard};
|
package/dist/cmd/status.d.ts
CHANGED
package/dist/cmd/status.js
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { banner, defaultOpts } from "../ui/screens.js";
|
|
5
|
-
export async function runStatus() {
|
|
6
|
-
const ui = defaultOpts();
|
|
7
|
-
process.stdout.write(`${banner(ui)}\n\n`);
|
|
8
|
-
const cwd = process.cwd();
|
|
9
|
-
const manifest = await readManifest(cwd);
|
|
10
|
-
if (!manifest) {
|
|
11
|
-
console.error("No .unfairly/ found. Run `unfairly init` first.");
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
for (const [slug, v] of Object.entries(manifest.vaults)) {
|
|
15
|
-
let modified = 0;
|
|
16
|
-
for (const [, entry] of Object.entries(v.docs)) {
|
|
17
|
-
try {
|
|
18
|
-
const raw = await readDocRaw(path.join(cwd, ".unfairly", entry.path));
|
|
19
|
-
if (computeSha(raw) !== entry.sha)
|
|
20
|
-
modified++;
|
|
21
|
-
}
|
|
22
|
-
catch { }
|
|
23
|
-
}
|
|
24
|
-
const pulled = v.last_pulled_at ?? "never";
|
|
25
|
-
const pushed = v.last_pushed_at ?? "never";
|
|
26
|
-
console.log(`${slug.padEnd(20)} pulled ${pulled} · pushed ${pushed} · modified ${modified}`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=status.js.map
|
|
1
|
+
import d from"node:path";import{readManifest as u}from"../sync/manifest.js";import{computeSha as p,readDocRaw as l}from"../sync/files.js";import{banner as m,defaultOpts as h}from"../ui/screens.js";async function _(){const r=h();process.stdout.write(`${m(r)}
|
|
2
|
+
|
|
3
|
+
`);const o=process.cwd(),e=await u(o);e||(console.error("No .unfairly/ found. Run `unfairly init` first."),process.exit(1));for(const[a,t]of Object.entries(e.vaults)){let s=0;for(const[,n]of Object.entries(t.docs))try{const f=await l(d.join(o,".unfairly",n.path));p(f)!==n.sha&&s++}catch{}const i=t.last_pulled_at??"never",c=t.last_pushed_at??"never";console.log(`${a.padEnd(20)} pulled ${i} \xB7 pushed ${c} \xB7 modified ${s}`)}}export{_ as runStatus};
|
package/dist/cmd/watch.d.ts
CHANGED
package/dist/cmd/watch.js
CHANGED
|
@@ -1,26 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import chokidar from "chokidar";
|
|
3
|
-
import { runPush } from "./push.js";
|
|
4
|
-
export async function runWatch() {
|
|
5
|
-
const cwd = process.cwd();
|
|
6
|
-
const dir = path.join(cwd, ".unfairly");
|
|
7
|
-
console.log(`watching ${dir} (Ctrl+C to stop)`);
|
|
8
|
-
let timer = null;
|
|
9
|
-
const debouncePush = () => {
|
|
10
|
-
if (timer)
|
|
11
|
-
clearTimeout(timer);
|
|
12
|
-
timer = setTimeout(() => {
|
|
13
|
-
runPush(undefined).catch((e) => console.error("push failed:", e.message));
|
|
14
|
-
}, 5000);
|
|
15
|
-
};
|
|
16
|
-
chokidar
|
|
17
|
-
.watch(dir, {
|
|
18
|
-
ignored: [/(^|[\/\\])\../, /manifest\.json$/, /\.tmp\./],
|
|
19
|
-
ignoreInitial: true,
|
|
20
|
-
persistent: true,
|
|
21
|
-
})
|
|
22
|
-
.on("change", debouncePush)
|
|
23
|
-
.on("add", debouncePush);
|
|
24
|
-
await new Promise(() => { }); // keep alive until SIGINT
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=watch.js.map
|
|
1
|
+
import i from"node:path";import s from"chokidar";import{runPush as c}from"./push.js";async function d(){const n=process.cwd(),t=i.join(n,".unfairly");console.log(`watching ${t} (Ctrl+C to stop)`);let o=null;const e=()=>{o&&clearTimeout(o),o=setTimeout(()=>{c(void 0).catch(r=>console.error("push failed:",r.message))},5e3)};s.watch(t,{ignored:[/(^|[\/\\])\../,/manifest\.json$/,/\.tmp\./],ignoreInitial:!0,persistent:!0}).on("change",e).on("add",e),await new Promise(()=>{})}export{d as runWatch};
|