rivet-design 0.26.2 → 0.27.1
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/DirectionRefReservations.d.ts +11 -0
- package/dist/agent-variants/DirectionRefReservations.js +47 -0
- package/dist/agent-variants/PreviewRuntime.js +20 -7
- package/dist/agent-variants/SessionStore.d.ts +10 -1
- package/dist/agent-variants/SessionStore.js +25 -2
- package/dist/agent-variants/WorkItemRequestTimes.d.ts +6 -0
- package/dist/agent-variants/WorkItemRequestTimes.js +21 -0
- package/dist/agent-variants/WorktreeOrchestrator.d.ts +2 -1
- package/dist/agent-variants/WorktreeOrchestrator.js +92 -78
- package/dist/agent-variants/contracts.d.ts +120 -116
- package/dist/agent-variants/existingStaticEntry.d.ts +34 -0
- package/dist/agent-variants/existingStaticEntry.js +136 -0
- package/dist/agent-variants/staticPreviewFiles.js +1 -1
- package/dist/agent-variants/variant-project-runtime.d.ts +2 -1
- package/dist/agent-variants/variantsTelemetry.d.ts +8 -0
- package/dist/agent-variants/variantsTelemetry.js +5 -0
- package/dist/agent-variants/workItemInput.d.ts +168 -146
- package/dist/agent-variants/workItemInput.js +12 -1
- package/dist/cli/commands/directionTarget.d.ts +23 -0
- package/dist/cli/commands/directionTarget.js +87 -0
- package/dist/cli/commands/mcpServe.js +1 -0
- package/dist/cli/commands/open.js +28 -4
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/commands/variants.js +36 -43
- package/dist/cli/envelope.d.ts +1 -1
- package/dist/cli/envelope.js +1 -0
- package/dist/cli/handoff.d.ts +1 -0
- package/dist/cli/handoff.js +20 -4
- package/dist/cli/projectRuntimeUpgrade.d.ts +11 -0
- package/dist/cli/projectRuntimeUpgrade.js +43 -11
- 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 +38 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -5
- package/dist/mcp/explorationSchema.js +1 -1
- package/dist/mcp/getExploration.js +11 -5
- package/dist/proxy-middleware/proxy-config.js +7 -0
- package/dist/routes/agentVariants.js +4 -0
- package/dist/routes/cli.js +10 -1
- package/dist/routes/library/middleware.js +11 -5
- package/dist/server.js +20 -6
- package/dist/services/DependencyPreparationService.js +57 -19
- package/dist/services/GuidanceVerifierRunner.js +4 -4
- package/dist/services/ProjectLibrary.d.ts +4 -1
- package/dist/services/ProjectLibrary.js +14 -9
- package/dist/services/ProjectPackageManager.d.ts +5 -0
- package/dist/services/ProjectPackageManager.js +33 -11
- package/dist/services/ProjectRuntimeService.d.ts +8 -0
- package/dist/services/ProjectRuntimeService.js +15 -2
- package/dist/services/TelemetryService.d.ts +13 -2
- package/dist/services/TelemetryService.js +29 -0
- package/dist/services/VariantHistoryService.d.ts +8 -0
- package/dist/services/VariantHistoryService.js +40 -0
- package/dist/services/WorktreeManager.d.ts +1 -0
- package/dist/services/WorktreeManager.js +17 -0
- package/dist/services/createAgentVariantsOrchestrator.js +9 -2
- package/dist/services/directionRefs.d.ts +21 -0
- package/dist/services/directionRefs.js +148 -0
- package/dist/services/profiles/DesignProfiles.js +16 -8
- package/dist/services/projectDirections.d.ts +22 -0
- package/dist/services/projectDirections.js +38 -0
- package/dist/types/direction-ref.d.ts +36 -0
- package/dist/types/direction-ref.js +3 -0
- package/dist/types/guidance-verifier.d.ts +7 -5
- package/dist/types/guidance-verifier.js +14 -1
- package/dist/types/variant-history.d.ts +3 -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 +7 -6
- package/dist/utils/skills/cursor-rules.d.ts +1 -1
- package/dist/utils/skills/cursor-rules.js +1 -1
- package/dist/utils/subprocessEnvironment.d.ts +1 -1
- package/dist/utils/subprocessEnvironment.js +0 -1
- package/package.json +4 -4
- package/src/ui/dist/assets/LibraryEntry-BZ5EdFgA.js +37 -0
- package/src/ui/dist/assets/main-MepMmTwb.css +1 -0
- package/src/ui/dist/assets/main-RriU35pI.js +497 -0
- package/src/ui/dist/index.html +2 -2
- package/dist/services/StaticPreviewServer.d.ts +0 -8
- package/dist/services/StaticPreviewServer.js +0 -207
- package/src/ui/dist/assets/LibraryEntry-92R_T-Xa.js +0 -37
- package/src/ui/dist/assets/main-D0UmAaUe.css +0 -1
- package/src/ui/dist/assets/main-DgEfJvYu.js +0 -497
- package/src/ui/dist/fonts/Goldman-Bold.woff2 +0 -0
- package/src/ui/dist/fonts/Goldman-Regular.woff2 +0 -0
|
@@ -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
|
|
@@ -108,12 +120,17 @@ const metricProperties = zod_1.z.object({
|
|
|
108
120
|
'WAIT_TIMEOUT',
|
|
109
121
|
'SESSION_NOT_FOUND',
|
|
110
122
|
'WORK_ITEM_NOT_FOUND',
|
|
123
|
+
'DIRECTION_AMBIGUOUS',
|
|
111
124
|
'MCP_REMOVED',
|
|
112
125
|
'STATIC_PREVIEW_FAILED',
|
|
113
126
|
'DEV_SERVER_FAILED',
|
|
114
127
|
'PREVIEW_UNAVAILABLE',
|
|
128
|
+
'DEPENDENCY_INSTALL_FAILED',
|
|
129
|
+
'DEV_SERVER_START_FAILED',
|
|
130
|
+
'DEV_SCRIPT_MISSING',
|
|
115
131
|
])
|
|
116
132
|
.optional(),
|
|
133
|
+
reason_code: zod_1.z.enum(failureReason_1.FAILURE_REASON_CODES).optional(),
|
|
117
134
|
failure_reason: zod_1.z
|
|
118
135
|
.enum([
|
|
119
136
|
'ack_failed',
|
|
@@ -148,6 +165,7 @@ const metricProperties = zod_1.z.object({
|
|
|
148
165
|
'usage_error',
|
|
149
166
|
'wait_timeout',
|
|
150
167
|
'session_not_found',
|
|
168
|
+
'direction_ambiguous',
|
|
151
169
|
'mcp_removed',
|
|
152
170
|
'server_start_timeout',
|
|
153
171
|
'handshake_missing',
|
|
@@ -169,6 +187,26 @@ const metricProperties = zod_1.z.object({
|
|
|
169
187
|
'editor_server_start_failed',
|
|
170
188
|
])
|
|
171
189
|
.optional(),
|
|
190
|
+
verification_outcome: zod_1.z
|
|
191
|
+
.enum([
|
|
192
|
+
'pass',
|
|
193
|
+
'fail',
|
|
194
|
+
'blocked',
|
|
195
|
+
'package_required',
|
|
196
|
+
'verifier_missing',
|
|
197
|
+
'verifier_stale',
|
|
198
|
+
'cancelled',
|
|
199
|
+
'timeout',
|
|
200
|
+
'output_limit',
|
|
201
|
+
'process_failed',
|
|
202
|
+
'invalid_result',
|
|
203
|
+
'invalid_evidence',
|
|
204
|
+
'cleanup_failed',
|
|
205
|
+
'setup_failed',
|
|
206
|
+
])
|
|
207
|
+
.optional(),
|
|
208
|
+
verification_check_count: count.int().optional(),
|
|
209
|
+
verification_duration_ms: count.optional(),
|
|
172
210
|
ok: zod_1.z.boolean().optional(),
|
|
173
211
|
is_recoverable: zod_1.z.boolean().optional(),
|
|
174
212
|
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);
|
|
@@ -117,7 +117,7 @@ exports.explorationShape = {
|
|
|
117
117
|
baseDirectionId: v3_1.z
|
|
118
118
|
.string()
|
|
119
119
|
.optional()
|
|
120
|
-
.describe('start: sessionId:variantId — refine an existing variant in place'),
|
|
120
|
+
.describe('start: direction ref (e.g. warm-editorial-green), sessionId:variantId, or projectId/sessionId:variantId — refine an existing variant in place'),
|
|
121
121
|
project: v3_1.z.string().trim().min(1).describe('Absolute project root path'),
|
|
122
122
|
};
|
|
123
123
|
//# sourceMappingURL=explorationSchema.js.map
|
|
@@ -17,6 +17,8 @@ const ConfigManager_1 = require("../services/ConfigManager");
|
|
|
17
17
|
const ProjectLibrary_1 = require("../services/ProjectLibrary");
|
|
18
18
|
const VariantHistoryService_1 = require("../services/VariantHistoryService");
|
|
19
19
|
const historyRoots_1 = require("../services/historyRoots");
|
|
20
|
+
const directionRefs_1 = require("../services/directionRefs");
|
|
21
|
+
const projectDirections_1 = require("../services/projectDirections");
|
|
20
22
|
const profileAccess_1 = require("./profileAccess");
|
|
21
23
|
const getExploration = async (input) => {
|
|
22
24
|
const command = 'rivet_get_exploration';
|
|
@@ -68,12 +70,16 @@ const getExploration = async (input) => {
|
|
|
68
70
|
? registered.library.registry.historyRoots(registered.owner.id)
|
|
69
71
|
: [project, ...(0, historyRoots_1.nestedFreshSessionWorkdirs)(project)];
|
|
70
72
|
if (!hasLiveServer || input.includeHistory || input.sessionId) {
|
|
73
|
+
const directions = await (0, projectDirections_1.listProjectDirections)(registered
|
|
74
|
+
? {
|
|
75
|
+
kind: 'library',
|
|
76
|
+
library: registered.library,
|
|
77
|
+
projectId: registered.owner.id,
|
|
78
|
+
}
|
|
79
|
+
: { kind: 'history', history, projectPath: project });
|
|
71
80
|
const entries = registered
|
|
72
|
-
?
|
|
73
|
-
:
|
|
74
|
-
...entry,
|
|
75
|
-
projectPath,
|
|
76
|
-
}))))).flat();
|
|
81
|
+
? directions.filter((entry) => (0, directionRefs_1.isDirectionActive)(entry.manifest.status))
|
|
82
|
+
: directions;
|
|
77
83
|
data.history = entries.filter((entry) => (!input.sessionId || entry.manifest.sessionId === input.sessionId) &&
|
|
78
84
|
(!input.variantId || entry.manifest.variantId === input.variantId));
|
|
79
85
|
if (input.variantId && input.sessionId) {
|
|
@@ -13,6 +13,10 @@ const TARGET_ADDRESSED_PREVIEW_PREFIX = '/__rivet/preview';
|
|
|
13
13
|
const TARGET_ADDRESSED_PREVIEW_KIND_BASE = 'base';
|
|
14
14
|
const TARGET_ADDRESSED_PREVIEW_KIND_HISTORY = 'history';
|
|
15
15
|
const TARGET_ADDRESSED_PREVIEW_KIND_VARIANT = 'variant';
|
|
16
|
+
const CACHE_VALIDATOR_REQUEST_HEADERS = [
|
|
17
|
+
'if-none-match',
|
|
18
|
+
'if-modified-since',
|
|
19
|
+
];
|
|
16
20
|
const hasValidUserDevPort = (userPort) => Number.isInteger(userPort) && userPort > 0;
|
|
17
21
|
const resolveTargetAddressedPreview = (args) => {
|
|
18
22
|
const parsed = new URL(args.rawUrl ?? '/', 'http://rivet.local');
|
|
@@ -152,6 +156,9 @@ const createUserDevServerProxy = (userPort, userHost = 'localhost', options = {}
|
|
|
152
156
|
requestTargets.set(req, target);
|
|
153
157
|
if (target.isTargetAddressed) {
|
|
154
158
|
req.url = target.path;
|
|
159
|
+
for (const header of CACHE_VALIDATOR_REQUEST_HEADERS) {
|
|
160
|
+
delete req.headers[header];
|
|
161
|
+
}
|
|
155
162
|
}
|
|
156
163
|
};
|
|
157
164
|
const getRouterTargetForLibrary = (req) => {
|
|
@@ -543,6 +543,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
|
|
|
543
543
|
filesDir,
|
|
544
544
|
entryPath: preview.entryPath,
|
|
545
545
|
label: detail.manifest.label,
|
|
546
|
+
directionRef: detail.manifest.directionRef,
|
|
546
547
|
description: detail.manifest.brief,
|
|
547
548
|
references: detail.manifest.references,
|
|
548
549
|
previewRoutes: detail.manifest.previewRoutes,
|
|
@@ -1058,6 +1059,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
|
|
|
1058
1059
|
sessionId: entry.manifest.sessionId,
|
|
1059
1060
|
variantId: entry.manifest.variantId,
|
|
1060
1061
|
requestId: entry.manifest.requestId,
|
|
1062
|
+
directionRef: entry.manifest.directionRef,
|
|
1061
1063
|
label: entry.manifest.label,
|
|
1062
1064
|
brief: entry.manifest.brief,
|
|
1063
1065
|
references: entry.manifest.references,
|
|
@@ -1507,6 +1509,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
|
|
|
1507
1509
|
filesDir,
|
|
1508
1510
|
entryPath: preview.entryPath,
|
|
1509
1511
|
label: detail.manifest.label,
|
|
1512
|
+
directionRef: detail.manifest.directionRef,
|
|
1510
1513
|
description: detail.manifest.brief,
|
|
1511
1514
|
references: detail.manifest.references,
|
|
1512
1515
|
previewRoutes: detail.manifest.previewRoutes,
|
|
@@ -1580,6 +1583,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
|
|
|
1580
1583
|
filesDir,
|
|
1581
1584
|
entryPath: preview.entryPath,
|
|
1582
1585
|
label: detail.manifest.label,
|
|
1586
|
+
directionRef: detail.manifest.directionRef,
|
|
1583
1587
|
description: detail.manifest.brief,
|
|
1584
1588
|
references: detail.manifest.references,
|
|
1585
1589
|
previewRoutes: detail.manifest.previewRoutes,
|
package/dist/routes/cli.js
CHANGED
|
@@ -102,8 +102,17 @@ const createCliRouter = (options) => {
|
|
|
102
102
|
setImmediate(() => options.onShutdown?.());
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
|
+
const isIdleRequired = req.body?.requireIdle === true;
|
|
105
106
|
const activity = (0, runtimeActivity_1.classifyRuntimeActivity)(readStatus());
|
|
106
|
-
if (!admission.beginRetirement(activity,
|
|
107
|
+
if (!admission.beginRetirement(activity, !isIdleRequired)) {
|
|
108
|
+
if (isIdleRequired) {
|
|
109
|
+
res.status(409).json({
|
|
110
|
+
reason: 'active_work',
|
|
111
|
+
error: 'Rivet left the server running because it has active work or is already restarting.',
|
|
112
|
+
upgrade: admission.status(),
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
107
116
|
res.status(409).json({
|
|
108
117
|
reason: 'server_retiring',
|
|
109
118
|
error: 'Rivet is already restarting. Retry after the runtime handoff completes.',
|
|
@@ -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
|
@@ -212,6 +212,7 @@ const startServer = async (options) => {
|
|
|
212
212
|
? (0, crypto_1.randomBytes)(QUEUE_ACCESS_TOKEN_BYTES).toString('base64url')
|
|
213
213
|
: null;
|
|
214
214
|
const variantsOrchestrator = options.variantsRuntime?.orchestrator ?? options.agentVariantsOrchestrator;
|
|
215
|
+
await variantsOrchestrator?.loadDirectionRefs();
|
|
215
216
|
const inlineStartTracker = (0, inlineStartTracker_1.createInlineStartTracker)();
|
|
216
217
|
const runtimeAdmission = new RuntimeAdmission_1.RuntimeAdmission(telemetry_2.RIVET_VERSION);
|
|
217
218
|
const variantsBridge = options.variantsRuntime?.bridge ?? sessionBridge;
|
|
@@ -251,11 +252,16 @@ const startServer = async (options) => {
|
|
|
251
252
|
const editorHost = () => `${EMBEDDED_AGENT_LISTEN_HOST}:${listenPort}`;
|
|
252
253
|
const isPreviewRequest = (req) => req.headers.host === new URL(previewOrigin).host &&
|
|
253
254
|
(previewPort === undefined || req.socket.localPort === previewPort);
|
|
255
|
+
const rejectRequest = ({ req, res, reason, }) => {
|
|
256
|
+
log.warn(`Rejected ${req.method} ${req.path} with 403: ${reason}`);
|
|
257
|
+
telemetry?.trackRequestRejected(reason);
|
|
258
|
+
res.sendStatus(403);
|
|
259
|
+
};
|
|
254
260
|
app.use((req, res, next) => {
|
|
255
261
|
const requestHost = req.headers.host;
|
|
256
262
|
const isEditorRequest = requestHost === editorHost() && req.socket.localPort === listenPort;
|
|
257
263
|
if (!isEditorRequest && !isPreviewRequest(req)) {
|
|
258
|
-
res
|
|
264
|
+
rejectRequest({ req, res, reason: 'host_not_allowed' });
|
|
259
265
|
return;
|
|
260
266
|
}
|
|
261
267
|
next();
|
|
@@ -293,7 +299,7 @@ const startServer = async (options) => {
|
|
|
293
299
|
return;
|
|
294
300
|
}
|
|
295
301
|
if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
|
|
296
|
-
res
|
|
302
|
+
rejectRequest({ req, res, reason: 'editor_token_on_preview' });
|
|
297
303
|
return;
|
|
298
304
|
}
|
|
299
305
|
const isAppApi = req.path.toLowerCase().startsWith('/api/') &&
|
|
@@ -370,6 +376,14 @@ const startServer = async (options) => {
|
|
|
370
376
|
app.get('/rivet/favicon.ico', (_req, res) => {
|
|
371
377
|
res.sendFile('favicon.ico', { root: exports.DIST_UI_PATH });
|
|
372
378
|
});
|
|
379
|
+
app.get(/^\/rivet\/library(?:\/.*)?$/, (req, res) => {
|
|
380
|
+
const resolvedLibraryOrigin = options.resolveLibraryOrigin?.();
|
|
381
|
+
if (!resolvedLibraryOrigin) {
|
|
382
|
+
res.sendStatus(404);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
res.redirect(302, `${new URL(resolvedLibraryOrigin).origin}${req.path}`);
|
|
386
|
+
});
|
|
373
387
|
app.get(/^\/rivet(?:\/.*)?$/, (req, res, next) => {
|
|
374
388
|
if ((0, exports.isRivetUiStaticAssetPath)(req.path)) {
|
|
375
389
|
next();
|
|
@@ -430,7 +444,7 @@ const startServer = async (options) => {
|
|
|
430
444
|
PREVIEW_PORT_ALLOWED_API_RE.test(req.path.toLowerCase());
|
|
431
445
|
if (!isPreviewRequest(req) && isPreviewPath) {
|
|
432
446
|
if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
|
|
433
|
-
res
|
|
447
|
+
rejectRequest({ req, res, reason: 'editor_token_on_preview' });
|
|
434
448
|
return;
|
|
435
449
|
}
|
|
436
450
|
res.redirect(302, `${previewOrigin}${req.originalUrl}`);
|
|
@@ -503,7 +517,7 @@ const startServer = async (options) => {
|
|
|
503
517
|
});
|
|
504
518
|
app.post('/api/library/browser-launches', express_1.default.urlencoded({ extended: false }), async (req, res) => {
|
|
505
519
|
if (!(0, middleware_1.isLocalBrowserRequest)(req))
|
|
506
|
-
return res
|
|
520
|
+
return rejectRequest({ req, res, reason: 'origin_not_allowed' });
|
|
507
521
|
const view = req.body?.view;
|
|
508
522
|
if (view !== 'projects' && view !== 'references')
|
|
509
523
|
return res.sendStatus(400);
|
|
@@ -583,14 +597,14 @@ const startServer = async (options) => {
|
|
|
583
597
|
return;
|
|
584
598
|
}
|
|
585
599
|
if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
|
|
586
|
-
res
|
|
600
|
+
rejectRequest({ req, res, reason: 'editor_token_on_preview' });
|
|
587
601
|
return;
|
|
588
602
|
}
|
|
589
603
|
res.redirect(302, `${previewOrigin}${req.originalUrl}`);
|
|
590
604
|
return;
|
|
591
605
|
}
|
|
592
606
|
if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
|
|
593
|
-
res
|
|
607
|
+
rejectRequest({ req, res, reason: 'editor_token_on_preview' });
|
|
594
608
|
return;
|
|
595
609
|
}
|
|
596
610
|
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,
|
|
@@ -12,6 +12,9 @@ interface VariantReference {
|
|
|
12
12
|
sessionId: string;
|
|
13
13
|
variantId: string;
|
|
14
14
|
}
|
|
15
|
+
interface LibraryListOptions {
|
|
16
|
+
isRemovedIncluded?: boolean;
|
|
17
|
+
}
|
|
15
18
|
export declare const currentLibraryRoot: (env?: NodeJS.ProcessEnv) => string;
|
|
16
19
|
export declare class ProjectLibrary {
|
|
17
20
|
readonly registry: ProjectRegistry;
|
|
@@ -45,7 +48,7 @@ export declare class ProjectLibrary {
|
|
|
45
48
|
projectPath: string;
|
|
46
49
|
}): Promise<void>;
|
|
47
50
|
private listProject;
|
|
48
|
-
listVariants(projectId?: string): Promise<LibraryVariant[]>;
|
|
51
|
+
listVariants(projectId?: string, options?: LibraryListOptions): Promise<LibraryVariant[]>;
|
|
49
52
|
private findVariant;
|
|
50
53
|
variantSummary(reference: VariantReference): Promise<{
|
|
51
54
|
summary: VariantHistorySummary;
|
|
@@ -23,6 +23,11 @@ const currentLibraryRoot = (env = process.env) => {
|
|
|
23
23
|
: (0, ProjectRegistry_1.libraryRoot)({ channel });
|
|
24
24
|
};
|
|
25
25
|
exports.currentLibraryRoot = currentLibraryRoot;
|
|
26
|
+
const isSameDirection = (left, right) => (!left.directionRef ||
|
|
27
|
+
!right.directionRef ||
|
|
28
|
+
left.directionRef === right.directionRef) &&
|
|
29
|
+
JSON.stringify({ ...left, directionRef: undefined }) ===
|
|
30
|
+
JSON.stringify({ ...right, directionRef: undefined });
|
|
26
31
|
class ProjectLibrary {
|
|
27
32
|
registry;
|
|
28
33
|
isReadOnly;
|
|
@@ -158,7 +163,7 @@ class ProjectLibrary {
|
|
|
158
163
|
});
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
|
-
async listProject(projectId) {
|
|
166
|
+
async listProject(projectId, options) {
|
|
162
167
|
const project = this.registry.get(projectId);
|
|
163
168
|
if (!project.isAvailable && project.storageKind === 'project')
|
|
164
169
|
throw new Error('Working folder and project-local history are unavailable');
|
|
@@ -171,23 +176,23 @@ class ProjectLibrary {
|
|
|
171
176
|
: history.listVariantsFromIndex({ projectPath }))).map((entry) => ({ ...entry, projectId, projectPath }))))).flat();
|
|
172
177
|
const unique = new Map();
|
|
173
178
|
for (const entry of entries) {
|
|
174
|
-
if (
|
|
175
|
-
entry.manifest.status === '
|
|
179
|
+
if (!options.isRemovedIncluded &&
|
|
180
|
+
(entry.manifest.status === 'removed' ||
|
|
181
|
+
entry.manifest.status === 'cancelled'))
|
|
176
182
|
continue;
|
|
177
183
|
const key = `${entry.manifest.sessionId}:${entry.manifest.variantId}`;
|
|
178
184
|
const existing = unique.get(key);
|
|
179
|
-
if (existing &&
|
|
180
|
-
JSON.stringify(existing.manifest) !== JSON.stringify(entry.manifest))
|
|
185
|
+
if (existing && !isSameDirection(existing.manifest, entry.manifest))
|
|
181
186
|
throw new Error('Conflicting history identities across attached roots');
|
|
182
|
-
unique.set(key, entry);
|
|
187
|
+
unique.set(key, existing?.manifest.directionRef ? existing : entry);
|
|
183
188
|
}
|
|
184
189
|
return [...unique.values()];
|
|
185
190
|
}
|
|
186
|
-
async listVariants(projectId) {
|
|
191
|
+
async listVariants(projectId, options = {}) {
|
|
187
192
|
if (projectId)
|
|
188
|
-
return this.listProject(projectId);
|
|
193
|
+
return this.listProject(projectId, options);
|
|
189
194
|
const projects = this.registry.list();
|
|
190
|
-
const results = await Promise.allSettled(projects.map((project) => this.listProject(project.id)));
|
|
195
|
+
const results = await Promise.allSettled(projects.map((project) => this.listProject(project.id, options)));
|
|
191
196
|
return results
|
|
192
197
|
.flatMap((result, index) => {
|
|
193
198
|
if (result.status === 'fulfilled') {
|
|
@@ -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;
|