rivet-design 0.26.2 → 0.27.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/dist/agent-variants/PreviewRuntime.js +20 -7
- package/dist/agent-variants/contracts.d.ts +116 -116
- package/dist/agent-variants/variantsTelemetry.d.ts +4 -0
- package/dist/agent-variants/variantsTelemetry.js +3 -0
- package/dist/agent-variants/workItemInput.d.ts +146 -146
- package/dist/cli/commands/mcpServe.js +1 -0
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/handoff.d.ts +1 -0
- package/dist/cli/handoff.js +20 -4
- package/dist/cli/router.js +1 -0
- package/dist/cli/telemetry.d.ts +10 -0
- package/dist/cli/telemetry.js +37 -1
- package/dist/config/pluginMetrics.js +36 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -5
- package/dist/routes/library/middleware.js +11 -5
- package/dist/server.js +8 -0
- package/dist/services/DependencyPreparationService.js +57 -19
- package/dist/services/GuidanceVerifierRunner.js +4 -4
- package/dist/services/ProjectPackageManager.d.ts +5 -0
- package/dist/services/ProjectPackageManager.js +33 -11
- package/dist/services/TelemetryService.d.ts +8 -2
- package/dist/services/TelemetryService.js +10 -0
- package/dist/services/WorktreeManager.js +5 -0
- package/dist/services/profiles/DesignProfiles.js +16 -8
- package/dist/types/guidance-verifier.d.ts +7 -5
- package/dist/types/guidance-verifier.js +14 -1
- package/dist/utils/devServerError.d.ts +5 -0
- package/dist/utils/devServerError.js +4 -0
- package/dist/utils/failureReason.d.ts +16 -0
- package/dist/utils/failureReason.js +106 -0
- package/dist/utils/hostedLogPrivacy.d.ts +1 -0
- package/dist/utils/hostedLogPrivacy.js +10 -4
- package/dist/utils/skills/claude-skill.d.ts +1 -1
- package/dist/utils/skills/claude-skill.js +1 -1
- package/dist/utils/skills/cli-guidance.d.ts +1 -1
- package/dist/utils/skills/cli-guidance.js +3 -3
- package/dist/utils/skills/cursor-rules.d.ts +1 -1
- package/dist/utils/skills/cursor-rules.js +1 -1
- package/package.json +3 -3
- package/src/ui/dist/assets/{LibraryEntry-92R_T-Xa.js → LibraryEntry-RI9Lc7P3.js} +9 -9
- package/src/ui/dist/assets/main-DgrLzS5j.js +497 -0
- package/src/ui/dist/index.html +1 -1
- package/src/ui/dist/assets/main-DgEfJvYu.js +0 -497
|
@@ -182,6 +182,7 @@ const buildMcpServer = async (options = {}) => {
|
|
|
182
182
|
durationMs: Date.now() - startedAt,
|
|
183
183
|
designContextKind: (0, telemetry_1.designContextKindForEnvelope)(result.envelope),
|
|
184
184
|
...(0, telemetry_1.variantContextMetadataForArgs)(command, rest),
|
|
185
|
+
...(0, telemetry_1.verificationMetadataForEnvelope)(result.envelope),
|
|
185
186
|
...callerFields,
|
|
186
187
|
source: 'mcp',
|
|
187
188
|
});
|
|
@@ -113,6 +113,7 @@ const handleServe = async (args, io) => {
|
|
|
113
113
|
stage: 'resolve_dev_server',
|
|
114
114
|
failureReason: 'operation_failed',
|
|
115
115
|
durationMs: Date.now() - serverStartStartedAt,
|
|
116
|
+
error,
|
|
116
117
|
});
|
|
117
118
|
await telemetry.shutdown();
|
|
118
119
|
await logForwarder.shutdown();
|
|
@@ -299,6 +300,7 @@ const handleServe = async (args, io) => {
|
|
|
299
300
|
stage: 'start_editor_server',
|
|
300
301
|
failureReason: 'operation_failed',
|
|
301
302
|
durationMs: Date.now() - serverStartStartedAt,
|
|
303
|
+
error,
|
|
302
304
|
});
|
|
303
305
|
await telemetry.shutdown();
|
|
304
306
|
await logForwarder.shutdown();
|
package/dist/cli/handoff.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type CallerTelemetryFields = {
|
|
|
35
35
|
callerAgent?: string;
|
|
36
36
|
callerModel?: string;
|
|
37
37
|
callerAgentSource?: 'handoff' | 'env';
|
|
38
|
+
callerRunId?: string;
|
|
38
39
|
};
|
|
39
40
|
export declare const callerTelemetryFieldsFromArgs: (args: string[], env?: NodeJS.ProcessEnv) => CallerTelemetryFields;
|
|
40
41
|
export declare const initiatorFor: (handoff: CallerHandoff | null) => WorkItemInitiator;
|
package/dist/cli/handoff.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initiatorFor = exports.callerTelemetryFieldsFromArgs = exports.detectCallerAgentFromEnv = exports.callerAgentEvidenceFromEnv = exports.detectCallerAgentsFromEnv = exports.parseHandoff = exports.HANDOFF_ENV_VARS = exports.CALLER_AGENT_ALLOWLIST = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
5
|
exports.CALLER_AGENT_ALLOWLIST = ['claude', 'codex', 'cursor'];
|
|
5
6
|
exports.HANDOFF_ENV_VARS = {
|
|
6
7
|
agent: 'RIVET_CALLER_AGENT',
|
|
@@ -84,6 +85,11 @@ const callerAgentEvidenceFromEnv = (env = process.env) => ENV_MARKERS_BY_AGENT.m
|
|
|
84
85
|
exports.callerAgentEvidenceFromEnv = callerAgentEvidenceFromEnv;
|
|
85
86
|
const detectCallerAgentFromEnv = (env = process.env) => (0, exports.detectCallerAgentsFromEnv)(env)[0];
|
|
86
87
|
exports.detectCallerAgentFromEnv = detectCallerAgentFromEnv;
|
|
88
|
+
const CALLER_RUN_ID_HEX_LENGTH = 16;
|
|
89
|
+
const callerRunIdForSession = (session) => (0, node_crypto_1.createHash)('sha256')
|
|
90
|
+
.update(session)
|
|
91
|
+
.digest('hex')
|
|
92
|
+
.slice(0, CALLER_RUN_ID_HEX_LENGTH);
|
|
87
93
|
const callerTelemetryFieldsFromArgs = (args, env = process.env) => {
|
|
88
94
|
const valueOf = (flag) => {
|
|
89
95
|
const index = args.indexOf(flag);
|
|
@@ -95,15 +101,25 @@ const callerTelemetryFieldsFromArgs = (args, env = process.env) => {
|
|
|
95
101
|
callerSession: valueOf('--caller-session'),
|
|
96
102
|
handoffJson: valueOf('--handoff'),
|
|
97
103
|
}, env);
|
|
98
|
-
|
|
104
|
+
const handoff = parsed.ok ? parsed.handoff : null;
|
|
105
|
+
const session = handoff?.session ??
|
|
106
|
+
nonEmpty(env.CODEX_THREAD_ID) ??
|
|
107
|
+
nonEmpty(env.CLAUDE_CODE_SESSION_ID);
|
|
108
|
+
const callerRunId = session ? callerRunIdForSession(session) : undefined;
|
|
109
|
+
const runId = callerRunId ? { callerRunId } : {};
|
|
110
|
+
if (handoff) {
|
|
99
111
|
return {
|
|
100
|
-
callerAgent:
|
|
101
|
-
callerModel:
|
|
112
|
+
callerAgent: handoff.agent,
|
|
113
|
+
callerModel: handoff.model,
|
|
102
114
|
callerAgentSource: 'handoff',
|
|
115
|
+
...runId,
|
|
103
116
|
};
|
|
104
117
|
}
|
|
105
118
|
const detected = (0, exports.detectCallerAgentFromEnv)(env);
|
|
106
|
-
|
|
119
|
+
if (detected) {
|
|
120
|
+
return { callerAgent: detected, callerAgentSource: 'env', ...runId };
|
|
121
|
+
}
|
|
122
|
+
return runId;
|
|
107
123
|
};
|
|
108
124
|
exports.callerTelemetryFieldsFromArgs = callerTelemetryFieldsFromArgs;
|
|
109
125
|
const initiatorFor = (handoff) => handoff
|
package/dist/cli/router.js
CHANGED
|
@@ -309,6 +309,7 @@ const runCliControlCommand = async (args, io = io_1.processCliIO) => {
|
|
|
309
309
|
designContextKind: (0, telemetry_1.designContextKindForEnvelope)(result.envelope),
|
|
310
310
|
...(0, telemetry_1.variantContextMetadataForArgs)(command, rest),
|
|
311
311
|
...(0, telemetry_1.browserMetadataForEnvelope)(result.envelope),
|
|
312
|
+
...(0, telemetry_1.verificationMetadataForEnvelope)(result.envelope),
|
|
312
313
|
...(0, handoff_1.callerTelemetryFieldsFromArgs)(rest),
|
|
313
314
|
});
|
|
314
315
|
telemetry.reportInstallRefActivations();
|
package/dist/cli/telemetry.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CliEnvelope } from './envelope';
|
|
2
2
|
import type { BrowserOpenResult } from './browser';
|
|
3
3
|
import { type ServerUnavailableReason } from './serverUnavailable';
|
|
4
|
+
import { type GuidanceVerifierErrorReason } from '../types/guidance-verifier';
|
|
4
5
|
export type { ServerUnavailableReason } from './serverUnavailable';
|
|
5
6
|
export type DesignContextKind = 'arena' | 'pinterest' | 'reference' | 'unsupported' | 'website';
|
|
6
7
|
export type VariantContextKind = 'image' | 'variant' | 'video' | 'website';
|
|
@@ -14,6 +15,15 @@ export type BrowserMetadata = {
|
|
|
14
15
|
browserAction?: BrowserOpenResult['action'];
|
|
15
16
|
browserReason?: BrowserOpenResult['reason'];
|
|
16
17
|
};
|
|
18
|
+
declare const COMPLETED_VERIFICATION_STATUSES: readonly ["pass", "fail", "blocked"];
|
|
19
|
+
type CompletedVerificationStatus = (typeof COMPLETED_VERIFICATION_STATUSES)[number];
|
|
20
|
+
export type VerificationOutcome = CompletedVerificationStatus | GuidanceVerifierErrorReason;
|
|
21
|
+
export type VerificationMetadata = {
|
|
22
|
+
verificationOutcome?: VerificationOutcome;
|
|
23
|
+
verificationCheckCount?: number;
|
|
24
|
+
verificationDurationMs?: number;
|
|
25
|
+
};
|
|
26
|
+
export declare const verificationMetadataForEnvelope: (envelope: CliEnvelope) => VerificationMetadata;
|
|
17
27
|
export declare const browserMetadataForEnvelope: (envelope: CliEnvelope) => BrowserMetadata;
|
|
18
28
|
export declare const serverUnavailableReasonForEnvelope: (envelope: CliEnvelope) => ServerUnavailableReason | undefined;
|
|
19
29
|
export declare const designContextKindForEnvelope: (envelope: CliEnvelope) => DesignContextKind | undefined;
|
package/dist/cli/telemetry.js
CHANGED
|
@@ -1,10 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.variantContextMetadataForArgs = exports.designContextKindForEnvelope = exports.serverUnavailableReasonForEnvelope = exports.browserMetadataForEnvelope = void 0;
|
|
3
|
+
exports.variantContextMetadataForArgs = exports.designContextKindForEnvelope = exports.serverUnavailableReasonForEnvelope = exports.browserMetadataForEnvelope = exports.verificationMetadataForEnvelope = void 0;
|
|
4
4
|
const serverUnavailable_1 = require("./serverUnavailable");
|
|
5
|
+
const guidance_verifier_1 = require("../types/guidance-verifier");
|
|
6
|
+
const COMPLETED_VERIFICATION_STATUSES = ['pass', 'fail', 'blocked'];
|
|
7
|
+
const isCompletedVerificationStatus = (value) => typeof value === 'string' &&
|
|
8
|
+
COMPLETED_VERIFICATION_STATUSES.includes(value);
|
|
9
|
+
const isGuidanceVerifierErrorReason = (value) => typeof value === 'string' &&
|
|
10
|
+
guidance_verifier_1.GUIDANCE_VERIFIER_ERROR_REASONS.includes(value);
|
|
11
|
+
const isNonNegativeDuration = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
12
|
+
const verificationOutcomeForReport = (report) => {
|
|
13
|
+
if (report.execution === 'completed' &&
|
|
14
|
+
isCompletedVerificationStatus(report.status)) {
|
|
15
|
+
return report.status;
|
|
16
|
+
}
|
|
17
|
+
if (report.execution === 'error' &&
|
|
18
|
+
isGuidanceVerifierErrorReason(report.reason)) {
|
|
19
|
+
return report.reason;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
5
23
|
const recordOf = (value) => value !== null && typeof value === 'object'
|
|
6
24
|
? value
|
|
7
25
|
: {};
|
|
26
|
+
const verificationMetadataForEnvelope = (envelope) => {
|
|
27
|
+
if (envelope.command !== 'context.verify')
|
|
28
|
+
return {};
|
|
29
|
+
const report = recordOf(envelope.ok ? envelope.data : envelope.error.details);
|
|
30
|
+
const verificationOutcome = verificationOutcomeForReport(report);
|
|
31
|
+
const verificationCheckCount = Array.isArray(report.checks)
|
|
32
|
+
? report.checks.length
|
|
33
|
+
: undefined;
|
|
34
|
+
const verificationDurationMs = isNonNegativeDuration(report.durationMs)
|
|
35
|
+
? report.durationMs
|
|
36
|
+
: undefined;
|
|
37
|
+
return {
|
|
38
|
+
...(verificationOutcome !== undefined ? { verificationOutcome } : {}),
|
|
39
|
+
...(verificationCheckCount !== undefined ? { verificationCheckCount } : {}),
|
|
40
|
+
...(verificationDurationMs !== undefined ? { verificationDurationMs } : {}),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
exports.verificationMetadataForEnvelope = verificationMetadataForEnvelope;
|
|
8
44
|
const browserMetadataForEnvelope = (envelope) => {
|
|
9
45
|
if (envelope.command !== 'open')
|
|
10
46
|
return {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pluginMetricProperties = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const failureReason_1 = require("../utils/failureReason");
|
|
5
6
|
const count = zod_1.z.number().finite().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
6
7
|
const metricProperties = zod_1.z.object({
|
|
7
8
|
command: zod_1.z
|
|
@@ -19,6 +20,17 @@ const metricProperties = zod_1.z.object({
|
|
|
19
20
|
'variants.cancel',
|
|
20
21
|
'reference.fetch',
|
|
21
22
|
'evidence.capture',
|
|
23
|
+
'context.add',
|
|
24
|
+
'context.update',
|
|
25
|
+
'context.list',
|
|
26
|
+
'context.show',
|
|
27
|
+
'context.enable',
|
|
28
|
+
'context.disable',
|
|
29
|
+
'context.remove',
|
|
30
|
+
'context.resolve',
|
|
31
|
+
'context.inspect',
|
|
32
|
+
'context.verifier',
|
|
33
|
+
'context.verify',
|
|
22
34
|
])
|
|
23
35
|
.optional(),
|
|
24
36
|
tool_name: zod_1.z
|
|
@@ -112,8 +124,12 @@ const metricProperties = zod_1.z.object({
|
|
|
112
124
|
'STATIC_PREVIEW_FAILED',
|
|
113
125
|
'DEV_SERVER_FAILED',
|
|
114
126
|
'PREVIEW_UNAVAILABLE',
|
|
127
|
+
'DEPENDENCY_INSTALL_FAILED',
|
|
128
|
+
'DEV_SERVER_START_FAILED',
|
|
129
|
+
'DEV_SCRIPT_MISSING',
|
|
115
130
|
])
|
|
116
131
|
.optional(),
|
|
132
|
+
reason_code: zod_1.z.enum(failureReason_1.FAILURE_REASON_CODES).optional(),
|
|
117
133
|
failure_reason: zod_1.z
|
|
118
134
|
.enum([
|
|
119
135
|
'ack_failed',
|
|
@@ -169,6 +185,26 @@ const metricProperties = zod_1.z.object({
|
|
|
169
185
|
'editor_server_start_failed',
|
|
170
186
|
])
|
|
171
187
|
.optional(),
|
|
188
|
+
verification_outcome: zod_1.z
|
|
189
|
+
.enum([
|
|
190
|
+
'pass',
|
|
191
|
+
'fail',
|
|
192
|
+
'blocked',
|
|
193
|
+
'package_required',
|
|
194
|
+
'verifier_missing',
|
|
195
|
+
'verifier_stale',
|
|
196
|
+
'cancelled',
|
|
197
|
+
'timeout',
|
|
198
|
+
'output_limit',
|
|
199
|
+
'process_failed',
|
|
200
|
+
'invalid_result',
|
|
201
|
+
'invalid_evidence',
|
|
202
|
+
'cleanup_failed',
|
|
203
|
+
'setup_failed',
|
|
204
|
+
])
|
|
205
|
+
.optional(),
|
|
206
|
+
verification_check_count: count.int().optional(),
|
|
207
|
+
verification_duration_ms: count.optional(),
|
|
172
208
|
ok: zod_1.z.boolean().optional(),
|
|
173
209
|
is_recoverable: zod_1.z.boolean().optional(),
|
|
174
210
|
is_fresh: zod_1.z.boolean().optional(),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ProjectDetectionService } from './index-core';
|
|
2
|
+
import { AuthService } from './services/AuthService';
|
|
2
3
|
import { SessionBridgeService } from './services/SessionBridgeService';
|
|
3
4
|
import type { Framework } from './services/ProjectDetectionService';
|
|
4
5
|
import { DevServerRuntimeService, type StartDevServerResult } from './services/DevServerRuntimeService';
|
|
@@ -75,6 +76,24 @@ export declare const authCommandOptionsForArgs: (args: string[]) => {
|
|
|
75
76
|
};
|
|
76
77
|
export declare const rememberInstallRef: (args: string[], downloadType?: string) => string[];
|
|
77
78
|
export declare const authFailureReasonForError: (error: string | undefined) => "oauth_failed" | "timeout";
|
|
79
|
+
export declare const signInWithoutBrowser: (authService?: AuthService) => Promise<{
|
|
80
|
+
success: boolean;
|
|
81
|
+
token?: string | undefined;
|
|
82
|
+
refreshToken?: string | undefined;
|
|
83
|
+
user?: {
|
|
84
|
+
id: string;
|
|
85
|
+
email: string;
|
|
86
|
+
displayName?: string | undefined;
|
|
87
|
+
} | undefined;
|
|
88
|
+
profile?: {
|
|
89
|
+
remaining_spend_usd?: number | null | undefined;
|
|
90
|
+
monthly_limit_usd?: number | null | undefined;
|
|
91
|
+
current_month_spend_usd?: number | null | undefined;
|
|
92
|
+
total_spend_usd?: number | null | undefined;
|
|
93
|
+
} | null | undefined;
|
|
94
|
+
error?: string | undefined;
|
|
95
|
+
message?: string | undefined;
|
|
96
|
+
}>;
|
|
78
97
|
export declare const main: (args?: string[]) => Promise<void>;
|
|
79
98
|
export declare const parseArgs: (args: string[]) => CLIOptions;
|
|
80
99
|
export declare const handleLogin: (options?: {
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.handleLogin = exports.parseArgs = exports.main = exports.authFailureReasonForError = exports.rememberInstallRef = exports.authCommandOptionsForArgs = exports.registerOwnedDevServerSignalHandlers = exports.isAgentVariantsEnabled = exports.updateOwnedDevServerHealthOnExit = exports.buildAgentDevServerHealth = exports.resolveAgentDevServer = exports.buildAgentDevServerContext = exports.DEFAULT_USER_PORT = exports.DEFAULT_PORT = void 0;
|
|
39
|
+
exports.handleLogin = exports.parseArgs = exports.main = exports.signInWithoutBrowser = exports.authFailureReasonForError = exports.rememberInstallRef = exports.authCommandOptionsForArgs = exports.registerOwnedDevServerSignalHandlers = exports.isAgentVariantsEnabled = exports.updateOwnedDevServerHealthOnExit = exports.buildAgentDevServerHealth = exports.resolveAgentDevServer = exports.buildAgentDevServerContext = exports.DEFAULT_USER_PORT = exports.DEFAULT_PORT = void 0;
|
|
40
40
|
/* eslint-disable no-console */
|
|
41
41
|
const dotenv = __importStar(require("dotenv"));
|
|
42
42
|
const crypto_1 = require("crypto");
|
|
@@ -260,6 +260,18 @@ const discardInstallRefForValidatedSession = () => {
|
|
|
260
260
|
};
|
|
261
261
|
const authFailureReasonForError = (error) => error?.trim().toLowerCase() === 'timeout' ? 'timeout' : 'oauth_failed';
|
|
262
262
|
exports.authFailureReasonForError = authFailureReasonForError;
|
|
263
|
+
const signInWithoutBrowser = async (authService = new AuthService_1.AuthService()) => {
|
|
264
|
+
console.log('\n🔐 Authentication required');
|
|
265
|
+
console.log('Open this link to sign in:');
|
|
266
|
+
return authService.loginWithGoogle({
|
|
267
|
+
source: 'cli',
|
|
268
|
+
noBrowser: true,
|
|
269
|
+
onAuthUrl: (url) => {
|
|
270
|
+
console.log(`\n ${url}\n`);
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
exports.signInWithoutBrowser = signInWithoutBrowser;
|
|
263
275
|
const main = async (args = process.argv.slice(2)) => {
|
|
264
276
|
if (args.includes('--no-telemetry'))
|
|
265
277
|
process.env.RIVET_TELEMETRY_DISABLED = '1';
|
|
@@ -452,10 +464,7 @@ const main = async (args = process.argv.slice(2)) => {
|
|
|
452
464
|
process.exit(1);
|
|
453
465
|
}
|
|
454
466
|
if (options.noBrowser) {
|
|
455
|
-
|
|
456
|
-
console.log('Opening browser for Google login...\n');
|
|
457
|
-
const authService = new AuthService_1.AuthService();
|
|
458
|
-
const loginResult = await authService.loginWithGoogle();
|
|
467
|
+
const loginResult = await (0, exports.signInWithoutBrowser)();
|
|
459
468
|
if (!loginResult.success) {
|
|
460
469
|
console.log(`\n❌ Authentication failed: ${loginResult.error || loginResult.message}`);
|
|
461
470
|
process.exit(1);
|
|
@@ -45,13 +45,18 @@ const authenticateLibrary = (token, sessions) => (req, res, next) => {
|
|
|
45
45
|
next();
|
|
46
46
|
};
|
|
47
47
|
exports.authenticateLibrary = authenticateLibrary;
|
|
48
|
+
const HANDLED_ERROR_KEY = 'libraryError';
|
|
48
49
|
const trackLibraryOperation = (telemetry) => (operation) => (_req, res, next) => {
|
|
49
50
|
const startedAt = Date.now();
|
|
50
|
-
res.once('finish', () =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
res.once('finish', () => {
|
|
52
|
+
const isCompleted = res.statusCode < 400;
|
|
53
|
+
telemetry?.trackLibraryOperation({
|
|
54
|
+
operation,
|
|
55
|
+
outcome: isCompleted ? 'completed' : 'failed',
|
|
56
|
+
durationMs: Date.now() - startedAt,
|
|
57
|
+
error: isCompleted ? undefined : res.locals[HANDLED_ERROR_KEY],
|
|
58
|
+
});
|
|
59
|
+
});
|
|
55
60
|
next();
|
|
56
61
|
};
|
|
57
62
|
exports.trackLibraryOperation = trackLibraryOperation;
|
|
@@ -73,6 +78,7 @@ const libraryErrorStatus = (code) => {
|
|
|
73
78
|
}
|
|
74
79
|
};
|
|
75
80
|
const handleLibraryError = (error, _req, res, _next) => {
|
|
81
|
+
res.locals[HANDLED_ERROR_KEY] = error;
|
|
76
82
|
const message = (0, errorMessage_1.errorMessage)(error);
|
|
77
83
|
if (error instanceof libraryErrors_1.LibraryError) {
|
|
78
84
|
res
|
package/dist/server.js
CHANGED
|
@@ -370,6 +370,14 @@ const startServer = async (options) => {
|
|
|
370
370
|
app.get('/rivet/favicon.ico', (_req, res) => {
|
|
371
371
|
res.sendFile('favicon.ico', { root: exports.DIST_UI_PATH });
|
|
372
372
|
});
|
|
373
|
+
app.get(/^\/rivet\/library(?:\/.*)?$/, (req, res) => {
|
|
374
|
+
const resolvedLibraryOrigin = options.resolveLibraryOrigin?.();
|
|
375
|
+
if (!resolvedLibraryOrigin) {
|
|
376
|
+
res.sendStatus(404);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
res.redirect(302, `${new URL(resolvedLibraryOrigin).origin}${req.path}`);
|
|
380
|
+
});
|
|
373
381
|
app.get(/^\/rivet(?:\/.*)?$/, (req, res, next) => {
|
|
374
382
|
if ((0, exports.isRivetUiStaticAssetPath)(req.path)) {
|
|
375
383
|
next();
|
|
@@ -16,6 +16,7 @@ const ProjectPackageManager_1 = require("./ProjectPackageManager");
|
|
|
16
16
|
const errnoCode_1 = require("../utils/errnoCode");
|
|
17
17
|
const pathContainment_1 = require("../utils/pathContainment");
|
|
18
18
|
const errorMessage_1 = require("../utils/errorMessage");
|
|
19
|
+
const failureReason_1 = require("../utils/failureReason");
|
|
19
20
|
const subprocessEnvironment_1 = require("../utils/subprocessEnvironment");
|
|
20
21
|
const MAX_CONCURRENT_INSTALLS = 2;
|
|
21
22
|
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -43,11 +44,13 @@ const isInstallEnvironmentKey = (key) => {
|
|
|
43
44
|
INSTALL_ENVIRONMENT_KEYS.has(lower));
|
|
44
45
|
};
|
|
45
46
|
const yarnAgentForVersion = (version) => Number.parseInt(version, 10) > 1 ? 'yarn@berry' : 'yarn';
|
|
46
|
-
const preparationError = (message, detail = '') => new devServerError_1.DevServerStartError({
|
|
47
|
+
const preparationError = ({ reasonCode, message, detail = '', }) => new devServerError_1.DevServerStartError({
|
|
47
48
|
message: detail ? `${message}\n${detail}` : message,
|
|
48
49
|
reason: 'dependency_install_failed',
|
|
49
50
|
portInUse: false,
|
|
50
51
|
detail,
|
|
52
|
+
reasonCode,
|
|
53
|
+
summary: message,
|
|
51
54
|
});
|
|
52
55
|
class DependencyPreparationService {
|
|
53
56
|
pending = new Map();
|
|
@@ -78,7 +81,10 @@ class DependencyPreparationService {
|
|
|
78
81
|
catch (error) {
|
|
79
82
|
if (error instanceof devServerError_1.DevServerStartError)
|
|
80
83
|
throw error;
|
|
81
|
-
throw preparationError(
|
|
84
|
+
throw preparationError({
|
|
85
|
+
reasonCode: (0, failureReason_1.failureReasonCode)(error),
|
|
86
|
+
message: `Could not prepare preview dependencies: ${(0, errorMessage_1.errorMessage)(error)}`,
|
|
87
|
+
});
|
|
82
88
|
}
|
|
83
89
|
}
|
|
84
90
|
forget(workspaceRoot) {
|
|
@@ -116,9 +122,16 @@ class DependencyPreparationService {
|
|
|
116
122
|
.reverse()
|
|
117
123
|
.find((line) => /^\d+\.\d+\.\d+(?:-.+)?$/.test(line));
|
|
118
124
|
if (!version)
|
|
119
|
-
throw preparationError(
|
|
125
|
+
throw preparationError({
|
|
126
|
+
reasonCode: 'version_unreadable',
|
|
127
|
+
message: `Could not identify the ${project.name} version`,
|
|
128
|
+
detail: versionOutput,
|
|
129
|
+
});
|
|
120
130
|
if (project.version && version !== project.version) {
|
|
121
|
-
throw preparationError(
|
|
131
|
+
throw preparationError({
|
|
132
|
+
reasonCode: 'version_mismatch',
|
|
133
|
+
message: `This project requires ${project.name}@${project.version}, but the available command reports ${version}. Activate the project's package manager version, then retry the preview.`,
|
|
134
|
+
});
|
|
122
135
|
}
|
|
123
136
|
const agent = project.name === 'yarn' ? yarnAgentForVersion(version) : project.agent;
|
|
124
137
|
const fingerprint = await this.fingerprint(project, version);
|
|
@@ -134,7 +147,10 @@ class DependencyPreparationService {
|
|
|
134
147
|
: await this.fingerprint(project, version, true);
|
|
135
148
|
const command = (0, package_manager_detector_1.resolveCommand)(agent, project.hasLockfile ? 'frozen' : 'install', []);
|
|
136
149
|
if (!command)
|
|
137
|
-
throw preparationError(
|
|
150
|
+
throw preparationError({
|
|
151
|
+
reasonCode: 'no_install_command',
|
|
152
|
+
message: `No dependency install command is available for ${project.name}`,
|
|
153
|
+
});
|
|
138
154
|
if (project.name === 'npm')
|
|
139
155
|
command.args.push('--no-audit', '--no-fund');
|
|
140
156
|
if (this.activeInstalls >= MAX_CONCURRENT_INSTALLS) {
|
|
@@ -166,7 +182,10 @@ class DependencyPreparationService {
|
|
|
166
182
|
? completedFingerprint
|
|
167
183
|
: await this.fingerprint(project, version, true);
|
|
168
184
|
if (inputsAfterInstall !== inputsBeforeInstall) {
|
|
169
|
-
throw preparationError(
|
|
185
|
+
throw preparationError({
|
|
186
|
+
reasonCode: 'inputs_changed',
|
|
187
|
+
message: 'Dependency inputs changed during installation. Retry the preview to prepare the updated project.',
|
|
188
|
+
});
|
|
170
189
|
}
|
|
171
190
|
this.prepared.set(project.rootPath, {
|
|
172
191
|
fingerprint: completedFingerprint,
|
|
@@ -194,14 +213,20 @@ class DependencyPreparationService {
|
|
|
194
213
|
try {
|
|
195
214
|
const resolved = await promises_1.default.realpath(candidate);
|
|
196
215
|
if (!(0, pathContainment_1.isPathWithinRoot)(boundary, resolved))
|
|
197
|
-
throw preparationError(
|
|
216
|
+
throw preparationError({
|
|
217
|
+
reasonCode: 'path_outside_workspace',
|
|
218
|
+
message: `Dependency path resolves outside the preview workspace: ${candidate}`,
|
|
219
|
+
});
|
|
198
220
|
}
|
|
199
221
|
catch (error) {
|
|
200
222
|
if ((0, errnoCode_1.errnoCode)(error) !== 'ENOENT')
|
|
201
223
|
throw error;
|
|
202
224
|
const entry = await promises_1.default.lstat(candidate).catch(() => null);
|
|
203
225
|
if (entry?.isSymbolicLink())
|
|
204
|
-
throw preparationError(
|
|
226
|
+
throw preparationError({
|
|
227
|
+
reasonCode: 'dangling_symlink',
|
|
228
|
+
message: `Dependency path is a dangling symlink: ${candidate}`,
|
|
229
|
+
});
|
|
205
230
|
}
|
|
206
231
|
}
|
|
207
232
|
}
|
|
@@ -284,7 +309,7 @@ class DependencyPreparationService {
|
|
|
284
309
|
stdio: 'pipe',
|
|
285
310
|
});
|
|
286
311
|
let output = '';
|
|
287
|
-
let
|
|
312
|
+
let interruption;
|
|
288
313
|
let settleTimer;
|
|
289
314
|
const drain = (chunk) => {
|
|
290
315
|
output = (output + chunk.toString()).slice(-OUTPUT_TAIL_LIMIT);
|
|
@@ -292,35 +317,48 @@ class DependencyPreparationService {
|
|
|
292
317
|
proc.stdout?.on('data', drain);
|
|
293
318
|
proc.stderr?.on('data', drain);
|
|
294
319
|
proc.stdin?.end();
|
|
295
|
-
const stop = () => {
|
|
296
|
-
|
|
320
|
+
const stop = (cause) => {
|
|
321
|
+
interruption ??= cause;
|
|
297
322
|
void OperatingSystemServices_1.selectedOperatingSystemServices.projectProcesses
|
|
298
323
|
.terminate(proc, { isForceful: true })
|
|
299
324
|
.catch(() => undefined);
|
|
300
325
|
settleTimer ??= setTimeout(() => {
|
|
301
326
|
cleanup();
|
|
302
|
-
reject(preparationError(
|
|
327
|
+
reject(preparationError({
|
|
328
|
+
reasonCode: 'installer_unstoppable',
|
|
329
|
+
message: `Dependency preparation could not be stopped: ${project.name} ${args.join(' ')} is still running. Stop the process, then retry the preview.`,
|
|
330
|
+
detail: output.trim(),
|
|
331
|
+
}));
|
|
303
332
|
}, this.terminationSettleTimeoutMs);
|
|
304
333
|
};
|
|
305
|
-
const
|
|
306
|
-
|
|
334
|
+
const abort = () => stop('aborted');
|
|
335
|
+
const timer = setTimeout(() => stop('timeout'), timeoutMs);
|
|
336
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
307
337
|
if (signal?.aborted)
|
|
308
|
-
|
|
338
|
+
abort();
|
|
309
339
|
const cleanup = () => {
|
|
310
340
|
clearTimeout(timer);
|
|
311
341
|
clearTimeout(settleTimer);
|
|
312
|
-
signal?.removeEventListener('abort',
|
|
342
|
+
signal?.removeEventListener('abort', abort);
|
|
313
343
|
};
|
|
314
344
|
proc.once('error', (error) => {
|
|
315
345
|
cleanup();
|
|
316
|
-
reject(preparationError(
|
|
346
|
+
reject(preparationError({
|
|
347
|
+
reasonCode: (0, failureReason_1.failureReasonCode)(error),
|
|
348
|
+
message: `Could not run ${project.name} ${args.join(' ')}: ${error.message}`,
|
|
349
|
+
detail: output,
|
|
350
|
+
}));
|
|
317
351
|
});
|
|
318
352
|
proc.once('close', (code) => {
|
|
319
353
|
cleanup();
|
|
320
|
-
if (code === 0 && !
|
|
354
|
+
if (code === 0 && !interruption)
|
|
321
355
|
resolve(output);
|
|
322
356
|
else
|
|
323
|
-
reject(preparationError(
|
|
357
|
+
reject(preparationError({
|
|
358
|
+
reasonCode: interruption ?? 'install_failed',
|
|
359
|
+
message: `Dependency preparation failed: ${project.name} ${args.join(' ')} (${interruption ? 'cancelled or timed out' : `exit ${code}`}). Fix the installation error, then retry the preview.`,
|
|
360
|
+
detail: output.trim(),
|
|
361
|
+
}));
|
|
324
362
|
});
|
|
325
363
|
});
|
|
326
364
|
}
|
|
@@ -254,16 +254,16 @@ const executeGuidanceVerifier = async (args) => {
|
|
|
254
254
|
durationMs: Date.now() - started,
|
|
255
255
|
});
|
|
256
256
|
try {
|
|
257
|
-
await promises_1.default.mkdir(args.runsDirectory, { recursive: true, mode: 0o700 });
|
|
258
|
-
runDirectory = await promises_1.default.mkdtemp(path_1.default.join(await promises_1.default.realpath(args.runsDirectory), 'run-'));
|
|
259
|
-
if (args.signal?.aborted)
|
|
260
|
-
return failed('cancelled');
|
|
261
257
|
if (args.packet.verifierState === 'missing')
|
|
262
258
|
return failed('verifier_missing');
|
|
263
259
|
if (args.packet.verifierState === 'stale')
|
|
264
260
|
return failed('verifier_stale');
|
|
265
261
|
if (!args.packet.verifier.package)
|
|
266
262
|
return failed('package_required');
|
|
263
|
+
await promises_1.default.mkdir(args.runsDirectory, { recursive: true, mode: 0o700 });
|
|
264
|
+
runDirectory = await promises_1.default.mkdtemp(path_1.default.join(await promises_1.default.realpath(args.runsDirectory), 'run-'));
|
|
265
|
+
if (args.signal?.aborted)
|
|
266
|
+
return failed('cancelled');
|
|
267
267
|
const executable = guidance_verifier_1.GuidanceVerifierPackageSchema.parse(args.packet.verifier.package);
|
|
268
268
|
const packageDirectory = await (0, retainedTextFiles_1.materializeTextFiles)({
|
|
269
269
|
files: executable.files,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { type Agent } from 'package-manager-detector';
|
|
2
|
+
import type { FailureReasonCode } from '../utils/failureReason';
|
|
2
3
|
export type PreviewPackageManager = 'npm' | 'yarn' | 'pnpm';
|
|
4
|
+
export declare class PackageManagerError extends Error {
|
|
5
|
+
readonly reasonCode: FailureReasonCode;
|
|
6
|
+
constructor(reasonCode: FailureReasonCode, message: string);
|
|
7
|
+
}
|
|
3
8
|
export interface ProjectPackageManager {
|
|
4
9
|
name: PreviewPackageManager;
|
|
5
10
|
agent: Agent;
|