@ulpi/cli 0.1.5 → 0.1.7
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/README.md +143 -214
- package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
- package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
- package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
- package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
- package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
- package/dist/chunk-4XTHZVDS.js +109 -0
- package/dist/chunk-4ZPOZULQ.js +6522 -0
- package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
- package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
- package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
- package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
- package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
- package/dist/chunk-C7CLUQI6.js +1286 -0
- package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
- package/dist/chunk-EJ7TW77N.js +1418 -0
- package/dist/{chunk-P2RESJRN.js → chunk-EWLYVXQ4.js} +2 -2
- package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
- package/dist/chunk-IZPJHSPX.js +1478 -0
- package/dist/chunk-JLHNLM3C.js +228 -0
- package/dist/chunk-PO4NUZUU.js +147 -0
- package/dist/chunk-S6ANCSYO.js +1271 -0
- package/dist/chunk-SEU7WWNQ.js +1251 -0
- package/dist/chunk-SNQ7NAIS.js +453 -0
- package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
- package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
- package/dist/chunk-WED4LM5N.js +322 -0
- package/dist/chunk-WVOZE25N.js +6757 -0
- package/dist/{chunk-5SCG7UYM.js → chunk-XKF4DPUM.js} +7 -7
- package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
- package/dist/chunk-Z53CAR7G.js +298 -0
- package/dist/{ci-JQ56YIKC.js → ci-COZRTPGQ.js} +124 -26
- package/dist/cloud-2F3NLVHN.js +274 -0
- package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
- package/dist/codex-MB5YTMRT.js +132 -0
- package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
- package/dist/dist-2BJYR5EI.js +59 -0
- package/dist/dist-3EIQTZHT.js +1380 -0
- package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
- package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
- package/dist/dist-6M4MZWZW.js +58 -0
- package/dist/dist-6X576SU2.js +27 -0
- package/dist/dist-7QOEYLFX.js +103 -0
- package/dist/dist-AYBGHEDY.js +2541 -0
- package/dist/dist-EK45QNEM.js +45 -0
- package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
- package/dist/dist-GTEJUBBT.js +66 -0
- package/dist/dist-HA74OKJZ.js +40 -0
- package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
- package/dist/dist-IYE3OBRB.js +374 -0
- package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
- package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
- package/dist/dist-NUEMFZFL.js +33 -0
- package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
- package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
- package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
- package/dist/dist-ZG4OKCSR.js +15 -0
- package/dist/doctor-FKYSIHER.js +345 -0
- package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
- package/dist/{history-RNUWO4JZ.js → history-UMGQNQQ7.js} +7 -7
- package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-YEYTYA6Q.js} +2 -2
- package/dist/index.js +398 -622
- package/dist/{init-NQWFZPKO.js → init-TJYW5ROZ.js} +78 -12
- package/dist/job-HIDMAFW2.js +376 -0
- package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
- package/dist/kiro-VMUHDFGK.js +153 -0
- package/dist/{launchd-OYXUAVW6.js → launchd-U3MSWBRH.js} +9 -17
- package/dist/mcp-PDUD7SGP.js +249 -0
- package/dist/mcp-installer-PQU3XOGO.js +259 -0
- package/dist/mcp-setup-OA7IB3H3.js +263 -0
- package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
- package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
- package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
- package/dist/portal-JYWVHXDU.js +210 -0
- package/dist/prd-Q4J5NVAR.js +408 -0
- package/dist/repos-WWZXNN3P.js +271 -0
- package/dist/review-integration-RQE4KMAV.js +14 -0
- package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
- package/dist/run-VPNXEIBY.js +687 -0
- package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
- package/dist/server-U7PQ6FTS-MG4MJPTS.js +20 -0
- package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
- package/dist/start-IJKY5RVT.js +303 -0
- package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
- package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
- package/dist/tui-DP7736EX.js +61 -0
- package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
- package/dist/{uninstall-KWGSGZTI.js → uninstall-BX6FOV77.js} +3 -3
- package/dist/{update-QYZA4D23.js → update-AQKTHFVQ.js} +3 -3
- package/dist/{version-checker-MVB74DEX.js → version-checker-5L5PUOEX.js} +2 -2
- package/package.json +13 -4
- package/dist/chunk-26LLDX2T.js +0 -553
- package/dist/chunk-DDRLI6JU.js +0 -331
- package/dist/chunk-IFATANHR.js +0 -453
- package/dist/chunk-JWUUVXIV.js +0 -13694
- package/dist/chunk-LD52XG3X.js +0 -4273
- package/dist/chunk-MIAQVCFW.js +0 -39
- package/dist/chunk-YYZOFYS6.js +0 -415
- package/dist/dist-XD4YI27T.js +0 -26
- package/dist/mcp-installer-TOYDP77X.js +0 -124
- package/dist/projects-COUJP4ZC.js +0 -271
- package/dist/review-KMGP2S25.js +0 -152
- package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
- package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
- package/dist/ui-4SM2SUI6.js +0 -167
- package/dist/ui.html +0 -698
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/SKILL.md +0 -0
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/framework-rules.md +0 -0
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/language-rules.md +0 -0
package/dist/chunk-MIAQVCFW.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// ../api/dist/chunk-UOFEIZ6Y.js
|
|
2
|
-
import { randomBytes } from "crypto";
|
|
3
|
-
var apiSecret;
|
|
4
|
-
function generateApiSecret() {
|
|
5
|
-
return randomBytes(32).toString("hex");
|
|
6
|
-
}
|
|
7
|
-
function setApiSecret(secret) {
|
|
8
|
-
apiSecret = secret;
|
|
9
|
-
}
|
|
10
|
-
function getApiSecret() {
|
|
11
|
-
return apiSecret;
|
|
12
|
-
}
|
|
13
|
-
function validateLoopback(req) {
|
|
14
|
-
const remoteAddr = req.socket.remoteAddress ?? "";
|
|
15
|
-
return remoteAddr === "127.0.0.1" || remoteAddr === "::1" || remoteAddr === "::ffff:127.0.0.1";
|
|
16
|
-
}
|
|
17
|
-
function validateAuth(req) {
|
|
18
|
-
if (!validateLoopback(req)) return false;
|
|
19
|
-
if (apiSecret) {
|
|
20
|
-
const headerSecret = req.headers["x-ulpi-secret"];
|
|
21
|
-
if (headerSecret === apiSecret) return true;
|
|
22
|
-
try {
|
|
23
|
-
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
24
|
-
const paramSecret = url.searchParams.get("secret");
|
|
25
|
-
if (paramSecret === apiSecret) return true;
|
|
26
|
-
} catch {
|
|
27
|
-
}
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export {
|
|
34
|
-
generateApiSecret,
|
|
35
|
-
setApiSecret,
|
|
36
|
-
getApiSecret,
|
|
37
|
-
validateLoopback,
|
|
38
|
-
validateAuth
|
|
39
|
-
};
|
package/dist/chunk-YYZOFYS6.js
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
extractSections,
|
|
3
|
-
extractTitle,
|
|
4
|
-
generateSlug,
|
|
5
|
-
getNextVersionNumber,
|
|
6
|
-
parseMarkdownToBlocks,
|
|
7
|
-
savePlan,
|
|
8
|
-
scorePlanQuality
|
|
9
|
-
} from "./chunk-RJIRWQJD.js";
|
|
10
|
-
import {
|
|
11
|
-
API_LOCK_FILE,
|
|
12
|
-
getApiHost,
|
|
13
|
-
getApiPort
|
|
14
|
-
} from "./chunk-DDRLI6JU.js";
|
|
15
|
-
|
|
16
|
-
// ../../packages/review-runtime/dist/index.js
|
|
17
|
-
import { basename } from "path";
|
|
18
|
-
import { randomUUID } from "crypto";
|
|
19
|
-
import * as fs from "fs";
|
|
20
|
-
var ReviewHub = class _ReviewHub {
|
|
21
|
-
sessions = /* @__PURE__ */ new Map();
|
|
22
|
-
awaitWaiters = /* @__PURE__ */ new Map();
|
|
23
|
-
decidedEvictionMs = 5 * 60 * 1e3;
|
|
24
|
-
longPollTimeoutMs = 30 * 1e3;
|
|
25
|
-
/** Maximum number of concurrent sessions to prevent unbounded memory growth. */
|
|
26
|
-
static MAX_SESSIONS = 100;
|
|
27
|
-
/** Sessions older than this TTL are automatically evicted. */
|
|
28
|
-
static SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
29
|
-
// 30 minutes
|
|
30
|
-
/**
|
|
31
|
-
* Register a new review session.
|
|
32
|
-
*/
|
|
33
|
-
async register(payload) {
|
|
34
|
-
this.cleanupExpired();
|
|
35
|
-
if (this.sessions.size >= _ReviewHub.MAX_SESSIONS) {
|
|
36
|
-
const oldest = [...this.sessions.entries()].sort(([, a], [, b]) => a.registeredAt - b.registeredAt)[0];
|
|
37
|
-
if (oldest) {
|
|
38
|
-
this.sessions.delete(oldest[0]);
|
|
39
|
-
this.awaitWaiters.delete(oldest[0]);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const sessionId = randomUUID();
|
|
43
|
-
const token = randomUUID();
|
|
44
|
-
const projectSlug = basename(payload.projectPath);
|
|
45
|
-
let resolveDecision;
|
|
46
|
-
const decisionPromise = new Promise((resolve) => {
|
|
47
|
-
resolveDecision = resolve;
|
|
48
|
-
});
|
|
49
|
-
const session = {
|
|
50
|
-
id: sessionId,
|
|
51
|
-
type: payload.type,
|
|
52
|
-
projectPath: payload.projectPath,
|
|
53
|
-
projectSlug,
|
|
54
|
-
title: "",
|
|
55
|
-
status: "pending",
|
|
56
|
-
registeredAt: Date.now(),
|
|
57
|
-
resolveDecision,
|
|
58
|
-
token
|
|
59
|
-
};
|
|
60
|
-
if (payload.type === "plan" && payload.plan) {
|
|
61
|
-
const blocks = parseMarkdownToBlocks(payload.plan);
|
|
62
|
-
const sections = extractSections(blocks);
|
|
63
|
-
const quality = scorePlanQuality(blocks, sections);
|
|
64
|
-
const title = extractTitle(blocks);
|
|
65
|
-
const slug = generateSlug(title);
|
|
66
|
-
const versionNumber = await getNextVersionNumber(slug, payload.projectPath);
|
|
67
|
-
const version = {
|
|
68
|
-
id: randomUUID(),
|
|
69
|
-
versionNumber,
|
|
70
|
-
markdown: payload.plan,
|
|
71
|
-
annotations: [],
|
|
72
|
-
inlineEdits: [],
|
|
73
|
-
instructions: [],
|
|
74
|
-
priorities: [],
|
|
75
|
-
risks: [],
|
|
76
|
-
createdAt: Date.now()
|
|
77
|
-
};
|
|
78
|
-
try {
|
|
79
|
-
await savePlan(payload.plan, version, slug, payload.projectPath);
|
|
80
|
-
} catch (err) {
|
|
81
|
-
console.error(`[review-hub] Failed to persist plan: ${err instanceof Error ? err.message : String(err)}`);
|
|
82
|
-
}
|
|
83
|
-
session.plan = payload.plan;
|
|
84
|
-
session.blocks = blocks;
|
|
85
|
-
session.sections = sections;
|
|
86
|
-
session.quality = quality;
|
|
87
|
-
session.version = version;
|
|
88
|
-
session.slug = slug;
|
|
89
|
-
session.title = title;
|
|
90
|
-
} else if (payload.type === "code") {
|
|
91
|
-
session.diff = payload.diff;
|
|
92
|
-
session.commitMessage = payload.commitMessage;
|
|
93
|
-
session.title = payload.commitMessage || "Code Review";
|
|
94
|
-
}
|
|
95
|
-
this.sessions.set(sessionId, session);
|
|
96
|
-
decisionPromise.then((decision) => {
|
|
97
|
-
const waiters = this.awaitWaiters.get(sessionId) || [];
|
|
98
|
-
for (const resolve of waiters) {
|
|
99
|
-
resolve(decision);
|
|
100
|
-
}
|
|
101
|
-
this.awaitWaiters.delete(sessionId);
|
|
102
|
-
});
|
|
103
|
-
return { session: this.toPublicSession(session), sessionId, token };
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Get a session by ID.
|
|
107
|
-
*/
|
|
108
|
-
getSession(id) {
|
|
109
|
-
const session = this.sessions.get(id);
|
|
110
|
-
return session ? this.toPublicSession(session) : null;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* List all sessions, optionally filtered by type/status.
|
|
114
|
-
*/
|
|
115
|
-
listSessions(opts) {
|
|
116
|
-
let sessions = [...this.sessions.values()];
|
|
117
|
-
if (opts?.type) sessions = sessions.filter((s) => s.type === opts.type);
|
|
118
|
-
if (opts?.status) sessions = sessions.filter((s) => s.status === opts.status);
|
|
119
|
-
sessions.sort((a, b) => a.registeredAt - b.registeredAt);
|
|
120
|
-
return sessions.map((s) => this.toPublicSession(s));
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Verify a session token.
|
|
124
|
-
*/
|
|
125
|
-
verifyToken(sessionId, token) {
|
|
126
|
-
const session = this.sessions.get(sessionId);
|
|
127
|
-
if (!session) return false;
|
|
128
|
-
return session.token === token;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Submit a decision for a session.
|
|
132
|
-
* When token is provided, it must match the session token.
|
|
133
|
-
*/
|
|
134
|
-
async submitDecision(sessionId, decision, token) {
|
|
135
|
-
const session = this.sessions.get(sessionId);
|
|
136
|
-
if (!session) return false;
|
|
137
|
-
if (token !== void 0 && session.token !== token) return false;
|
|
138
|
-
if (!isValidDecision(decision)) return false;
|
|
139
|
-
try {
|
|
140
|
-
if (session.type === "plan" && session.version && session.slug) {
|
|
141
|
-
const planDecision = decision;
|
|
142
|
-
const reviewedVersion = {
|
|
143
|
-
...session.version,
|
|
144
|
-
annotations: planDecision.annotations || [],
|
|
145
|
-
inlineEdits: planDecision.inlineEdits || [],
|
|
146
|
-
instructions: planDecision.instructions || [],
|
|
147
|
-
priorities: planDecision.priorities || [],
|
|
148
|
-
risks: planDecision.risks || [],
|
|
149
|
-
decision: {
|
|
150
|
-
behavior: planDecision.behavior,
|
|
151
|
-
message: planDecision.message,
|
|
152
|
-
decidedAt: Date.now()
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
await savePlan(session.plan, reviewedVersion, session.slug, session.projectPath);
|
|
156
|
-
}
|
|
157
|
-
} catch {
|
|
158
|
-
}
|
|
159
|
-
session.status = "decided";
|
|
160
|
-
session.decidedAt = Date.now();
|
|
161
|
-
session.decisionPayload = decision;
|
|
162
|
-
session.resolveDecision(decision);
|
|
163
|
-
const waiters = this.awaitWaiters.get(sessionId);
|
|
164
|
-
if (waiters && waiters.length > 0) {
|
|
165
|
-
for (const waiter of waiters) {
|
|
166
|
-
waiter(decision);
|
|
167
|
-
}
|
|
168
|
-
this.awaitWaiters.delete(sessionId);
|
|
169
|
-
}
|
|
170
|
-
setTimeout(() => {
|
|
171
|
-
this.sessions.delete(sessionId);
|
|
172
|
-
this.awaitWaiters.delete(sessionId);
|
|
173
|
-
}, this.decidedEvictionMs);
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Long-poll waiting for a decision on a session.
|
|
178
|
-
* Returns the decision or null on timeout.
|
|
179
|
-
*/
|
|
180
|
-
awaitDecision(sessionId, timeoutMs) {
|
|
181
|
-
const session = this.sessions.get(sessionId);
|
|
182
|
-
if (!session) return Promise.resolve(null);
|
|
183
|
-
if (session.status === "decided" && session.decisionPayload) {
|
|
184
|
-
return Promise.resolve(session.decisionPayload);
|
|
185
|
-
}
|
|
186
|
-
const timeout = timeoutMs ?? this.longPollTimeoutMs;
|
|
187
|
-
return new Promise((resolve) => {
|
|
188
|
-
const waiters = this.awaitWaiters.get(sessionId) || [];
|
|
189
|
-
const timer = setTimeout(() => {
|
|
190
|
-
const idx = waiters.indexOf(onDecision);
|
|
191
|
-
if (idx !== -1) waiters.splice(idx, 1);
|
|
192
|
-
resolve(null);
|
|
193
|
-
}, timeout);
|
|
194
|
-
function onDecision(decision) {
|
|
195
|
-
clearTimeout(timer);
|
|
196
|
-
resolve(decision);
|
|
197
|
-
}
|
|
198
|
-
waiters.push(onDecision);
|
|
199
|
-
this.awaitWaiters.set(sessionId, waiters);
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Save annotations/version for a plan session.
|
|
204
|
-
*/
|
|
205
|
-
async saveVersion(sessionId, data) {
|
|
206
|
-
const session = this.sessions.get(sessionId);
|
|
207
|
-
if (!session || session.type !== "plan" || !session.version || !session.slug) {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
const updatedVersion = {
|
|
211
|
-
...session.version,
|
|
212
|
-
annotations: data.annotations || [],
|
|
213
|
-
inlineEdits: data.inlineEdits || [],
|
|
214
|
-
instructions: data.instructions || [],
|
|
215
|
-
priorities: data.priorities || [],
|
|
216
|
-
risks: data.risks || []
|
|
217
|
-
};
|
|
218
|
-
await savePlan(session.plan, updatedVersion, session.slug, session.projectPath);
|
|
219
|
-
Object.assign(session.version, updatedVersion);
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Get the session token (for loopback clients that need to pass it to the UI).
|
|
224
|
-
*/
|
|
225
|
-
getSessionToken(id) {
|
|
226
|
-
const session = this.sessions.get(id);
|
|
227
|
-
return session?.token ?? null;
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get the raw decision payload for a session (used by await endpoint).
|
|
231
|
-
*/
|
|
232
|
-
getDecisionPayload(id) {
|
|
233
|
-
const session = this.sessions.get(id);
|
|
234
|
-
return session?.decisionPayload ?? null;
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Cleanup orphaned sessions (pending for over 1 hour).
|
|
238
|
-
*/
|
|
239
|
-
cleanup() {
|
|
240
|
-
const now = Date.now();
|
|
241
|
-
for (const [id, session] of this.sessions) {
|
|
242
|
-
if (session.status === "pending" && now - session.registeredAt > 60 * 60 * 1e3) {
|
|
243
|
-
this.sessions.delete(id);
|
|
244
|
-
this.awaitWaiters.delete(id);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Remove sessions that have exceeded the TTL, regardless of status.
|
|
250
|
-
*/
|
|
251
|
-
cleanupExpired() {
|
|
252
|
-
const now = Date.now();
|
|
253
|
-
for (const [id, session] of this.sessions) {
|
|
254
|
-
if (now - session.registeredAt > _ReviewHub.SESSION_TTL_MS) {
|
|
255
|
-
this.sessions.delete(id);
|
|
256
|
-
this.awaitWaiters.delete(id);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
toPublicSession(session) {
|
|
261
|
-
const { resolveDecision, decisionPayload, token: _token, ...pub } = session;
|
|
262
|
-
if (decisionPayload && !pub.decision) {
|
|
263
|
-
if ("behavior" in decisionPayload) {
|
|
264
|
-
pub.decision = {
|
|
265
|
-
behavior: decisionPayload.behavior,
|
|
266
|
-
message: decisionPayload.message,
|
|
267
|
-
decidedAt: session.decidedAt ?? Date.now()
|
|
268
|
-
};
|
|
269
|
-
} else if ("approved" in decisionPayload) {
|
|
270
|
-
pub.decision = {
|
|
271
|
-
behavior: decisionPayload.approved ? "allow" : "deny",
|
|
272
|
-
message: decisionPayload.message ?? decisionPayload.feedback,
|
|
273
|
-
decidedAt: session.decidedAt ?? Date.now()
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return pub;
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
function isValidDecision(d) {
|
|
281
|
-
if (!d || typeof d !== "object") return false;
|
|
282
|
-
if ("behavior" in d) {
|
|
283
|
-
return d.behavior === "allow" || d.behavior === "deny";
|
|
284
|
-
}
|
|
285
|
-
if ("approved" in d) {
|
|
286
|
-
return typeof d.approved === "boolean";
|
|
287
|
-
}
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
var DISCOVERY_TIMEOUT_MS = 2e3;
|
|
291
|
-
var MAX_PORTS_TO_PROBE = 5;
|
|
292
|
-
function isProcessAlive(pid) {
|
|
293
|
-
try {
|
|
294
|
-
process.kill(pid, 0);
|
|
295
|
-
return true;
|
|
296
|
-
} catch {
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
function readLockFile() {
|
|
301
|
-
try {
|
|
302
|
-
const raw = fs.readFileSync(API_LOCK_FILE, "utf-8");
|
|
303
|
-
const data = JSON.parse(raw);
|
|
304
|
-
if (typeof data === "object" && data !== null && typeof data.port === "number" && typeof data.pid === "number") {
|
|
305
|
-
const { port, pid, secret } = data;
|
|
306
|
-
if (isProcessAlive(pid)) {
|
|
307
|
-
return { port, secret };
|
|
308
|
-
}
|
|
309
|
-
try {
|
|
310
|
-
fs.unlinkSync(API_LOCK_FILE);
|
|
311
|
-
} catch {
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
} catch {
|
|
315
|
-
}
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
async function healthCheck(host, port) {
|
|
319
|
-
try {
|
|
320
|
-
const controller = new AbortController();
|
|
321
|
-
const timer = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT_MS);
|
|
322
|
-
const res = await fetch(`http://${host}:${port}/api/health`, {
|
|
323
|
-
signal: controller.signal
|
|
324
|
-
});
|
|
325
|
-
clearTimeout(timer);
|
|
326
|
-
return res.ok;
|
|
327
|
-
} catch {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
async function discoverUlpiServer() {
|
|
332
|
-
const host = getApiHost();
|
|
333
|
-
const lockData = readLockFile();
|
|
334
|
-
if (lockData !== null) {
|
|
335
|
-
if (await healthCheck(host, lockData.port)) {
|
|
336
|
-
return lockData;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
const configPort = getApiPort();
|
|
340
|
-
const ports = [configPort, ...configPort !== 9800 ? [9800] : []].slice(0, MAX_PORTS_TO_PROBE);
|
|
341
|
-
for (const port of ports) {
|
|
342
|
-
if (port === lockData?.port) continue;
|
|
343
|
-
if (await healthCheck(host, port)) {
|
|
344
|
-
return { port };
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
function buildHeaders(secret) {
|
|
350
|
-
const headers = { "Content-Type": "application/json" };
|
|
351
|
-
if (secret) headers["X-Ulpi-Secret"] = secret;
|
|
352
|
-
return headers;
|
|
353
|
-
}
|
|
354
|
-
async function registerWithServer(port, payload, secret) {
|
|
355
|
-
try {
|
|
356
|
-
const host = getApiHost();
|
|
357
|
-
const res = await fetch(`http://${host}:${port}/api/review/hub/register`, {
|
|
358
|
-
method: "POST",
|
|
359
|
-
headers: buildHeaders(secret),
|
|
360
|
-
body: JSON.stringify(payload)
|
|
361
|
-
});
|
|
362
|
-
if (!res.ok) return null;
|
|
363
|
-
const data = await res.json();
|
|
364
|
-
return data;
|
|
365
|
-
} catch {
|
|
366
|
-
return null;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
async function waitForServerDecision(port, sessionId, timeoutMs = 3e4, maxWaitMs, token, secret) {
|
|
370
|
-
const deadline = Date.now() + (maxWaitMs && maxWaitMs > 0 ? maxWaitMs : 10 * 60 * 1e3);
|
|
371
|
-
const host = getApiHost();
|
|
372
|
-
const tokenParam = token ? `?token=${encodeURIComponent(token)}` : "";
|
|
373
|
-
const secretHeaders = secret ? { "X-Ulpi-Secret": secret } : {};
|
|
374
|
-
while (Date.now() < deadline) {
|
|
375
|
-
try {
|
|
376
|
-
const controller = new AbortController();
|
|
377
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs + 5e3);
|
|
378
|
-
const res = await fetch(
|
|
379
|
-
`http://${host}:${port}/api/review/hub/session/${sessionId}/await${tokenParam}`,
|
|
380
|
-
{ signal: controller.signal, headers: secretHeaders }
|
|
381
|
-
);
|
|
382
|
-
clearTimeout(timer);
|
|
383
|
-
if (res.status === 204) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (res.ok) {
|
|
387
|
-
const data = await res.json();
|
|
388
|
-
return data.decision;
|
|
389
|
-
}
|
|
390
|
-
if (res.status === 410) {
|
|
391
|
-
try {
|
|
392
|
-
const data = await res.json();
|
|
393
|
-
if (data.decision) return data.decision;
|
|
394
|
-
} catch {
|
|
395
|
-
}
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
if (res.status >= 500) {
|
|
399
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
} catch {
|
|
404
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
export {
|
|
411
|
-
ReviewHub,
|
|
412
|
-
discoverUlpiServer,
|
|
413
|
-
registerWithServer,
|
|
414
|
-
waitForServerDecision
|
|
415
|
-
};
|
package/dist/dist-XD4YI27T.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
JsonSessionStore,
|
|
3
|
-
appendEvent,
|
|
4
|
-
createEmptyState,
|
|
5
|
-
createInitialState,
|
|
6
|
-
detectNewCommit,
|
|
7
|
-
nextPhase,
|
|
8
|
-
projectDirToSlug,
|
|
9
|
-
readEvents,
|
|
10
|
-
updateStateFromInput,
|
|
11
|
-
validateSessionId
|
|
12
|
-
} from "./chunk-26LLDX2T.js";
|
|
13
|
-
import "./chunk-DDRLI6JU.js";
|
|
14
|
-
import "./chunk-4VNS5WPM.js";
|
|
15
|
-
export {
|
|
16
|
-
JsonSessionStore,
|
|
17
|
-
appendEvent,
|
|
18
|
-
createEmptyState,
|
|
19
|
-
createInitialState,
|
|
20
|
-
detectNewCommit,
|
|
21
|
-
nextPhase,
|
|
22
|
-
projectDirToSlug,
|
|
23
|
-
readEvents,
|
|
24
|
-
updateStateFromInput,
|
|
25
|
-
validateSessionId
|
|
26
|
-
};
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getBinaryPath
|
|
3
|
-
} from "./chunk-DDRLI6JU.js";
|
|
4
|
-
import "./chunk-4VNS5WPM.js";
|
|
5
|
-
|
|
6
|
-
// src/mcp-installer.ts
|
|
7
|
-
import * as fs from "fs";
|
|
8
|
-
import * as path from "path";
|
|
9
|
-
function installMcpServer(projectDir) {
|
|
10
|
-
const settingsDir = path.join(projectDir, ".claude");
|
|
11
|
-
const settingsPath = path.join(settingsDir, "settings.local.json");
|
|
12
|
-
let settings = {};
|
|
13
|
-
if (fs.existsSync(settingsPath)) {
|
|
14
|
-
try {
|
|
15
|
-
settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
16
|
-
} catch {
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
const mcpServers = settings.mcpServers ?? {};
|
|
20
|
-
if (mcpServers["codemap"]) {
|
|
21
|
-
return { installed: false, message: "CodeMap MCP server already registered" };
|
|
22
|
-
}
|
|
23
|
-
const binary = getBinaryPath();
|
|
24
|
-
const parts = binary.split(" ");
|
|
25
|
-
const command = parts[0];
|
|
26
|
-
const baseArgs = parts.length > 1 ? [...parts.slice(1).map((a) => a.replace(/"/g, "")), "codemap", "serve"] : ["codemap", "serve"];
|
|
27
|
-
mcpServers["codemap"] = {
|
|
28
|
-
command,
|
|
29
|
-
args: baseArgs
|
|
30
|
-
};
|
|
31
|
-
settings.mcpServers = mcpServers;
|
|
32
|
-
fs.mkdirSync(settingsDir, { recursive: true });
|
|
33
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
34
|
-
return { installed: true, message: "CodeMap MCP server registered in .claude/settings.local.json" };
|
|
35
|
-
}
|
|
36
|
-
function uninstallMcpServer(projectDir) {
|
|
37
|
-
const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
|
|
38
|
-
if (!fs.existsSync(settingsPath)) return;
|
|
39
|
-
try {
|
|
40
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
41
|
-
const mcpServers = settings.mcpServers;
|
|
42
|
-
if (mcpServers?.["codemap"]) {
|
|
43
|
-
delete mcpServers["codemap"];
|
|
44
|
-
if (Object.keys(mcpServers).length === 0) {
|
|
45
|
-
delete settings.mcpServers;
|
|
46
|
-
}
|
|
47
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function isMcpServerInstalled(projectDir) {
|
|
53
|
-
const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
|
|
54
|
-
if (!fs.existsSync(settingsPath)) return false;
|
|
55
|
-
try {
|
|
56
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
57
|
-
const mcpServers = settings.mcpServers;
|
|
58
|
-
return !!mcpServers?.["codemap"];
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function installMemoryMcpServer(projectDir) {
|
|
64
|
-
const settingsDir = path.join(projectDir, ".claude");
|
|
65
|
-
const settingsPath = path.join(settingsDir, "settings.local.json");
|
|
66
|
-
let settings = {};
|
|
67
|
-
if (fs.existsSync(settingsPath)) {
|
|
68
|
-
try {
|
|
69
|
-
settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
70
|
-
} catch {
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const mcpServers = settings.mcpServers ?? {};
|
|
74
|
-
if (mcpServers["memory"]) {
|
|
75
|
-
return { installed: false, message: "Memory MCP server already registered" };
|
|
76
|
-
}
|
|
77
|
-
const binary = getBinaryPath();
|
|
78
|
-
const parts = binary.split(" ");
|
|
79
|
-
const command = parts[0];
|
|
80
|
-
const baseArgs = parts.length > 1 ? [...parts.slice(1).map((a) => a.replace(/"/g, "")), "memory", "serve"] : ["memory", "serve"];
|
|
81
|
-
mcpServers["memory"] = {
|
|
82
|
-
command,
|
|
83
|
-
args: baseArgs
|
|
84
|
-
};
|
|
85
|
-
settings.mcpServers = mcpServers;
|
|
86
|
-
fs.mkdirSync(settingsDir, { recursive: true });
|
|
87
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
88
|
-
return { installed: true, message: "Memory MCP server registered in .claude/settings.local.json" };
|
|
89
|
-
}
|
|
90
|
-
function uninstallMemoryMcpServer(projectDir) {
|
|
91
|
-
const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
|
|
92
|
-
if (!fs.existsSync(settingsPath)) return;
|
|
93
|
-
try {
|
|
94
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
95
|
-
const mcpServers = settings.mcpServers;
|
|
96
|
-
if (mcpServers?.["memory"]) {
|
|
97
|
-
delete mcpServers["memory"];
|
|
98
|
-
if (Object.keys(mcpServers).length === 0) {
|
|
99
|
-
delete settings.mcpServers;
|
|
100
|
-
}
|
|
101
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function isMemoryMcpServerInstalled(projectDir) {
|
|
107
|
-
const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
|
|
108
|
-
if (!fs.existsSync(settingsPath)) return false;
|
|
109
|
-
try {
|
|
110
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
111
|
-
const mcpServers = settings.mcpServers;
|
|
112
|
-
return !!mcpServers?.["memory"];
|
|
113
|
-
} catch {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
export {
|
|
118
|
-
installMcpServer,
|
|
119
|
-
installMemoryMcpServer,
|
|
120
|
-
isMcpServerInstalled,
|
|
121
|
-
isMemoryMcpServerInstalled,
|
|
122
|
-
uninstallMcpServer,
|
|
123
|
-
uninstallMemoryMcpServer
|
|
124
|
-
};
|