@clien-ai/mcp 0.1.0
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 +221 -0
- package/dist/auth/oauth.js +468 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/pkce.js +31 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/storage.js +371 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/cli.js +280 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/authorize.js +57 -0
- package/dist/commands/authorize.js.map +1 -0
- package/dist/commands/install.js +144 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/logout.js +35 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/status.js +70 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/server.js +189 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/research.js +453 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/types/report.js +60 -0
- package/dist/types/report.js.map +1 -0
- package/dist/util/atomic-write.js +43 -0
- package/dist/util/atomic-write.js.map +1 -0
- package/dist/util/exit-codes.js +97 -0
- package/dist/util/exit-codes.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `clien_research` — the hero tool.
|
|
3
|
+
*
|
|
4
|
+
* Contract (summary):
|
|
5
|
+
* 1. Ensure a valid bearer token (triggers OAuth on first use).
|
|
6
|
+
* 2. POST /api/validate/start -> { jobId }
|
|
7
|
+
* 3. Poll /api/validate/{jobId}/events every 5s, forward progress to the
|
|
8
|
+
* caller via MCP notifications/progress.
|
|
9
|
+
* 4. On terminal status, GET /api/validate/{jobId}/report (Accept: application/json)
|
|
10
|
+
* -> { reportMarkdown, reportData }.
|
|
11
|
+
* 5. Return { content: [{ type: 'text', text: reportMarkdown }],
|
|
12
|
+
* _meta: { report_data, job_id, status, project_id } }.
|
|
13
|
+
*
|
|
14
|
+
* Error mapping:
|
|
15
|
+
* - 402 -> tool error "Insufficient credits ..."
|
|
16
|
+
* - 401 mid-poll -> session.refresh() once; if still 401, surface
|
|
17
|
+
* "session expired — re-run to re-authenticate"
|
|
18
|
+
* with jobId so the user can recover via email link.
|
|
19
|
+
* - 5xx -> up to 3 backoff retries, then surface with jobId.
|
|
20
|
+
*
|
|
21
|
+
* Timeout behavior:
|
|
22
|
+
* The MCP SDK's progress notifications reset the caller's timeout when
|
|
23
|
+
* `resetTimeoutOnProgress: true` is set on the *caller* side. We emit one
|
|
24
|
+
* notification per events batch, which — per Unit 0's spike — is sufficient
|
|
25
|
+
* to keep Claude Code's 25-min synchronous tool call alive.
|
|
26
|
+
*/
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
import { OAuthError } from '../auth/oauth.js';
|
|
29
|
+
import { ResearchReportDataSchema } from '../types/report.js';
|
|
30
|
+
// Per-endpoint timeouts (#173). Node fetch has no implicit timeout, so a hung
|
|
31
|
+
// Supabase or Clien.ai endpoint would otherwise wedge the MCP server forever.
|
|
32
|
+
const START_TIMEOUT_MS = 30_000;
|
|
33
|
+
const EVENTS_POLL_TIMEOUT_MS = 30_000;
|
|
34
|
+
const REPORT_FETCH_TIMEOUT_MS = 60_000;
|
|
35
|
+
export const ResearchInputSchema = z.object({
|
|
36
|
+
idea: z
|
|
37
|
+
.string()
|
|
38
|
+
.min(1, 'idea must be non-empty')
|
|
39
|
+
.max(10_000, 'idea too long')
|
|
40
|
+
.describe('The product idea, feature hypothesis, or research question to validate. ' +
|
|
41
|
+
'Can be a one-liner or several paragraphs — more context yields a better report.'),
|
|
42
|
+
depth: z
|
|
43
|
+
.enum(['quick', 'deep'])
|
|
44
|
+
.optional()
|
|
45
|
+
.default('deep')
|
|
46
|
+
.describe('"deep" (default) runs a 15-25 minute full research pass. ' +
|
|
47
|
+
'"quick" caps cost and runs a tighter pass.'),
|
|
48
|
+
project_id: z
|
|
49
|
+
.string()
|
|
50
|
+
.uuid()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe('Optional Clien.ai project UUID to attach this run to. When omitted, ' +
|
|
53
|
+
'the run is standalone and not associated with any project.'),
|
|
54
|
+
// Cap aligned with PROJECT_NAME_MAX (100) on the server side. Hardcoded
|
|
55
|
+
// because the @clien-ai/mcp package is published separately and cannot
|
|
56
|
+
// import from the Next.js app. Keep both sides in sync if either changes.
|
|
57
|
+
project_name: z
|
|
58
|
+
.string()
|
|
59
|
+
.min(1, 'project_name must be non-empty')
|
|
60
|
+
.max(100, 'project_name too long (max 100 chars)')
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Name for a new Clien.ai project to attach this run to. You SHOULD ' +
|
|
63
|
+
'supply this whenever `project_id` is not set, otherwise the ' +
|
|
64
|
+
'resulting report will not be discoverable in the Clien.ai web UI. ' +
|
|
65
|
+
'Derive a short (2-4 word) human-readable name from the idea — ' +
|
|
66
|
+
'typically the product name. Guidance on when to create a new ' +
|
|
67
|
+
'project vs. reuse an existing one: create a NEW project (supply a ' +
|
|
68
|
+
'fresh `project_name`) for each distinct research idea or product ' +
|
|
69
|
+
'concept; REUSE an existing `project_id` only when the user ' +
|
|
70
|
+
'explicitly asks to add follow-up research to a prior run on the ' +
|
|
71
|
+
'same idea. Ignored when `project_id` is also provided.'),
|
|
72
|
+
});
|
|
73
|
+
const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
74
|
+
const TERMINAL_STATUSES = new Set(['complete', 'failed', 'cancelled']);
|
|
75
|
+
// Roughly map phase -> coarse progress (0..1). We emit progress on every
|
|
76
|
+
// events batch; when the job reports totalCostCents we include it in meta.
|
|
77
|
+
const PHASE_PROGRESS = {
|
|
78
|
+
initialising: 0.05,
|
|
79
|
+
researching: 0.4,
|
|
80
|
+
synthesising: 0.85,
|
|
81
|
+
complete: 1,
|
|
82
|
+
failed: 1,
|
|
83
|
+
cancelled: 1,
|
|
84
|
+
};
|
|
85
|
+
function categorizeFetchError(status) {
|
|
86
|
+
if (status === 401)
|
|
87
|
+
return 'auth';
|
|
88
|
+
if (status === 402)
|
|
89
|
+
return 'credits';
|
|
90
|
+
if (status >= 400 && status < 500)
|
|
91
|
+
return 'client';
|
|
92
|
+
if (status >= 500)
|
|
93
|
+
return 'server';
|
|
94
|
+
return 'other';
|
|
95
|
+
}
|
|
96
|
+
export class ResearchToolError extends Error {
|
|
97
|
+
userMessage;
|
|
98
|
+
jobId;
|
|
99
|
+
constructor(userMessage, opts = {}) {
|
|
100
|
+
super(userMessage);
|
|
101
|
+
this.userMessage = userMessage;
|
|
102
|
+
this.jobId = opts.jobId;
|
|
103
|
+
if (opts.cause !== undefined)
|
|
104
|
+
this.cause = opts.cause;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* OAuth error codes we treat as transient (network/availability) rather than
|
|
109
|
+
* auth-fatal. Anything not in this set is classified as `auth` and triggers
|
|
110
|
+
* the user-visible re-auth path. Positive list (allow-list) instead of a
|
|
111
|
+
* single-code negative gate so future codes default to the safer auth path.
|
|
112
|
+
*/
|
|
113
|
+
const TRANSIENT_OAUTH_CODES = new Set([
|
|
114
|
+
'oauth_network_error',
|
|
115
|
+
'oauth_timeout',
|
|
116
|
+
'lock_unavailable',
|
|
117
|
+
]);
|
|
118
|
+
async function apiCall(ctx, method, path, opts = {}) {
|
|
119
|
+
const url = `${ctx.config.serverUrl}${path}`;
|
|
120
|
+
const doFetch = async (token) => {
|
|
121
|
+
const headers = {
|
|
122
|
+
Authorization: `Bearer ${token}`,
|
|
123
|
+
Accept: opts.acceptJson === false ? '*/*' : 'application/json',
|
|
124
|
+
...(opts.headers ?? {}),
|
|
125
|
+
};
|
|
126
|
+
const body = opts.body === undefined ? undefined : JSON.stringify(opts.body);
|
|
127
|
+
if (body !== undefined && !headers['Content-Type']) {
|
|
128
|
+
headers['Content-Type'] = 'application/json';
|
|
129
|
+
}
|
|
130
|
+
const init = { method, headers, body };
|
|
131
|
+
if (opts.timeoutMs !== undefined) {
|
|
132
|
+
init.signal = AbortSignal.timeout(opts.timeoutMs);
|
|
133
|
+
}
|
|
134
|
+
return ctx.fetch(url, init);
|
|
135
|
+
};
|
|
136
|
+
let response = (await doFetch(ctx.session.accessToken));
|
|
137
|
+
if (response.status === 401 && opts.retryOn401 !== false) {
|
|
138
|
+
try {
|
|
139
|
+
const fresh = await ctx.session.refresh();
|
|
140
|
+
response = (await doFetch(fresh));
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
// Distinguish: transient (network/lock/timeout) vs auth (revoked).
|
|
144
|
+
// Positive allow-list means future OAuth codes default to `auth` —
|
|
145
|
+
// safer to surface re-auth than to mistakenly retry on a real
|
|
146
|
+
// credential failure.
|
|
147
|
+
const isTransient = err instanceof OAuthError && TRANSIENT_OAUTH_CODES.has(err.code);
|
|
148
|
+
response.refreshError = {
|
|
149
|
+
kind: isTransient ? 'transient' : 'auth',
|
|
150
|
+
message: err instanceof Error ? err.message : String(err),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return response;
|
|
155
|
+
}
|
|
156
|
+
// ------------------------------------------------------------------
|
|
157
|
+
// Main tool handler
|
|
158
|
+
// ------------------------------------------------------------------
|
|
159
|
+
export async function runResearch(input, deps) {
|
|
160
|
+
const parsed = ResearchInputSchema.safeParse(input);
|
|
161
|
+
if (!parsed.success) {
|
|
162
|
+
const details = parsed.error.issues
|
|
163
|
+
.map((i) => `${i.path.join('.') || 'input'}: ${i.message}`)
|
|
164
|
+
.join('; ');
|
|
165
|
+
throw new ResearchToolError(`Invalid input — ${details}`);
|
|
166
|
+
}
|
|
167
|
+
const { idea, depth, project_id, project_name } = parsed.data;
|
|
168
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
169
|
+
const sleepImpl = deps.sleep ?? defaultSleep;
|
|
170
|
+
const pollIntervalMs = deps.pollIntervalMs ?? deps.config.pollIntervalMs;
|
|
171
|
+
const ctx = {
|
|
172
|
+
config: deps.config,
|
|
173
|
+
session: deps.session,
|
|
174
|
+
fetch: fetchImpl,
|
|
175
|
+
};
|
|
176
|
+
// 1. Start. Forward `depth` so the backend can apply the matching
|
|
177
|
+
// budget/model defaults — see resolveValidationConfig in the Next.js
|
|
178
|
+
// app's app/lib/validation/schemas.ts.
|
|
179
|
+
const startBody = {
|
|
180
|
+
message: idea,
|
|
181
|
+
depth,
|
|
182
|
+
};
|
|
183
|
+
if (project_id)
|
|
184
|
+
startBody.projectId = project_id;
|
|
185
|
+
if (project_name)
|
|
186
|
+
startBody.projectName = project_name;
|
|
187
|
+
let startResponse;
|
|
188
|
+
try {
|
|
189
|
+
startResponse = await apiCall(ctx, 'POST', '/api/validate/start', {
|
|
190
|
+
body: startBody,
|
|
191
|
+
timeoutMs: START_TIMEOUT_MS,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const e = err;
|
|
196
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
197
|
+
throw new ResearchToolError(`Clien.ai start endpoint timed out after ${START_TIMEOUT_MS / 1000}s. Try again in a moment.`);
|
|
198
|
+
}
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
if (!startResponse.ok) {
|
|
202
|
+
// If the 401 came from a refresh failure, the cached session is suspect —
|
|
203
|
+
// invalidate so the next tool invocation rebuilds it from disk.
|
|
204
|
+
if (startResponse.status === 401) {
|
|
205
|
+
deps.invalidateSession?.();
|
|
206
|
+
}
|
|
207
|
+
await handleStartError(startResponse);
|
|
208
|
+
}
|
|
209
|
+
const startJson = (await startResponse.json().catch(() => null));
|
|
210
|
+
if (!startJson?.jobId) {
|
|
211
|
+
throw new ResearchToolError('Clien.ai accepted the request but did not return a job id. Please try again.');
|
|
212
|
+
}
|
|
213
|
+
const jobId = startJson.jobId;
|
|
214
|
+
// projectId may be absent on older deployments; treat missing as null so
|
|
215
|
+
// _meta.project_id is always present in the result shape.
|
|
216
|
+
const startProjectId = startJson.projectId ?? null;
|
|
217
|
+
try {
|
|
218
|
+
await deps.onProgress?.({
|
|
219
|
+
progress: 0,
|
|
220
|
+
total: 1,
|
|
221
|
+
message: `Research started (job ${jobId})`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// progress is advisory; never let it tank the run
|
|
226
|
+
}
|
|
227
|
+
// 2. Poll events
|
|
228
|
+
let lastSequence = 0;
|
|
229
|
+
let status = startJson.status ?? 'initialising';
|
|
230
|
+
let consecutiveServerErrors = 0;
|
|
231
|
+
let totalCostCents;
|
|
232
|
+
const deadline = deps.maxDurationMs ? Date.now() + deps.maxDurationMs : Number.POSITIVE_INFINITY;
|
|
233
|
+
while (!TERMINAL_STATUSES.has(status)) {
|
|
234
|
+
if (Date.now() > deadline) {
|
|
235
|
+
throw new ResearchToolError(`Research did not complete within the allotted time. Check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
|
|
236
|
+
}
|
|
237
|
+
await sleepImpl(pollIntervalMs);
|
|
238
|
+
let eventsResponse;
|
|
239
|
+
try {
|
|
240
|
+
eventsResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/events?since=${lastSequence}&limit=100`, { timeoutMs: EVENTS_POLL_TIMEOUT_MS });
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
const e = err;
|
|
244
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
245
|
+
// Treat poll timeouts the same as transient 5xx — backoff and retry.
|
|
246
|
+
consecutiveServerErrors += 1;
|
|
247
|
+
if (consecutiveServerErrors >= 3) {
|
|
248
|
+
throw new ResearchToolError(`Repeated network timeouts polling /events. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
|
|
249
|
+
}
|
|
250
|
+
const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
|
|
251
|
+
await sleepImpl(backoffMs);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
if (eventsResponse.ok) {
|
|
257
|
+
consecutiveServerErrors = 0;
|
|
258
|
+
const data = (await eventsResponse.json().catch(() => null));
|
|
259
|
+
if (data?.job?.status)
|
|
260
|
+
status = data.job.status;
|
|
261
|
+
if (typeof data?.job?.totalCostCents === 'number') {
|
|
262
|
+
totalCostCents = data.job.totalCostCents;
|
|
263
|
+
}
|
|
264
|
+
if (typeof data?.lastSequence === 'number') {
|
|
265
|
+
lastSequence = data.lastSequence;
|
|
266
|
+
}
|
|
267
|
+
const latestMessage = summarizeEvents(data?.events);
|
|
268
|
+
const phaseProgress = PHASE_PROGRESS[status] ?? 0.5;
|
|
269
|
+
try {
|
|
270
|
+
await deps.onProgress?.({
|
|
271
|
+
progress: phaseProgress,
|
|
272
|
+
total: 1,
|
|
273
|
+
message: latestMessage ?? `phase: ${status}`,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// ignore
|
|
278
|
+
}
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const category = categorizeFetchError(eventsResponse.status);
|
|
282
|
+
if (category === 'auth') {
|
|
283
|
+
// Distinguish "transient refresh failure" from "genuinely revoked".
|
|
284
|
+
// The former came from network issues during the refresh attempt and
|
|
285
|
+
// should backoff like any 5xx; the latter requires re-authentication.
|
|
286
|
+
if (eventsResponse.refreshError?.kind === 'transient') {
|
|
287
|
+
consecutiveServerErrors += 1;
|
|
288
|
+
if (consecutiveServerErrors >= 3) {
|
|
289
|
+
throw new ResearchToolError(`Repeated network errors refreshing session. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
|
|
290
|
+
}
|
|
291
|
+
const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
|
|
292
|
+
await sleepImpl(backoffMs);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
// Genuine auth failure — drop the in-memory cached session so the next
|
|
296
|
+
// tool call rebuilds from disk (or fails fast with `oauth_no_tty`).
|
|
297
|
+
deps.invalidateSession?.();
|
|
298
|
+
throw new ResearchToolError(`Session expired during research. Your run is still in progress — view the report at ${webJobUrl(deps.config, jobId)} or re-run clien_research to re-authenticate.`, { jobId });
|
|
299
|
+
}
|
|
300
|
+
if (category === 'credits') {
|
|
301
|
+
// extremely unlikely mid-run; the server would have 402'd at start.
|
|
302
|
+
throw new ResearchToolError('Insufficient credits — buy more at https://clien.ai/app/billing', { jobId });
|
|
303
|
+
}
|
|
304
|
+
if (category === 'server') {
|
|
305
|
+
consecutiveServerErrors += 1;
|
|
306
|
+
if (consecutiveServerErrors >= 3) {
|
|
307
|
+
throw new ResearchToolError(`Clien.ai is returning server errors. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
|
|
308
|
+
}
|
|
309
|
+
// Exponential backoff: 2s, 5s, 10s.
|
|
310
|
+
const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
|
|
311
|
+
await sleepImpl(backoffMs);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
// 404 / 400 on events after a successful start is highly unusual — surface.
|
|
315
|
+
const text = await eventsResponse.text().catch(() => '');
|
|
316
|
+
throw new ResearchToolError(`Unexpected error polling events (HTTP ${eventsResponse.status}): ${truncate(text, 200)}`, { jobId });
|
|
317
|
+
}
|
|
318
|
+
// 3. Fetch final report (JSON variant)
|
|
319
|
+
let reportResponse;
|
|
320
|
+
try {
|
|
321
|
+
reportResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/report`, { acceptJson: true, timeoutMs: REPORT_FETCH_TIMEOUT_MS });
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
const e = err;
|
|
325
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
326
|
+
throw new ResearchToolError(`Clien.ai report fetch timed out after ${REPORT_FETCH_TIMEOUT_MS / 1000}s. Job ${jobId} completed; view at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
327
|
+
}
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
if (!reportResponse.ok) {
|
|
331
|
+
// If the run failed but the report endpoint has no markdown yet, provide
|
|
332
|
+
// the best message we can given we know the job status.
|
|
333
|
+
if (reportResponse.status === 404) {
|
|
334
|
+
throw new ResearchToolError(`The research run ${status === 'failed' ? 'failed' : 'did not produce a report'}. See ${webJobUrl(deps.config, jobId)} for details.`, { jobId });
|
|
335
|
+
}
|
|
336
|
+
throw new ResearchToolError(`Failed to fetch report (HTTP ${reportResponse.status}). Job ${jobId} completed with status "${status}". See ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
337
|
+
}
|
|
338
|
+
const reportJson = (await reportResponse.json().catch(() => null));
|
|
339
|
+
if (!reportJson?.reportMarkdown) {
|
|
340
|
+
throw new ResearchToolError(`Report is empty for job ${jobId}. View at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
341
|
+
}
|
|
342
|
+
// Validate report_data against our typed schema. On failure, pass through
|
|
343
|
+
// the raw payload unchanged — never drop data, even if the backend ships a
|
|
344
|
+
// shape we don't yet recognize. The schema is `.passthrough()` so unknown
|
|
345
|
+
// fields survive validation silently.
|
|
346
|
+
const rawReportData = reportJson.reportData ?? null;
|
|
347
|
+
let reportData = rawReportData;
|
|
348
|
+
if (rawReportData) {
|
|
349
|
+
const parsedReport = ResearchReportDataSchema.safeParse(rawReportData);
|
|
350
|
+
if (parsedReport.success) {
|
|
351
|
+
reportData = parsedReport.data;
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Stderr only — stdout is the MCP transport. Log the first issue so
|
|
355
|
+
// operators can spot a backend/MCP version drift.
|
|
356
|
+
console.error('[clien-mcp] report_data failed schema validation; passing through raw payload:', parsedReport.error.issues[0]);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
content: [{ type: 'text', text: reportJson.reportMarkdown }],
|
|
361
|
+
_meta: {
|
|
362
|
+
job_id: jobId,
|
|
363
|
+
status: reportJson.status ?? status,
|
|
364
|
+
project_id: startProjectId,
|
|
365
|
+
report_data: reportData,
|
|
366
|
+
...(totalCostCents !== undefined ? { total_cost_cents: totalCostCents } : {}),
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async function handleStartError(response) {
|
|
371
|
+
const text = await response.text().catch(() => '');
|
|
372
|
+
let parsed = null;
|
|
373
|
+
try {
|
|
374
|
+
parsed = JSON.parse(text);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// non-JSON body
|
|
378
|
+
}
|
|
379
|
+
const status = response.status;
|
|
380
|
+
if (status === 402 || parsed?.code === 'INSUFFICIENT_CREDITS') {
|
|
381
|
+
// Forward the structured 402 fields set by /api/validate/start so the
|
|
382
|
+
// calling agent can give the user a concrete "you have X, need Y" prompt
|
|
383
|
+
// and surface the env-correct billing URL (production vs preview vs
|
|
384
|
+
// staging). Falls back to a generic message if the server-side body
|
|
385
|
+
// shape ever drifts.
|
|
386
|
+
const balance = typeof parsed?.balance === 'number' ? parsed.balance : null;
|
|
387
|
+
const required = typeof parsed?.required === 'number' ? parsed.required : null;
|
|
388
|
+
const buyUrl = typeof parsed?.buy_url === 'string' && parsed.buy_url.length > 0
|
|
389
|
+
? parsed.buy_url
|
|
390
|
+
: 'https://clien.ai/app/billing';
|
|
391
|
+
let message;
|
|
392
|
+
if (balance !== null && required !== null) {
|
|
393
|
+
const needed = Math.max(required - balance, 1);
|
|
394
|
+
message =
|
|
395
|
+
`Insufficient credits to start a research run — you have ${balance}, ` +
|
|
396
|
+
`this run needs ${required} (${needed} more). Buy credits at ${buyUrl}.`;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
message = `Insufficient credits to start a research run. Buy more at ${buyUrl}.`;
|
|
400
|
+
}
|
|
401
|
+
throw new ResearchToolError(message);
|
|
402
|
+
}
|
|
403
|
+
if (status === 403 && parsed?.code === 'BILLING_LIMIT_EXCEEDED') {
|
|
404
|
+
throw new ResearchToolError(parsed?.message ??
|
|
405
|
+
'Monthly validation limit reached. Upgrade at https://clien.ai/app/billing.');
|
|
406
|
+
}
|
|
407
|
+
if (status === 401) {
|
|
408
|
+
throw new ResearchToolError('Authentication failed. Run clien_research again to re-authenticate.');
|
|
409
|
+
}
|
|
410
|
+
if (status === 409) {
|
|
411
|
+
throw new ResearchToolError(parsed?.message ?? 'A research run is already in progress for this project.');
|
|
412
|
+
}
|
|
413
|
+
if (status === 400) {
|
|
414
|
+
throw new ResearchToolError(parsed?.message ?? `Bad request starting research: ${truncate(text, 300)}`);
|
|
415
|
+
}
|
|
416
|
+
// 503 CIRCUIT_BREAKER_OPEN — Unit 10's third-party-fault breaker. The server
|
|
417
|
+
// body carries the user-facing message ("...try again in 30 minutes"); we
|
|
418
|
+
// forward it instead of collapsing to the generic 5xx "few minutes" wording
|
|
419
|
+
// because the breaker's whole point is signalling a *longer* outage.
|
|
420
|
+
if (status === 503 && parsed?.code === 'CIRCUIT_BREAKER_OPEN') {
|
|
421
|
+
throw new ResearchToolError(parsed?.error ??
|
|
422
|
+
parsed?.message ??
|
|
423
|
+
'Our research infrastructure is experiencing issues. Please try again in 30 minutes.');
|
|
424
|
+
}
|
|
425
|
+
if (status >= 500) {
|
|
426
|
+
throw new ResearchToolError(`Clien.ai server error (${status}). Please try again in a few minutes.`);
|
|
427
|
+
}
|
|
428
|
+
throw new ResearchToolError(`Failed to start research (HTTP ${status}): ${truncate(text, 200)}`);
|
|
429
|
+
}
|
|
430
|
+
function webJobUrl(config, jobId) {
|
|
431
|
+
return `${config.serverUrl}/app?job=${encodeURIComponent(jobId)}`;
|
|
432
|
+
}
|
|
433
|
+
function summarizeEvents(events) {
|
|
434
|
+
if (!events || events.length === 0)
|
|
435
|
+
return null;
|
|
436
|
+
const last = events[events.length - 1];
|
|
437
|
+
if (!last)
|
|
438
|
+
return null;
|
|
439
|
+
const type = last.type ?? 'event';
|
|
440
|
+
const data = last.data ?? {};
|
|
441
|
+
// Best-effort human summary, kept <100 chars.
|
|
442
|
+
if (typeof data.message === 'string' && data.message.length > 0) {
|
|
443
|
+
return truncate(`${type}: ${data.message}`, 120);
|
|
444
|
+
}
|
|
445
|
+
if (typeof data.phase === 'string') {
|
|
446
|
+
return truncate(`${type}: ${data.phase}`, 120);
|
|
447
|
+
}
|
|
448
|
+
return type;
|
|
449
|
+
}
|
|
450
|
+
function truncate(s, n) {
|
|
451
|
+
return s.length <= n ? s : `${s.slice(0, n - 1)}…`;
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=research.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research.js","sourceRoot":"","sources":["../../src/tools/research.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,wBAAwB,EAA2B,MAAM,oBAAoB,CAAA;AAEtF,8EAA8E;AAC9E,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,MAAM,CAAA;AAC/B,MAAM,sBAAsB,GAAG,MAAM,CAAA;AACrC,MAAM,uBAAuB,GAAG,MAAM,CAAA;AAEtC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;SAChC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;SAC5B,QAAQ,CACP,0EAA0E;QACxE,iFAAiF,CACpF;IACH,KAAK,EAAE,CAAC;SACL,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;SACvB,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CACP,2DAA2D;QACzD,4CAA4C,CAC/C;IACH,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,IAAI,EAAE;SACN,QAAQ,EAAE;SACV,QAAQ,CACP,sEAAsE;QACpE,4DAA4D,CAC/D;IACH,wEAAwE;IACxE,uEAAuE;IACvE,0EAA0E;IAC1E,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,gCAAgC,CAAC;SACxC,GAAG,CAAC,GAAG,EAAE,uCAAuC,CAAC;SACjD,QAAQ,EAAE;SACV,QAAQ,CACP,oEAAoE;QAClE,8DAA8D;QAC9D,oEAAoE;QACpE,gEAAgE;QAChE,+DAA+D;QAC/D,oEAAoE;QACpE,mEAAmE;QACnE,6DAA6D;QAC7D,kEAAkE;QAClE,wDAAwD,CAC3D;CACJ,CAAC,CAAA;AA6EF,MAAM,YAAY,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AAEhF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAA;AAEtE,yEAAyE;AACzE,2EAA2E;AAC3E,MAAM,cAAc,GAA2B;IAC7C,YAAY,EAAE,IAAI;IAClB,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC;IACT,SAAS,EAAE,CAAC;CACb,CAAA;AAED,SAAS,oBAAoB,CAAC,MAAc;IAC1C,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,MAAM,CAAA;IACjC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,SAAS,CAAA;IACpC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,QAAQ,CAAA;IAClD,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,QAAQ,CAAA;IAClC,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,WAAW,CAAQ;IACnB,KAAK,CAAS;IACvB,YAAY,WAAmB,EAAE,OAA4C,EAAE;QAC7E,KAAK,CAAC,WAAW,CAAC,CAAA;QAClB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACvB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAG,IAA4B,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IAChF,CAAC;CACF;AAyBD;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,qBAAqB;IACrB,eAAe;IACf,kBAAkB;CACnB,CAAC,CAAA;AAEF,KAAK,UAAU,OAAO,CACpB,GAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,OAMI,EAAE;IAEN,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,CAAA;IAC5C,MAAM,OAAO,GAAG,KAAK,EAAE,KAAa,EAAqB,EAAE;QACzD,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,MAAM,EAAE,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB;YAC9D,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;SACxB,CAAA;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5E,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAA;QAC9C,CAAC;QACD,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACnD,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACnD,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAA;IAED,IAAI,QAAQ,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAgB,CAAA;IAEtE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;YACzC,QAAQ,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAgB,CAAA;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mEAAmE;YACnE,mEAAmE;YACnE,8DAA8D;YAC9D,sBAAsB;YACtB,MAAM,WAAW,GACf,GAAG,YAAY,UAAU,IAAI,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAClE,QAAQ,CAAC,YAAY,GAAG;gBACtB,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;gBACxC,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,qEAAqE;AACrE,oBAAoB;AACpB,qEAAqE;AAErE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAc,EACd,IAAsB;IAEtB,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC1D,IAAI,CAAC,IAAI,CAAC,CAAA;QACb,MAAM,IAAI,iBAAiB,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAA;IAC3D,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;IAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAA;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAA;IAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,CAAA;IACxE,MAAM,GAAG,GAAmB;QAC1B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,SAAS;KACjB,CAAA;IAED,kEAAkE;IAClE,qEAAqE;IACrE,uCAAuC;IACvC,MAAM,SAAS,GAKX;QACF,OAAO,EAAE,IAAI;QACb,KAAK;KACN,CAAA;IACD,IAAI,UAAU;QAAE,SAAS,CAAC,SAAS,GAAG,UAAU,CAAA;IAChD,IAAI,YAAY;QAAE,SAAS,CAAC,WAAW,GAAG,YAAY,CAAA;IAEtD,IAAI,aAA0B,CAAA;IAC9B,IAAI,CAAC;QACH,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE;YAChE,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,gBAAgB;SAC5B,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAA0C,CAAA;QACpD,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,iBAAiB,CACzB,2CAA2C,gBAAgB,GAAG,IAAI,2BAA2B,CAC9F,CAAA;QACH,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;QACtB,0EAA0E;QAC1E,gEAAgE;QAChE,IAAI,aAAa,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACjC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAA;QAC5B,CAAC;QACD,MAAM,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACvC,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAEvD,CAAA;IACR,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,iBAAiB,CACzB,8EAA8E,CAC/E,CAAA;IACH,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAA;IAC7B,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,cAAc,GAAkB,SAAS,CAAC,SAAS,IAAI,IAAI,CAAA;IAEjE,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACtB,QAAQ,EAAE,CAAC;YACX,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,yBAAyB,KAAK,GAAG;SAC3C,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;IAED,iBAAiB;IACjB,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,MAAM,GAAG,SAAS,CAAC,MAAM,IAAI,cAAc,CAAA;IAC/C,IAAI,uBAAuB,GAAG,CAAC,CAAA;IAC/B,IAAI,cAAkC,CAAA;IAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAA;IAEhG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CACzB,6EAA6E,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAC5G,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;QAED,MAAM,SAAS,CAAC,cAAc,CAAC,CAAA;QAE/B,IAAI,cAA2B,CAAA;QAC/B,IAAI,CAAC;YACH,cAAc,GAAG,MAAM,OAAO,CAC5B,GAAG,EACH,KAAK,EACL,iBAAiB,KAAK,iBAAiB,YAAY,YAAY,EAC/D,EAAE,SAAS,EAAE,sBAAsB,EAAE,CACtC,CAAA;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,GAA0C,CAAA;YACpD,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACzD,qEAAqE;gBACrE,uBAAuB,IAAI,CAAC,CAAA;gBAC5B,IAAI,uBAAuB,IAAI,CAAC,EAAE,CAAC;oBACjC,MAAM,IAAI,iBAAiB,CACzB,gGAAgG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAC/H,EAAE,KAAK,EAAE,CACV,CAAA;gBACH,CAAC;gBACD,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,IAAI,MAAM,CAAA;gBAC7E,MAAM,SAAS,CAAC,SAAS,CAAC,CAAA;gBAC1B,SAAQ;YACV,CAAC;YACD,MAAM,GAAG,CAAA;QACX,CAAC;QAED,IAAI,cAAc,CAAC,EAAE,EAAE,CAAC;YACtB,uBAAuB,GAAG,CAAC,CAAA;YAC3B,MAAM,IAAI,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA0B,CAAA;YACrF,IAAI,IAAI,EAAE,GAAG,EAAE,MAAM;gBAAE,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAA;YAC/C,IAAI,OAAO,IAAI,EAAE,GAAG,EAAE,cAAc,KAAK,QAAQ,EAAE,CAAC;gBAClD,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,CAAA;YAC1C,CAAC;YACD,IAAI,OAAO,IAAI,EAAE,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC3C,YAAY,GAAG,IAAI,CAAC,YAAY,CAAA;YAClC,CAAC;YACD,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YACnD,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,GAAG,CAAA;YAEnD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;oBACtB,QAAQ,EAAE,aAAa;oBACvB,KAAK,EAAE,CAAC;oBACR,OAAO,EAAE,aAAa,IAAI,UAAU,MAAM,EAAE;iBAC7C,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,SAAQ;QACV,CAAC;QAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QAC5D,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YACxB,oEAAoE;YACpE,qEAAqE;YACrE,sEAAsE;YACtE,IAAI,cAAc,CAAC,YAAY,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBACtD,uBAAuB,IAAI,CAAC,CAAA;gBAC5B,IAAI,uBAAuB,IAAI,CAAC,EAAE,CAAC;oBACjC,MAAM,IAAI,iBAAiB,CACzB,iGAAiG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAChI,EAAE,KAAK,EAAE,CACV,CAAA;gBACH,CAAC;gBACD,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,IAAI,MAAM,CAAA;gBAC7E,MAAM,SAAS,CAAC,SAAS,CAAC,CAAA;gBAC1B,SAAQ;YACV,CAAC;YACD,uEAAuE;YACvE,oEAAoE;YACpE,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAA;YAC1B,MAAM,IAAI,iBAAiB,CACzB,uFAAuF,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,+CAA+C,EACnK,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,oEAAoE;YACpE,MAAM,IAAI,iBAAiB,CACzB,iEAAiE,EACjE,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;QACD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,uBAAuB,IAAI,CAAC,CAAA;YAC5B,IAAI,uBAAuB,IAAI,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,iBAAiB,CACzB,0FAA0F,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EACzH,EAAE,KAAK,EAAE,CACV,CAAA;YACH,CAAC;YACD,oCAAoC;YACpC,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,IAAI,MAAM,CAAA;YAC7E,MAAM,SAAS,CAAC,SAAS,CAAC,CAAA;YAC1B,SAAQ;QACV,CAAC;QACD,4EAA4E;QAC5E,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QACxD,MAAM,IAAI,iBAAiB,CACzB,yCAAyC,cAAc,CAAC,MAAM,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EACzF,EAAE,KAAK,EAAE,CACV,CAAA;IACH,CAAC;IAED,uCAAuC;IACvC,IAAI,cAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,cAAc,GAAG,MAAM,OAAO,CAC5B,GAAG,EACH,KAAK,EACL,iBAAiB,KAAK,SAAS,EAC/B,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,uBAAuB,EAAE,CACzD,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAA0C,CAAA;QACpD,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,iBAAiB,CACzB,yCAAyC,uBAAuB,GAAG,IAAI,UAAU,KAAK,uBAAuB,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,EAC7I,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;QACvB,yEAAyE;QACzE,wDAAwD;QACxD,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAClC,MAAM,IAAI,iBAAiB,CACzB,oBAAoB,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,0BAA0B,SAAS,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,EACpI,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;QACD,MAAM,IAAI,iBAAiB,CACzB,gCAAgC,cAAc,CAAC,MAAM,UAAU,KAAK,2BAA2B,MAAM,UAAU,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,EAC/I,EAAE,KAAK,EAAE,CACV,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA0B,CAAA;IAC3F,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,iBAAiB,CACzB,2BAA2B,KAAK,aAAa,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,EAC7E,EAAE,KAAK,EAAE,CACV,CAAA;IACH,CAAC;IAED,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,sCAAsC;IACtC,MAAM,aAAa,GAAG,UAAU,CAAC,UAAU,IAAI,IAAI,CAAA;IACnD,IAAI,UAAU,GAAwD,aAAa,CAAA;IACnF,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,YAAY,GAAG,wBAAwB,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QACtE,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YACzB,UAAU,GAAG,YAAY,CAAC,IAAI,CAAA;QAChC,CAAC;aAAM,CAAC;YACN,oEAAoE;YACpE,kDAAkD;YAClD,OAAO,CAAC,KAAK,CACX,gFAAgF,EAChF,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAC7B,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,cAAc,EAAE,CAAC;QAC5D,KAAK,EAAE;YACL,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,MAAM;YACnC,UAAU,EAAE,cAAc;YAC1B,WAAW,EAAE,UAAU;YACvB,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9E;KACF,CAAA;AACH,CAAC;AAYD,KAAK,UAAU,gBAAgB,CAAC,QAAkB;IAChD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAClD,IAAI,MAAM,GAAwB,IAAI,CAAA;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;IAC9B,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE,IAAI,KAAK,sBAAsB,EAAE,CAAC;QAC9D,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,oEAAoE;QACpE,qBAAqB;QACrB,MAAM,OAAO,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;QAC3E,MAAM,QAAQ,GAAG,OAAO,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA;QAC9E,MAAM,MAAM,GACV,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAC9D,CAAC,CAAC,MAAM,CAAC,OAAO;YAChB,CAAC,CAAC,8BAA8B,CAAA;QAEpC,IAAI,OAAe,CAAA;QACnB,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,OAAO,EAAE,CAAC,CAAC,CAAA;YAC9C,OAAO;gBACL,2DAA2D,OAAO,IAAI;oBACtE,kBAAkB,QAAQ,KAAK,MAAM,0BAA0B,MAAM,GAAG,CAAA;QAC5E,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,6DAA6D,MAAM,GAAG,CAAA;QAClF,CAAC;QACD,MAAM,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;IACtC,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE,IAAI,KAAK,wBAAwB,EAAE,CAAC;QAChE,MAAM,IAAI,iBAAiB,CACzB,MAAM,EAAE,OAAO;YACb,4EAA4E,CAC/E,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,iBAAiB,CACzB,qEAAqE,CACtE,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,iBAAiB,CACzB,MAAM,EAAE,OAAO,IAAI,yDAAyD,CAC7E,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,iBAAiB,CACzB,MAAM,EAAE,OAAO,IAAI,kCAAkC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAC3E,CAAA;IACH,CAAC;IACD,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,qEAAqE;IACrE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE,IAAI,KAAK,sBAAsB,EAAE,CAAC;QAC9D,MAAM,IAAI,iBAAiB,CACzB,MAAM,EAAE,KAAK;YACX,MAAM,EAAE,OAAO;YACf,qFAAqF,CACxF,CAAA;IACH,CAAC;IACD,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,MAAM,IAAI,iBAAiB,CACzB,0BAA0B,MAAM,uCAAuC,CACxE,CAAA;IACH,CAAC;IACD,MAAM,IAAI,iBAAiB,CACzB,kCAAkC,MAAM,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CACpE,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,KAAa;IAC9C,OAAO,GAAG,MAAM,CAAC,SAAS,YAAY,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAA;AACnE,CAAC;AAED,SAAS,eAAe,CAAC,MAAgC;IACvD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACtC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;IAC5B,8CAA8C;IAC9C,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChE,OAAO,QAAQ,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;IAClD,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO,QAAQ,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,CAAS;IACpC,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAA;AACpD,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed shapes for the structured `report_data` field returned by
|
|
3
|
+
* `clien_research`. These exist so downstream agents can extract competitor
|
|
4
|
+
* info, hypotheses, etc. without hand-rolling JSON parsing.
|
|
5
|
+
*
|
|
6
|
+
* The agent (agent/src/types.ts:ValidationReportData) is the source of truth
|
|
7
|
+
* — this schema mirrors the boundary fields the MCP exposes. Unknown fields
|
|
8
|
+
* pass through (`.passthrough()`) so a backend that adds a new field doesn't
|
|
9
|
+
* break old MCP clients.
|
|
10
|
+
*
|
|
11
|
+
* On parse failure, callers should preserve the raw payload (don't drop data)
|
|
12
|
+
* — see how research.ts handles it.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
/**
|
|
16
|
+
* A competitor discovered during the research run.
|
|
17
|
+
*
|
|
18
|
+
* Per the project's MEMORY note: competitor discovery is the standout value of
|
|
19
|
+
* the report. Making this a typed surface lets downstream agents (e.g. another
|
|
20
|
+
* Claude Code session asked to "look into our top three competitors") extract
|
|
21
|
+
* the list directly from `_meta.report_data.competitors` instead of grepping
|
|
22
|
+
* the markdown.
|
|
23
|
+
*/
|
|
24
|
+
// `name` is `z.string()` (NOT min(1)) to mirror the agent's lenient
|
|
25
|
+
// CompetitorSummarySchema. If the agent emits a competitor with an empty
|
|
26
|
+
// name, validation should not fail on the consumer side — let it through and
|
|
27
|
+
// trust the agent's own validation. Stricter local validation here would
|
|
28
|
+
// trigger our passthrough fallback and add stderr noise without preventing
|
|
29
|
+
// the agent's emission.
|
|
30
|
+
//
|
|
31
|
+
// `url` validates URL format when present (matches agent's `.url()`). Still
|
|
32
|
+
// optional because the agent occasionally emits competitors without a URL
|
|
33
|
+
// (e.g. private/internal tools surfaced from interviews).
|
|
34
|
+
export const CompetitorSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
name: z.string(),
|
|
37
|
+
url: z.string().url().optional(),
|
|
38
|
+
positioning: z.string().optional(),
|
|
39
|
+
threat_level: z.enum(['low', 'medium', 'high']).optional(),
|
|
40
|
+
notes: z.string().optional(),
|
|
41
|
+
})
|
|
42
|
+
.passthrough();
|
|
43
|
+
/**
|
|
44
|
+
* The MCP-exposed shape of `_meta.report_data`. All fields optional —
|
|
45
|
+
* different runs surface different sections, and old/new agent versions may
|
|
46
|
+
* differ. `.passthrough()` preserves any backend-added fields the schema
|
|
47
|
+
* doesn't yet name, so this contract evolves additively.
|
|
48
|
+
*/
|
|
49
|
+
export const ResearchReportDataSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
competitors: z.array(CompetitorSchema).optional(),
|
|
52
|
+
hypotheses: z.array(z.unknown()).optional(),
|
|
53
|
+
insights: z.array(z.unknown()).optional(),
|
|
54
|
+
keyFindings: z.array(z.string()).optional(),
|
|
55
|
+
recommendations: z.array(z.unknown()).optional(),
|
|
56
|
+
executiveSummary: z.string().optional(),
|
|
57
|
+
overallConfidence: z.number().min(0).max(1).optional(),
|
|
58
|
+
})
|
|
59
|
+
.passthrough();
|
|
60
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/types/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;GAQG;AACH,oEAAoE;AACpE,yEAAyE;AACzE,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,wBAAwB;AACxB,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC;KACD,WAAW,EAAE,CAAA;AAIhB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC3C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC3C,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvD,CAAC;KACD,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crash-safe file writes via tmp file + rename.
|
|
3
|
+
*
|
|
4
|
+
* Both write paths in this package need the same guarantee: a SIGKILL or
|
|
5
|
+
* power loss between open and flush must NOT leave a half-written file at the
|
|
6
|
+
* target. We get that by writing to `${path}.tmp-${pid}-${Date.now()}` first,
|
|
7
|
+
* then `renameSync` to the final path — `rename(2)` is atomic within a single
|
|
8
|
+
* filesystem on POSIX, so observers see either the old contents or the new
|
|
9
|
+
* contents, never a torn write.
|
|
10
|
+
*
|
|
11
|
+
* Used by:
|
|
12
|
+
* - commands/install.ts → writes ~/.claude.json
|
|
13
|
+
* - auth/storage.ts → writes ~/.clien-ai/tokens.json.enc
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
16
|
+
import { dirname } from 'node:path';
|
|
17
|
+
/**
|
|
18
|
+
* Write `data` to `path` atomically. Creates the parent directory if missing.
|
|
19
|
+
* On error, best-effort unlink of the tmp file (which may or may not exist).
|
|
20
|
+
*/
|
|
21
|
+
export function writeFileAtomic(path, data, options = {}) {
|
|
22
|
+
const mode = options.mode ?? 0o600;
|
|
23
|
+
const dirMode = options.dirMode ?? 0o700;
|
|
24
|
+
const dir = dirname(path);
|
|
25
|
+
if (!existsSync(dir)) {
|
|
26
|
+
mkdirSync(dir, { recursive: true, mode: dirMode });
|
|
27
|
+
}
|
|
28
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
29
|
+
try {
|
|
30
|
+
writeFileSync(tmp, data, { mode });
|
|
31
|
+
renameSync(tmp, path);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
try {
|
|
35
|
+
unlinkSync(tmp);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// tmp may not have been created (e.g. mkdir failed) — benign
|
|
39
|
+
}
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=atomic-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/util/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAqB,EACrB,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,CAAA;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAA;IAExC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,IAAI,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IACtD,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QAClC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACvB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,CAAA;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,MAAM,CAAC,CAAA;IACT,CAAC;AACH,CAAC"}
|