veryfront 0.1.246 → 0.1.247

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.
Files changed (35) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/agent/durable.d.ts +34 -0
  3. package/esm/src/agent/durable.d.ts.map +1 -1
  4. package/esm/src/agent/durable.js +39 -0
  5. package/esm/src/agent/index.d.ts +1 -1
  6. package/esm/src/agent/index.d.ts.map +1 -1
  7. package/esm/src/agent/index.js +1 -1
  8. package/esm/src/integrations/schema.d.ts +2 -2
  9. package/esm/src/mcp/index.d.ts +7 -2
  10. package/esm/src/mcp/index.d.ts.map +1 -1
  11. package/esm/src/mcp/index.js +7 -2
  12. package/esm/src/mcp/schemas/index.d.ts +1 -1
  13. package/esm/src/mcp/schemas/index.d.ts.map +1 -1
  14. package/esm/src/mcp/schemas/index.js +1 -1
  15. package/esm/src/mcp/schemas/mcp.schema.d.ts +13 -7
  16. package/esm/src/mcp/schemas/mcp.schema.d.ts.map +1 -1
  17. package/esm/src/mcp/schemas/mcp.schema.js +17 -7
  18. package/esm/src/mcp/server.d.ts +13 -0
  19. package/esm/src/mcp/server.d.ts.map +1 -1
  20. package/esm/src/mcp/server.js +43 -6
  21. package/esm/src/utils/version-constant.d.ts +1 -1
  22. package/esm/src/utils/version-constant.js +1 -1
  23. package/esm/src/workflow/claude-code/workspace-sync.d.ts +17 -2
  24. package/esm/src/workflow/claude-code/workspace-sync.d.ts.map +1 -1
  25. package/esm/src/workflow/claude-code/workspace-sync.js +106 -19
  26. package/package.json +1 -1
  27. package/src/deno.js +1 -1
  28. package/src/src/agent/durable.ts +83 -0
  29. package/src/src/agent/index.ts +2 -0
  30. package/src/src/mcp/index.ts +7 -2
  31. package/src/src/mcp/schemas/index.ts +1 -0
  32. package/src/src/mcp/schemas/mcp.schema.ts +20 -7
  33. package/src/src/mcp/server.ts +60 -6
  34. package/src/src/utils/version-constant.ts +1 -1
  35. package/src/src/workflow/claude-code/workspace-sync.ts +109 -20
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.246",
3
+ "version": "0.1.247",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -80,6 +80,7 @@ export type TerminalConversationRunStatus = Extract<ConversationRunProjection["s
80
80
  export type ConversationRunAppendCursorResyncResult = "advanced" | "non_appendable" | "unchanged";
81
81
  export type ConversationRunAppendRecoveryOutcome = "resumed" | "stopped" | "bubbled";
82
82
  export type ConversationRunAppendFailureOutcome = "resumed" | "stopped" | "retry_scheduled";
83
+ export type ConversationRunAppendExecutionOutcome = "resumed" | "stopped" | "retry_scheduled";
83
84
  export declare const CreateConversationRunAcceptedSchema: z.ZodPipe<z.ZodObject<{
84
85
  run: z.ZodObject<{
85
86
  runId: z.ZodOptional<z.ZodString>;
@@ -250,6 +251,39 @@ export declare function recoverConversationRunAppendFailure(input: {
250
251
  errorMessage?: string;
251
252
  run?: ConversationRunProjection;
252
253
  }>;
254
+ export declare function recoverConversationRunAppendExecution(input: {
255
+ error: unknown;
256
+ authToken: string;
257
+ apiUrl: string;
258
+ conversationId: string;
259
+ runId: string;
260
+ latestEventId: number;
261
+ latestExternalEventSequence: number;
262
+ remainingEvents: unknown[];
263
+ pendingEvents: unknown[];
264
+ cursorResyncsThisFlush: number;
265
+ consecutiveFailures: number;
266
+ maxCursorResyncsPerFlush: number;
267
+ abortSignal?: AbortSignal;
268
+ }): Promise<{
269
+ outcome: "resumed";
270
+ latestEventId: number;
271
+ latestExternalEventSequence: number;
272
+ pendingEvents: unknown[];
273
+ consecutiveFailures: number;
274
+ } | {
275
+ outcome: "stopped";
276
+ latestEventId: number;
277
+ latestExternalEventSequence: number;
278
+ disableReason?: "cursor_resyncs_exhausted" | "non_appendable" | "ignorable_append_rejection";
279
+ } | {
280
+ outcome: "retry_scheduled";
281
+ latestEventId: number;
282
+ latestExternalEventSequence: number;
283
+ pendingEvents: unknown[];
284
+ consecutiveFailures: number;
285
+ errorMessage: string;
286
+ }>;
253
287
  export declare function getConversationRun(input: {
254
288
  authToken: string;
255
289
  apiUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"durable.d.ts","sourceRoot":"","sources":["../../../src/src/agent/durable.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"durable.d.ts","sourceRoot":"","sources":["../../../src/src/agent/durable.ts"],"names":[],"mappings":"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"}
@@ -361,6 +361,45 @@ export async function recoverConversationRunAppendFailure(input) {
361
361
  ...(cursorRecovery.run ? { run: cursorRecovery.run } : {}),
362
362
  };
363
363
  }
364
+ export async function recoverConversationRunAppendExecution(input) {
365
+ const recovered = await recoverConversationRunAppendFailure({
366
+ error: input.error,
367
+ authToken: input.authToken,
368
+ apiUrl: input.apiUrl,
369
+ conversationId: input.conversationId,
370
+ runId: input.runId,
371
+ latestEventId: input.latestEventId,
372
+ latestExternalEventSequence: input.latestExternalEventSequence,
373
+ cursorResyncsThisFlush: input.cursorResyncsThisFlush,
374
+ maxCursorResyncsPerFlush: input.maxCursorResyncsPerFlush,
375
+ abortSignal: input.abortSignal,
376
+ });
377
+ if (recovered.outcome === "resumed") {
378
+ return {
379
+ outcome: "resumed",
380
+ latestEventId: recovered.latestEventId,
381
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
382
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
383
+ consecutiveFailures: 0,
384
+ };
385
+ }
386
+ if (recovered.outcome === "stopped") {
387
+ return {
388
+ outcome: "stopped",
389
+ latestEventId: recovered.latestEventId,
390
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
391
+ ...(recovered.disableReason ? { disableReason: recovered.disableReason } : {}),
392
+ };
393
+ }
394
+ return {
395
+ outcome: "retry_scheduled",
396
+ latestEventId: recovered.latestEventId,
397
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
398
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
399
+ consecutiveFailures: input.consecutiveFailures + 1,
400
+ errorMessage: recovered.errorMessage ?? "Conversation run append failed",
401
+ };
402
+ }
364
403
  async function waitForConversationRunPoll(ms, abortSignal) {
365
404
  if (ms <= 0 || abortSignal?.aborted) {
366
405
  return;
@@ -93,7 +93,7 @@ export { type ConversationRunContext, createConversationRunContext, } from "./co
93
93
  export { type ConversationRootRunContext, type ConversationRootRunDescriptor, createConversationRootRunContext, createConversationRootRunStartAdapter, prepareConversationRootRunContext, startConversationRootRun, } from "./conversation-root-run-context.js";
94
94
  export { bootstrapConversationAgentRun, type BootstrapConversationAgentRunResult, type ConversationMessageRecord, ConversationMessageRecordSchema, type ConversationRecord, ConversationRecordSchema, createConversationMessage, createConversationRecord, ensureConversationProjectLink, fetchConversationRecord, } from "./conversation-bootstrap.js";
95
95
  export { type ConversationChildLifecycleContext, type ConversationHostedLifecycleFinalizeInput, createConversationChildLifecycleAdapter, createConversationHostedLifecycleAdapter, type CreateConversationHostedLifecycleAdapterOptions, } from "./conversation-hosted-lifecycle.js";
96
- export { type ActiveConversationRunStatus, appendConversationRunEvents, AppendConversationRunEventsError, type AppendConversationRunEventsResponse, AppendConversationRunEventsResponseSchema, CompleteConversationRunResponseSchema, type ConversationAgentRunUsage, type ConversationRunAppendCursorResyncResult, type ConversationRunAppendFailureOutcome, type ConversationRunAppendRecoveryOutcome, type ConversationRunProjection, ConversationRunProjectionSchema, ConversationRunStatusSchema, type ConversationRunTargets, ConversationRunTargetsSchema, ConversationRunTerminalStateError, createConversationAgentRun, finalizeConversationAgentRun, getConversationRun, isActiveConversationRunStatus, isAppendableConversationRunProjection, isCursorMismatchConversationRunAppendError, isIgnorableConversationRunAppendError, monitorConversationRunStatus, parseAppendConversationRunEventsErrorBody, recoverConversationRunAppendFailure, recoverConversationRunCursorMismatch, resolveConversationRunTargets, resyncConversationRunAppendCursor, type TerminalConversationRunStatus, } from "./durable.js";
96
+ export { type ActiveConversationRunStatus, appendConversationRunEvents, AppendConversationRunEventsError, type AppendConversationRunEventsResponse, AppendConversationRunEventsResponseSchema, CompleteConversationRunResponseSchema, type ConversationAgentRunUsage, type ConversationRunAppendCursorResyncResult, type ConversationRunAppendExecutionOutcome, type ConversationRunAppendFailureOutcome, type ConversationRunAppendRecoveryOutcome, type ConversationRunProjection, ConversationRunProjectionSchema, ConversationRunStatusSchema, type ConversationRunTargets, ConversationRunTargetsSchema, ConversationRunTerminalStateError, createConversationAgentRun, finalizeConversationAgentRun, getConversationRun, isActiveConversationRunStatus, isAppendableConversationRunProjection, isCursorMismatchConversationRunAppendError, isIgnorableConversationRunAppendError, monitorConversationRunStatus, parseAppendConversationRunEventsErrorBody, recoverConversationRunAppendExecution, recoverConversationRunAppendFailure, recoverConversationRunCursorMismatch, resolveConversationRunTargets, resyncConversationRunAppendCursor, type TerminalConversationRunStatus, } from "./durable.js";
97
97
  export { buildInvokeAgentChildRunLifecycleCustomEvent, buildInvokeAgentChildRunProgressEvents, buildInvokeAgentChildRunStateDelta, type InvokeAgentChildRunLifecycleCustomEvent, InvokeAgentChildRunLifecycleCustomEventSchema, type InvokeAgentChildRunLifecycleValue, InvokeAgentChildRunLifecycleValueSchema, type InvokeAgentChildRunProgressEvent, type InvokeAgentChildRunProgressInput, type InvokeAgentChildRunStateDelta, InvokeAgentChildRunStateDeltaSchema, publishInvokeAgentChildRunProgress, } from "./invoke-agent-child-runs.js";
98
98
  export { type HostedChildLifecycleAdapter, type HostedChildLifecycleRunnerOptions, type HostedChildLifecycleRunResult, type HostedChildLifecycleTerminalState, runHostedChildLifecycle, } from "./hosted-child-lifecycle.js";
99
99
  export { type HostedLifecycleAdapter, type HostedLifecycleExecution, type HostedLifecycleRunnerOptions, type HostedLifecycleRunResult, type HostedLifecycleTerminalState, runHostedLifecycle, } from "./hosted-lifecycle.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/agent/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/agent/index.ts"],"names":[],"mappings":"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"}
@@ -92,7 +92,7 @@ export { createConversationRunContext, } from "./conversation-run-context.js";
92
92
  export { createConversationRootRunContext, createConversationRootRunStartAdapter, prepareConversationRootRunContext, startConversationRootRun, } from "./conversation-root-run-context.js";
93
93
  export { bootstrapConversationAgentRun, ConversationMessageRecordSchema, ConversationRecordSchema, createConversationMessage, createConversationRecord, ensureConversationProjectLink, fetchConversationRecord, } from "./conversation-bootstrap.js";
94
94
  export { createConversationChildLifecycleAdapter, createConversationHostedLifecycleAdapter, } from "./conversation-hosted-lifecycle.js";
95
- export { appendConversationRunEvents, AppendConversationRunEventsError, AppendConversationRunEventsResponseSchema, CompleteConversationRunResponseSchema, ConversationRunProjectionSchema, ConversationRunStatusSchema, ConversationRunTargetsSchema, ConversationRunTerminalStateError, createConversationAgentRun, finalizeConversationAgentRun, getConversationRun, isActiveConversationRunStatus, isAppendableConversationRunProjection, isCursorMismatchConversationRunAppendError, isIgnorableConversationRunAppendError, monitorConversationRunStatus, parseAppendConversationRunEventsErrorBody, recoverConversationRunAppendFailure, recoverConversationRunCursorMismatch, resolveConversationRunTargets, resyncConversationRunAppendCursor, } from "./durable.js";
95
+ export { appendConversationRunEvents, AppendConversationRunEventsError, AppendConversationRunEventsResponseSchema, CompleteConversationRunResponseSchema, ConversationRunProjectionSchema, ConversationRunStatusSchema, ConversationRunTargetsSchema, ConversationRunTerminalStateError, createConversationAgentRun, finalizeConversationAgentRun, getConversationRun, isActiveConversationRunStatus, isAppendableConversationRunProjection, isCursorMismatchConversationRunAppendError, isIgnorableConversationRunAppendError, monitorConversationRunStatus, parseAppendConversationRunEventsErrorBody, recoverConversationRunAppendExecution, recoverConversationRunAppendFailure, recoverConversationRunCursorMismatch, resolveConversationRunTargets, resyncConversationRunAppendCursor, } from "./durable.js";
96
96
  export { buildInvokeAgentChildRunLifecycleCustomEvent, buildInvokeAgentChildRunProgressEvents, buildInvokeAgentChildRunStateDelta, InvokeAgentChildRunLifecycleCustomEventSchema, InvokeAgentChildRunLifecycleValueSchema, InvokeAgentChildRunStateDeltaSchema, publishInvokeAgentChildRunProgress, } from "./invoke-agent-child-runs.js";
97
97
  export { runHostedChildLifecycle, } from "./hosted-child-lifecycle.js";
98
98
  export { runHostedLifecycle, } from "./hosted-lifecycle.js";
@@ -70,9 +70,9 @@ export declare const OAuthFieldSchema: z.ZodObject<{
70
70
  }, z.core.$strip>;
71
71
  export declare const OAuthConfigSchema: z.ZodObject<{
72
72
  type: z.ZodEnum<{
73
- "api-key": "api-key";
74
73
  oauth2: "oauth2";
75
74
  oauth1: "oauth1";
75
+ "api-key": "api-key";
76
76
  }>;
77
77
  provider: z.ZodOptional<z.ZodString>;
78
78
  authorizationUrl: z.ZodOptional<z.ZodString>;
@@ -310,9 +310,9 @@ export declare const IntegrationConfigSchema: z.ZodObject<{
310
310
  description: z.ZodString;
311
311
  auth: z.ZodObject<{
312
312
  type: z.ZodEnum<{
313
- "api-key": "api-key";
314
313
  oauth2: "oauth2";
315
314
  oauth1: "oauth1";
315
+ "api-key": "api-key";
316
316
  }>;
317
317
  provider: z.ZodOptional<z.ZodString>;
318
318
  authorizationUrl: z.ZodOptional<z.ZodString>;
@@ -17,8 +17,13 @@
17
17
  * execute: async ({ query }) => ({ results: [] }),
18
18
  * });
19
19
  *
20
- * // Start MCP server — registered tools are exposed automatically
21
- * const server = createMCPServer();
20
+ * // Start MCP server — registered tools are exposed automatically.
21
+ * // `auth` is required: use bearer for production, or the explicit
22
+ * // `{ type: "none", allowUnauthenticated: true }` opt-in for local dev only.
23
+ * const server = createMCPServer({
24
+ * enabled: true,
25
+ * auth: { type: "none", allowUnauthenticated: true },
26
+ * });
22
27
  * ```
23
28
  */
24
29
  import "../../_dnt.polyfills.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,yBAAyB,CAAC;AAGjC,YAAY,EACV,eAAe,EACf,QAAQ,EACR,OAAO,EACP,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,KAAK,uBAAuB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,yBAAyB,CAAC;AAGjC,YAAY,EACV,eAAe,EACf,QAAQ,EACR,OAAO,EACP,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,KAAK,uBAAuB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
@@ -17,8 +17,13 @@
17
17
  * execute: async ({ query }) => ({ results: [] }),
18
18
  * });
19
19
  *
20
- * // Start MCP server — registered tools are exposed automatically
21
- * const server = createMCPServer();
20
+ * // Start MCP server — registered tools are exposed automatically.
21
+ * // `auth` is required: use bearer for production, or the explicit
22
+ * // `{ type: "none", allowUnauthenticated: true }` opt-in for local dev only.
23
+ * const server = createMCPServer({
24
+ * enabled: true,
25
+ * auth: { type: "none", allowUnauthenticated: true },
26
+ * });
22
27
  * ```
23
28
  */
24
29
  import "../../_dnt.polyfills.js";
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * @module mcp/schemas
5
5
  */
6
- export { type MCPServerConfig, MCPServerConfigSchema, type MCPStats, MCPStatsSchema, } from "./mcp.schema.js";
6
+ export { MCPAuthConfigSchema, type MCPServerConfig, MCPServerConfigSchema, type MCPStats, MCPStatsSchema, } from "./mcp.schema.js";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/schemas/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,QAAQ,EACb,cAAc,GACf,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/schemas/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,QAAQ,EACb,cAAc,GACf,MAAM,iBAAiB,CAAC"}
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * @module mcp/schemas
5
5
  */
6
- export { MCPServerConfigSchema, MCPStatsSchema, } from "./mcp.schema.js";
6
+ export { MCPAuthConfigSchema, MCPServerConfigSchema, MCPStatsSchema, } from "./mcp.schema.js";
@@ -1,15 +1,21 @@
1
1
  import { z } from "zod";
2
+ export declare const MCPAuthConfigSchema: z.ZodUnion<readonly [z.ZodObject<{
3
+ type: z.ZodLiteral<"bearer">;
4
+ validate: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
5
+ }, z.core.$strip>, z.ZodObject<{
6
+ type: z.ZodLiteral<"none">;
7
+ allowUnauthenticated: z.ZodLiteral<true>;
8
+ }, z.core.$strip>]>;
2
9
  export declare const MCPServerConfigSchema: z.ZodObject<{
3
10
  enabled: z.ZodBoolean;
4
11
  port: z.ZodOptional<z.ZodNumber>;
5
- auth: z.ZodOptional<z.ZodObject<{
6
- type: z.ZodEnum<{
7
- bearer: "bearer";
8
- none: "none";
9
- "api-key": "api-key";
10
- }>;
12
+ auth: z.ZodUnion<readonly [z.ZodObject<{
13
+ type: z.ZodLiteral<"bearer">;
11
14
  validate: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
12
- }, z.core.$strip>>;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ type: z.ZodLiteral<"none">;
17
+ allowUnauthenticated: z.ZodLiteral<true>;
18
+ }, z.core.$strip>]>;
13
19
  cors: z.ZodOptional<z.ZodObject<{
14
20
  enabled: z.ZodBoolean;
15
21
  origins: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.schema.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/schemas/mcp.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;iBAgBhC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;iBAKzB,CAAC;AAGH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"mcp.schema.d.ts","sourceRoot":"","sources":["../../../../src/src/mcp/schemas/mcp.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmBxB,eAAO,MAAM,mBAAmB;;;;;;mBAAiD,CAAC;AAElF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;iBAUhC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;iBAKzB,CAAC;AAGH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -1,14 +1,24 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * MCP auth configuration. One of:
4
+ * - `{ type: "bearer", validate?: (token) => Promise<boolean> }` — bearer-token auth.
5
+ * - `{ type: "none", allowUnauthenticated: true }` — explicit opt-in to an
6
+ * unauthenticated server. Required for local dev/testing; prevents accidental
7
+ * exposure of the JSON-RPC surface in production (VULN-SRV-5).
8
+ */
9
+ const AuthValidatedSchema = z.object({
10
+ type: z.literal("bearer"),
11
+ validate: z.function().optional(),
12
+ });
13
+ const AuthNoneSchema = z.object({
14
+ type: z.literal("none"),
15
+ allowUnauthenticated: z.literal(true),
16
+ });
17
+ export const MCPAuthConfigSchema = z.union([AuthValidatedSchema, AuthNoneSchema]);
2
18
  export const MCPServerConfigSchema = z.object({
3
19
  enabled: z.boolean(),
4
20
  port: z.number().int().positive().optional(),
5
- auth: z
6
- .object({
7
- type: z.enum(["bearer", "api-key", "none"]),
8
- validate: z.function()
9
- .optional(),
10
- })
11
- .optional(),
21
+ auth: MCPAuthConfigSchema,
12
22
  cors: z
13
23
  .object({
14
24
  enabled: z.boolean(),
@@ -41,6 +41,19 @@ export declare class MCPServer {
41
41
  params?: unknown;
42
42
  }) => void;
43
43
  constructor(config: MCPServerConfig);
44
+ /**
45
+ * Fail-closed validation of the auth configuration (VULN-SRV-5).
46
+ *
47
+ * Historically, an unset `auth` field — or `{ type: "none" }` — silently
48
+ * accepted every request with only a warning log. That meant an operator who
49
+ * forgot to configure auth shipped an unauthenticated JSON-RPC surface.
50
+ *
51
+ * The new contract: `auth` is required, and the only way to accept
52
+ * unauthenticated traffic is to explicitly set
53
+ * `{ type: "none", allowUnauthenticated: true }`. Any other shape is
54
+ * rejected at construction time.
55
+ */
56
+ private static validateAuthConfig;
44
57
  notifyToolsChanged(): void;
45
58
  notifyResourcesChanged(): void;
46
59
  notifyPromptsChanged(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/src/mcp/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/src/mcp/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7D,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AAIjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAWzE,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,CAAC;AA2CzD,UAAU,cAAc;IACtB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC,CAAC;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,UAAU,CASd;IACX,OAAO,CAAC,QAAQ,CAAkD;IAClE,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,iBAAiB,CAAC,CAA0B;IACpD,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,mBAAmB,CAA8C;IAEzE,2EAA2E;IAC3E,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;gBAElF,MAAM,EAAE,eAAe;IAWnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAyCjC,kBAAkB,IAAI,IAAI;IAI1B,sBAAsB,IAAI,IAAI;IAI9B,oBAAoB,IAAI,IAAI;IAI5B;;;;;;OAMG;IACH,oBAAoB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAK3D,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAY5E,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAmBhG,OAAO,CAAC,QAAQ;IAiDhB,OAAO,CAAC,UAAU;YAgCJ,SAAS;IA6BvB,OAAO,CAAC,QAAQ;IAkGhB,OAAO,CAAC,qBAAqB;IAuB7B,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,YAAY;IA6CpB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,SAAS;IAuCjB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,SAAS;IAIjB,0EAA0E;IAC1E,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC,iBAAiB,IAAI,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC;IAiB5D,OAAO,CAAC,qBAAqB;YAgBf,YAAY;IAoB1B,OAAO,CAAC,cAAc;YAsBR,0BAA0B;CA0BzC;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAElE"}
@@ -73,11 +73,50 @@ export class MCPServer {
73
73
  /** Callback for server-initiated notifications. Set by transport layer. */
74
74
  onNotification;
75
75
  constructor(config) {
76
+ MCPServer.validateAuthConfig(config);
76
77
  this.config = config;
77
- if (!config.auth || config.auth.type === "none") {
78
- logger.warn("MCP server has no authentication configured — all requests will be accepted");
78
+ if (config.auth.type === "none") {
79
+ logger.warn("MCP server started with auth.type='none' (allowUnauthenticated) — all requests will be accepted");
79
80
  }
80
81
  }
82
+ /**
83
+ * Fail-closed validation of the auth configuration (VULN-SRV-5).
84
+ *
85
+ * Historically, an unset `auth` field — or `{ type: "none" }` — silently
86
+ * accepted every request with only a warning log. That meant an operator who
87
+ * forgot to configure auth shipped an unauthenticated JSON-RPC surface.
88
+ *
89
+ * The new contract: `auth` is required, and the only way to accept
90
+ * unauthenticated traffic is to explicitly set
91
+ * `{ type: "none", allowUnauthenticated: true }`. Any other shape is
92
+ * rejected at construction time.
93
+ */
94
+ static validateAuthConfig(config) {
95
+ const auth = config.auth;
96
+ if (auth === undefined || auth === null) {
97
+ throw new Error("MCP auth must be configured. For local dev, pass " +
98
+ "{ auth: { type: 'none', allowUnauthenticated: true } } explicitly.");
99
+ }
100
+ if (typeof auth !== "object") {
101
+ throw new Error("MCP auth must be an object. For local dev, pass " +
102
+ "{ auth: { type: 'none', allowUnauthenticated: true } } explicitly.");
103
+ }
104
+ const type = auth.type;
105
+ if (type === "none") {
106
+ const allow = auth.allowUnauthenticated;
107
+ if (allow !== true) {
108
+ throw new Error("MCP auth type 'none' requires allowUnauthenticated: true to acknowledge " +
109
+ "the server will accept all requests.");
110
+ }
111
+ return;
112
+ }
113
+ if (type === "bearer") {
114
+ return;
115
+ }
116
+ throw new Error(`MCP auth type '${String(type)}' is not supported. Use 'bearer' ` +
117
+ "or { type: 'none', allowUnauthenticated: true } for explicit opt-in to " +
118
+ "unauthenticated traffic.");
119
+ }
81
120
  notifyToolsChanged() {
82
121
  this.onNotification?.({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
83
122
  }
@@ -470,7 +509,7 @@ export class MCPServer {
470
509
  }
471
510
  createHTTPHandler() {
472
511
  return createMCPHTTPHandler({
473
- authEnabled: Boolean(this.config.auth?.type && this.config.auth.type !== "none"),
512
+ authEnabled: this.config.auth.type !== "none",
474
513
  getCORSHeaders: (requestOrigin) => this.getCORSHeaders(requestOrigin),
475
514
  validateAuth: (request) => this.validateAuth(request),
476
515
  handleRequest: (request, context) => this.handleRequest(request, context),
@@ -497,13 +536,11 @@ export class MCPServer {
497
536
  }
498
537
  async validateAuth(request) {
499
538
  const auth = this.config.auth;
500
- if (!auth || auth.type === "none")
539
+ if (auth.type === "none")
501
540
  return true;
502
541
  const authHeader = request.headers.get("Authorization");
503
542
  if (!authHeader)
504
543
  return false;
505
- if (auth.type !== "bearer")
506
- return false;
507
544
  const token = authHeader.replace("Bearer ", "");
508
545
  // When bearer auth is configured without a validate function, reject all requests
509
546
  if (!auth.validate) {
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.246";
1
+ export declare const VERSION = "0.1.247";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.246";
3
+ export const VERSION = "0.1.247";
@@ -77,7 +77,11 @@ export declare class WorkspaceSync {
77
77
  */
78
78
  detectChanges(): Promise<FileChange[]>;
79
79
  /**
80
- * Recursively walk directory and detect changes
80
+ * Recursively walk directory and detect changes.
81
+ *
82
+ * SECURITY: Uses lstat (not stat) and skips any symlink it finds, so a
83
+ * symlink planted inside the workspace cannot cause us to descend into —
84
+ * or read the contents of — files outside the workspace (VULN-FS-4).
81
85
  */
82
86
  private walkAndDetect;
83
87
  /**
@@ -91,7 +95,18 @@ export declare class WorkspaceSync {
91
95
  onUpload?: (path: string, content: string, type: FileChange["type"]) => Promise<void>;
92
96
  }): Promise<UploadResult>;
93
97
  /**
94
- * Safely resolve a path within the workspace, preventing path traversal attacks
98
+ * Safely resolve a path within the workspace, preventing path traversal
99
+ * and symlink-based escapes (VULN-FS-4).
100
+ *
101
+ * - Rejects NUL bytes outright.
102
+ * - Rejects any intermediate path segment that is a symlink.
103
+ * - Re-checks containment by realpath-ing the parent directory after the
104
+ * segment walk, so a symlink that resolves through a non-symlink directory
105
+ * chain still cannot escape the workspace.
106
+ *
107
+ * Note: this deliberately rejects ALL symlinks inside the workspace — even
108
+ * those whose targets remain within it — because the race window between
109
+ * resolution and use is not worth the complexity for our use-case.
95
110
  */
96
111
  private resolveSafePath;
97
112
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-sync.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/claude-code/workspace-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-sync.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/claude-code/workspace-sync.ts"],"names":[],"mappings":"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"}
@@ -12,8 +12,9 @@
12
12
  import * as dntShim from "../../../_dnt.shims.js";
13
13
  import { logger as baseLogger } from "../../utils/index.js";
14
14
  import { api } from "../api.js";
15
- import { join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
15
+ import { dirname, join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
16
16
  import { INITIALIZATION_ERROR, INVALID_ARGUMENT, SECURITY_VIOLATION } from "../../errors/index.js";
17
+ import { isWithinDirectory } from "../../utils/path-utils.js";
17
18
  const logger = baseLogger.component("workspace-sync");
18
19
  /** Maximum file size for workspace sync (10 MB) */
19
20
  const MAX_WORKSPACE_FILE_SIZE = 10 * 1024 * 1024;
@@ -136,8 +137,8 @@ export class WorkspaceSync {
136
137
  const hash = await checksum(content);
137
138
  this.fileChecksums.set(path, hash);
138
139
  // Write to local filesystem (use safe path resolution)
139
- const localPath = this.resolveSafePath(path);
140
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
140
+ const localPath = await this.resolveSafePath(path);
141
+ const dir = dirname(localPath);
141
142
  await dntShim.Deno.mkdir(dir, { recursive: true });
142
143
  await dntShim.Deno.writeTextFile(localPath, content);
143
144
  filesDownloaded++;
@@ -188,7 +189,7 @@ export class WorkspaceSync {
188
189
  // Check for deleted files
189
190
  for (const [path, originalHash] of this.fileChecksums) {
190
191
  try {
191
- const localPath = this.resolveSafePath(path);
192
+ const localPath = await this.resolveSafePath(path);
192
193
  await dntShim.Deno.stat(localPath);
193
194
  }
194
195
  catch (_) {
@@ -206,17 +207,28 @@ export class WorkspaceSync {
206
207
  return changes;
207
208
  }
208
209
  /**
209
- * Recursively walk directory and detect changes
210
+ * Recursively walk directory and detect changes.
211
+ *
212
+ * SECURITY: Uses lstat (not stat) and skips any symlink it finds, so a
213
+ * symlink planted inside the workspace cannot cause us to descend into —
214
+ * or read the contents of — files outside the workspace (VULN-FS-4).
210
215
  */
211
216
  async walkAndDetect(localPath, relativePath, changes) {
212
- const stat = await dntShim.Deno.stat(localPath);
217
+ const stat = await dntShim.Deno.lstat(localPath);
218
+ // Ignore symlinks outright — we never treat them as real files here.
219
+ if (stat.isSymlink) {
220
+ if (this.config.debug) {
221
+ logger.info("Skipping symlink during change detection", { localPath });
222
+ }
223
+ return;
224
+ }
213
225
  if (stat.isDirectory) {
214
226
  for await (const entry of dntShim.Deno.readDir(localPath)) {
215
227
  await this.walkAndDetect(`${localPath}/${entry.name}`, `${relativePath}/${entry.name}`, changes);
216
228
  }
217
229
  return;
218
230
  }
219
- // It's a file - check for changes
231
+ // It's a regular file - check for changes
220
232
  const content = await dntShim.Deno.readTextFile(localPath);
221
233
  const newHash = await checksum(content);
222
234
  const originalHash = this.fileChecksums.get(relativePath);
@@ -258,7 +270,7 @@ export class WorkspaceSync {
258
270
  continue;
259
271
  }
260
272
  try {
261
- const localPath = this.resolveSafePath(change.path);
273
+ const localPath = await this.resolveSafePath(change.path);
262
274
  const content = await dntShim.Deno.readTextFile(localPath);
263
275
  if (options.onUpload) {
264
276
  await options.onUpload(change.path, content, change.type);
@@ -287,34 +299,109 @@ export class WorkspaceSync {
287
299
  };
288
300
  }
289
301
  /**
290
- * Safely resolve a path within the workspace, preventing path traversal attacks
302
+ * Safely resolve a path within the workspace, preventing path traversal
303
+ * and symlink-based escapes (VULN-FS-4).
304
+ *
305
+ * - Rejects NUL bytes outright.
306
+ * - Rejects any intermediate path segment that is a symlink.
307
+ * - Re-checks containment by realpath-ing the parent directory after the
308
+ * segment walk, so a symlink that resolves through a non-symlink directory
309
+ * chain still cannot escape the workspace.
310
+ *
311
+ * Note: this deliberately rejects ALL symlinks inside the workspace — even
312
+ * those whose targets remain within it — because the race window between
313
+ * resolution and use is not worth the complexity for our use-case.
291
314
  */
292
- resolveSafePath(path) {
293
- // Normalize the input path
315
+ async resolveSafePath(path) {
316
+ // Reject NUL bytes — they confuse filesystem APIs and are never legitimate.
317
+ if (path.includes("\0")) {
318
+ throw SECURITY_VIOLATION.create({ detail: `NUL byte in path` });
319
+ }
320
+ // Workspace-relative paths only. A single leading "/" is the canonical
321
+ // API form for "the project root" (e.g. "/src/foo.ts") and is accepted;
322
+ // anything that syntactically looks like a system-absolute path beyond
323
+ // that one-slash convention is rejected.
324
+ //
325
+ // - Windows drive letters (C:\...) — rejected.
326
+ // - UNC paths (//host/share) — rejected.
327
+ // - Unix absolute paths with a leading slash are treated as
328
+ // workspace-relative, but any component that tries to escape the
329
+ // workspace is still caught by the traversal / realpath checks below.
330
+ if (/^[A-Za-z]:[\\/]/.test(path)) {
331
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
332
+ }
333
+ if (path.startsWith("//")) {
334
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
335
+ }
336
+ // Normalize the input path (treat leading "/" as workspace-relative).
294
337
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
295
- // Resolve the full path
338
+ // Empty path resolves to the workspace dir itself — writing to it would
339
+ // clobber the workspace as a regular file. Reject explicitly.
340
+ if (normalizedPath === "") {
341
+ throw SECURITY_VIOLATION.create({ detail: `Empty path not allowed` });
342
+ }
343
+ // Resolve the full path lexically first (catches literal "..").
296
344
  const fullPath = resolve(join(this.workspaceDir, normalizedPath));
297
- // Verify the resolved path is within the workspace
298
345
  const relativePath = relative(this.workspaceDir, fullPath);
299
- if (relativePath.startsWith("..") || !relativePath || relativePath === "..") {
346
+ if (!relativePath || relativePath.startsWith("..") || relativePath === "..") {
300
347
  throw SECURITY_VIOLATION.create({ detail: `Path traversal detected: ${path}` });
301
348
  }
349
+ // Walk each segment and reject any existing symlink along the way.
350
+ // A segment that does not yet exist is fine — it will be created later.
351
+ const relSegments = relativePath === "" ? [] : relativePath.split(/[\\/]/).filter(Boolean);
352
+ let cursor = this.workspaceDir;
353
+ for (const seg of relSegments) {
354
+ cursor = join(cursor, seg);
355
+ try {
356
+ const info = await dntShim.Deno.lstat(cursor);
357
+ if (info.isSymlink) {
358
+ throw SECURITY_VIOLATION.create({
359
+ detail: `Refusing to traverse symlink: ${cursor}`,
360
+ });
361
+ }
362
+ }
363
+ catch (e) {
364
+ if (e instanceof dntShim.Deno.errors.NotFound) {
365
+ // Segment doesn't exist yet — the rest of the chain will be
366
+ // created under a verified-non-symlink parent, so stop walking.
367
+ break;
368
+ }
369
+ throw e;
370
+ }
371
+ }
372
+ // Final containment check against the realpath of the parent directory,
373
+ // to defeat any symlink-in-parent we might have missed (e.g. one that
374
+ // appeared mid-walk). If the parent doesn't exist yet, that's fine —
375
+ // the segment walk above already proved every existing ancestor is real.
376
+ try {
377
+ const parentReal = await dntShim.Deno.realPath(dirname(fullPath));
378
+ const workspaceReal = await dntShim.Deno.realPath(this.workspaceDir);
379
+ if (!isWithinDirectory(workspaceReal, parentReal)) {
380
+ throw SECURITY_VIOLATION.create({
381
+ detail: `Resolved parent outside workspace: ${parentReal}`,
382
+ });
383
+ }
384
+ }
385
+ catch (e) {
386
+ if (!(e instanceof dntShim.Deno.errors.NotFound))
387
+ throw e;
388
+ }
302
389
  return fullPath;
303
390
  }
304
391
  /**
305
392
  * Read a file from the workspace
306
393
  */
307
394
  async readFile(path) {
308
- const localPath = this.resolveSafePath(path);
395
+ const localPath = await this.resolveSafePath(path);
309
396
  return await dntShim.Deno.readTextFile(localPath);
310
397
  }
311
398
  /**
312
399
  * Write a file to the workspace
313
400
  */
314
401
  async writeFile(path, content) {
315
- const localPath = this.resolveSafePath(path);
402
+ const localPath = await this.resolveSafePath(path);
316
403
  // Ensure directory exists
317
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
404
+ const dir = dirname(localPath);
318
405
  await dntShim.Deno.mkdir(dir, { recursive: true });
319
406
  await dntShim.Deno.writeTextFile(localPath, content);
320
407
  }
@@ -322,7 +409,7 @@ export class WorkspaceSync {
322
409
  * Delete a file from the workspace
323
410
  */
324
411
  async deleteFile(path) {
325
- const localPath = this.resolveSafePath(path);
412
+ const localPath = await this.resolveSafePath(path);
326
413
  await dntShim.Deno.remove(localPath);
327
414
  }
328
415
  /**
@@ -330,7 +417,7 @@ export class WorkspaceSync {
330
417
  */
331
418
  async fileExists(path) {
332
419
  try {
333
- const localPath = this.resolveSafePath(path);
420
+ const localPath = await this.resolveSafePath(path);
334
421
  await dntShim.Deno.stat(localPath);
335
422
  return true;
336
423
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.246",
3
+ "version": "0.1.247",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.246",
3
+ "version": "0.1.247",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -142,6 +142,10 @@ export type ConversationRunAppendFailureOutcome =
142
142
  | "resumed"
143
143
  | "stopped"
144
144
  | "retry_scheduled";
145
+ export type ConversationRunAppendExecutionOutcome =
146
+ | "resumed"
147
+ | "stopped"
148
+ | "retry_scheduled";
145
149
 
146
150
  export const CreateConversationRunAcceptedSchema = z
147
151
  .object({
@@ -531,6 +535,85 @@ export async function recoverConversationRunAppendFailure(input: {
531
535
  };
532
536
  }
533
537
 
538
+ export async function recoverConversationRunAppendExecution(input: {
539
+ error: unknown;
540
+ authToken: string;
541
+ apiUrl: string;
542
+ conversationId: string;
543
+ runId: string;
544
+ latestEventId: number;
545
+ latestExternalEventSequence: number;
546
+ remainingEvents: unknown[];
547
+ pendingEvents: unknown[];
548
+ cursorResyncsThisFlush: number;
549
+ consecutiveFailures: number;
550
+ maxCursorResyncsPerFlush: number;
551
+ abortSignal?: AbortSignal;
552
+ }): Promise<
553
+ | {
554
+ outcome: "resumed";
555
+ latestEventId: number;
556
+ latestExternalEventSequence: number;
557
+ pendingEvents: unknown[];
558
+ consecutiveFailures: number;
559
+ }
560
+ | {
561
+ outcome: "stopped";
562
+ latestEventId: number;
563
+ latestExternalEventSequence: number;
564
+ disableReason?: "cursor_resyncs_exhausted" | "non_appendable" | "ignorable_append_rejection";
565
+ }
566
+ | {
567
+ outcome: "retry_scheduled";
568
+ latestEventId: number;
569
+ latestExternalEventSequence: number;
570
+ pendingEvents: unknown[];
571
+ consecutiveFailures: number;
572
+ errorMessage: string;
573
+ }
574
+ > {
575
+ const recovered = await recoverConversationRunAppendFailure({
576
+ error: input.error,
577
+ authToken: input.authToken,
578
+ apiUrl: input.apiUrl,
579
+ conversationId: input.conversationId,
580
+ runId: input.runId,
581
+ latestEventId: input.latestEventId,
582
+ latestExternalEventSequence: input.latestExternalEventSequence,
583
+ cursorResyncsThisFlush: input.cursorResyncsThisFlush,
584
+ maxCursorResyncsPerFlush: input.maxCursorResyncsPerFlush,
585
+ abortSignal: input.abortSignal,
586
+ });
587
+
588
+ if (recovered.outcome === "resumed") {
589
+ return {
590
+ outcome: "resumed",
591
+ latestEventId: recovered.latestEventId,
592
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
593
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
594
+ consecutiveFailures: 0,
595
+ };
596
+ }
597
+
598
+ if (recovered.outcome === "stopped") {
599
+ return {
600
+ outcome: "stopped",
601
+ latestEventId: recovered.latestEventId,
602
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
603
+ ...(recovered.disableReason ? { disableReason: recovered.disableReason } : {}),
604
+ };
605
+ }
606
+
607
+ return {
608
+ outcome: "retry_scheduled",
609
+ latestEventId: recovered.latestEventId,
610
+ latestExternalEventSequence: recovered.latestExternalEventSequence,
611
+ pendingEvents: [...input.remainingEvents, ...input.pendingEvents],
612
+ consecutiveFailures: input.consecutiveFailures + 1,
613
+ errorMessage: recovered.errorMessage ?? "Conversation run append failed",
614
+ };
615
+ }
616
+
534
617
  async function waitForConversationRunPoll(
535
618
  ms: number,
536
619
  abortSignal?: AbortSignal,
@@ -220,6 +220,7 @@ export {
220
220
  CompleteConversationRunResponseSchema,
221
221
  type ConversationAgentRunUsage,
222
222
  type ConversationRunAppendCursorResyncResult,
223
+ type ConversationRunAppendExecutionOutcome,
223
224
  type ConversationRunAppendFailureOutcome,
224
225
  type ConversationRunAppendRecoveryOutcome,
225
226
  type ConversationRunProjection,
@@ -237,6 +238,7 @@ export {
237
238
  isIgnorableConversationRunAppendError,
238
239
  monitorConversationRunStatus,
239
240
  parseAppendConversationRunEventsErrorBody,
241
+ recoverConversationRunAppendExecution,
240
242
  recoverConversationRunAppendFailure,
241
243
  recoverConversationRunCursorMismatch,
242
244
  resolveConversationRunTargets,
@@ -17,8 +17,13 @@
17
17
  * execute: async ({ query }) => ({ results: [] }),
18
18
  * });
19
19
  *
20
- * // Start MCP server — registered tools are exposed automatically
21
- * const server = createMCPServer();
20
+ * // Start MCP server — registered tools are exposed automatically.
21
+ * // `auth` is required: use bearer for production, or the explicit
22
+ * // `{ type: "none", allowUnauthenticated: true }` opt-in for local dev only.
23
+ * const server = createMCPServer({
24
+ * enabled: true,
25
+ * auth: { type: "none", allowUnauthenticated: true },
26
+ * });
22
27
  * ```
23
28
  */
24
29
  import "../../_dnt.polyfills.js";
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  export {
8
+ MCPAuthConfigSchema,
8
9
  type MCPServerConfig,
9
10
  MCPServerConfigSchema,
10
11
  type MCPStats,
@@ -1,15 +1,28 @@
1
1
  import { z } from "zod";
2
2
 
3
+ /**
4
+ * MCP auth configuration. One of:
5
+ * - `{ type: "bearer", validate?: (token) => Promise<boolean> }` — bearer-token auth.
6
+ * - `{ type: "none", allowUnauthenticated: true }` — explicit opt-in to an
7
+ * unauthenticated server. Required for local dev/testing; prevents accidental
8
+ * exposure of the JSON-RPC surface in production (VULN-SRV-5).
9
+ */
10
+ const AuthValidatedSchema = z.object({
11
+ type: z.literal("bearer"),
12
+ validate: z.function().optional(),
13
+ });
14
+
15
+ const AuthNoneSchema = z.object({
16
+ type: z.literal("none"),
17
+ allowUnauthenticated: z.literal(true),
18
+ });
19
+
20
+ export const MCPAuthConfigSchema = z.union([AuthValidatedSchema, AuthNoneSchema]);
21
+
3
22
  export const MCPServerConfigSchema = z.object({
4
23
  enabled: z.boolean(),
5
24
  port: z.number().int().positive().optional(),
6
- auth: z
7
- .object({
8
- type: z.enum(["bearer", "api-key", "none"]),
9
- validate: z.function()
10
- .optional(),
11
- })
12
- .optional(),
25
+ auth: MCPAuthConfigSchema,
13
26
  cors: z
14
27
  .object({
15
28
  enabled: z.boolean(),
@@ -113,13 +113,69 @@ export class MCPServer {
113
113
  onNotification?: (notification: { jsonrpc: "2.0"; method: string; params?: unknown }) => void;
114
114
 
115
115
  constructor(config: MCPServerConfig) {
116
+ MCPServer.validateAuthConfig(config);
116
117
  this.config = config;
117
118
 
118
- if (!config.auth || config.auth.type === "none") {
119
- logger.warn("MCP server has no authentication configured — all requests will be accepted");
119
+ if (config.auth.type === "none") {
120
+ logger.warn(
121
+ "MCP server started with auth.type='none' (allowUnauthenticated) — all requests will be accepted",
122
+ );
120
123
  }
121
124
  }
122
125
 
126
+ /**
127
+ * Fail-closed validation of the auth configuration (VULN-SRV-5).
128
+ *
129
+ * Historically, an unset `auth` field — or `{ type: "none" }` — silently
130
+ * accepted every request with only a warning log. That meant an operator who
131
+ * forgot to configure auth shipped an unauthenticated JSON-RPC surface.
132
+ *
133
+ * The new contract: `auth` is required, and the only way to accept
134
+ * unauthenticated traffic is to explicitly set
135
+ * `{ type: "none", allowUnauthenticated: true }`. Any other shape is
136
+ * rejected at construction time.
137
+ */
138
+ private static validateAuthConfig(config: MCPServerConfig): void {
139
+ const auth = (config as { auth?: unknown }).auth;
140
+
141
+ if (auth === undefined || auth === null) {
142
+ throw new Error(
143
+ "MCP auth must be configured. For local dev, pass " +
144
+ "{ auth: { type: 'none', allowUnauthenticated: true } } explicitly.",
145
+ );
146
+ }
147
+
148
+ if (typeof auth !== "object") {
149
+ throw new Error(
150
+ "MCP auth must be an object. For local dev, pass " +
151
+ "{ auth: { type: 'none', allowUnauthenticated: true } } explicitly.",
152
+ );
153
+ }
154
+
155
+ const type = (auth as { type?: unknown }).type;
156
+
157
+ if (type === "none") {
158
+ const allow = (auth as { allowUnauthenticated?: unknown }).allowUnauthenticated;
159
+ if (allow !== true) {
160
+ throw new Error(
161
+ "MCP auth type 'none' requires allowUnauthenticated: true to acknowledge " +
162
+ "the server will accept all requests.",
163
+ );
164
+ }
165
+ return;
166
+ }
167
+
168
+ if (type === "bearer") {
169
+ return;
170
+ }
171
+
172
+ throw new Error(
173
+ `MCP auth type '${String(type)}' is not supported. Use 'bearer' ` +
174
+ "or { type: 'none', allowUnauthenticated: true } for explicit opt-in to " +
175
+ "unauthenticated traffic.",
176
+ );
177
+ }
178
+
123
179
  notifyToolsChanged(): void {
124
180
  this.onNotification?.({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
125
181
  }
@@ -607,7 +663,7 @@ export class MCPServer {
607
663
 
608
664
  createHTTPHandler(): (request: Request) => Promise<Response> {
609
665
  return createMCPHTTPHandler({
610
- authEnabled: Boolean(this.config.auth?.type && this.config.auth.type !== "none"),
666
+ authEnabled: this.config.auth.type !== "none",
611
667
  getCORSHeaders: (requestOrigin) => this.getCORSHeaders(requestOrigin),
612
668
  validateAuth: (request) => this.validateAuth(request),
613
669
  handleRequest: (request, context) => this.handleRequest(request, context),
@@ -640,13 +696,11 @@ export class MCPServer {
640
696
 
641
697
  private async validateAuth(request: Request): Promise<boolean> {
642
698
  const auth = this.config.auth;
643
- if (!auth || auth.type === "none") return true;
699
+ if (auth.type === "none") return true;
644
700
 
645
701
  const authHeader = request.headers.get("Authorization");
646
702
  if (!authHeader) return false;
647
703
 
648
- if (auth.type !== "bearer") return false;
649
-
650
704
  const token = authHeader.replace("Bearer ", "");
651
705
 
652
706
  // When bearer auth is configured without a validate function, reject all requests
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.246";
3
+ export const VERSION = "0.1.247";
@@ -15,8 +15,9 @@ import * as dntShim from "../../../_dnt.shims.js";
15
15
  import { logger as baseLogger } from "../../utils/index.js";
16
16
  import { api } from "../api.js";
17
17
  import type { CapturedTenantContext } from "../types.js";
18
- import { join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
18
+ import { dirname, join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
19
19
  import { INITIALIZATION_ERROR, INVALID_ARGUMENT, SECURITY_VIOLATION } from "../../errors/index.js";
20
+ import { isWithinDirectory } from "../../utils/path-utils.js";
20
21
 
21
22
  const logger = baseLogger.component("workspace-sync");
22
23
 
@@ -230,8 +231,8 @@ export class WorkspaceSync {
230
231
  this.fileChecksums.set(path, hash);
231
232
 
232
233
  // Write to local filesystem (use safe path resolution)
233
- const localPath = this.resolveSafePath(path);
234
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
234
+ const localPath = await this.resolveSafePath(path);
235
+ const dir = dirname(localPath);
235
236
  await dntShim.Deno.mkdir(dir, { recursive: true });
236
237
  await dntShim.Deno.writeTextFile(localPath, content);
237
238
 
@@ -295,7 +296,7 @@ export class WorkspaceSync {
295
296
  // Check for deleted files
296
297
  for (const [path, originalHash] of this.fileChecksums) {
297
298
  try {
298
- const localPath = this.resolveSafePath(path);
299
+ const localPath = await this.resolveSafePath(path);
299
300
  await dntShim.Deno.stat(localPath);
300
301
  } catch (_) {
301
302
  // File was deleted
@@ -315,14 +316,26 @@ export class WorkspaceSync {
315
316
  }
316
317
 
317
318
  /**
318
- * Recursively walk directory and detect changes
319
+ * Recursively walk directory and detect changes.
320
+ *
321
+ * SECURITY: Uses lstat (not stat) and skips any symlink it finds, so a
322
+ * symlink planted inside the workspace cannot cause us to descend into —
323
+ * or read the contents of — files outside the workspace (VULN-FS-4).
319
324
  */
320
325
  private async walkAndDetect(
321
326
  localPath: string,
322
327
  relativePath: string,
323
328
  changes: FileChange[],
324
329
  ): Promise<void> {
325
- const stat = await dntShim.Deno.stat(localPath);
330
+ const stat = await dntShim.Deno.lstat(localPath);
331
+
332
+ // Ignore symlinks outright — we never treat them as real files here.
333
+ if (stat.isSymlink) {
334
+ if (this.config.debug) {
335
+ logger.info("Skipping symlink during change detection", { localPath });
336
+ }
337
+ return;
338
+ }
326
339
 
327
340
  if (stat.isDirectory) {
328
341
  for await (const entry of dntShim.Deno.readDir(localPath)) {
@@ -335,7 +348,7 @@ export class WorkspaceSync {
335
348
  return;
336
349
  }
337
350
 
338
- // It's a file - check for changes
351
+ // It's a regular file - check for changes
339
352
  const content = await dntShim.Deno.readTextFile(localPath);
340
353
  const newHash = await checksum(content);
341
354
  const originalHash = this.fileChecksums.get(relativePath);
@@ -390,7 +403,7 @@ export class WorkspaceSync {
390
403
  }
391
404
 
392
405
  try {
393
- const localPath = this.resolveSafePath(change.path);
406
+ const localPath = await this.resolveSafePath(change.path);
394
407
  const content = await dntShim.Deno.readTextFile(localPath);
395
408
 
396
409
  if (options.onUpload) {
@@ -420,21 +433,97 @@ export class WorkspaceSync {
420
433
  }
421
434
 
422
435
  /**
423
- * Safely resolve a path within the workspace, preventing path traversal attacks
436
+ * Safely resolve a path within the workspace, preventing path traversal
437
+ * and symlink-based escapes (VULN-FS-4).
438
+ *
439
+ * - Rejects NUL bytes outright.
440
+ * - Rejects any intermediate path segment that is a symlink.
441
+ * - Re-checks containment by realpath-ing the parent directory after the
442
+ * segment walk, so a symlink that resolves through a non-symlink directory
443
+ * chain still cannot escape the workspace.
444
+ *
445
+ * Note: this deliberately rejects ALL symlinks inside the workspace — even
446
+ * those whose targets remain within it — because the race window between
447
+ * resolution and use is not worth the complexity for our use-case.
424
448
  */
425
- private resolveSafePath(path: string): string {
426
- // Normalize the input path
449
+ private async resolveSafePath(path: string): Promise<string> {
450
+ // Reject NUL bytes — they confuse filesystem APIs and are never legitimate.
451
+ if (path.includes("\0")) {
452
+ throw SECURITY_VIOLATION.create({ detail: `NUL byte in path` });
453
+ }
454
+
455
+ // Workspace-relative paths only. A single leading "/" is the canonical
456
+ // API form for "the project root" (e.g. "/src/foo.ts") and is accepted;
457
+ // anything that syntactically looks like a system-absolute path beyond
458
+ // that one-slash convention is rejected.
459
+ //
460
+ // - Windows drive letters (C:\...) — rejected.
461
+ // - UNC paths (//host/share) — rejected.
462
+ // - Unix absolute paths with a leading slash are treated as
463
+ // workspace-relative, but any component that tries to escape the
464
+ // workspace is still caught by the traversal / realpath checks below.
465
+ if (/^[A-Za-z]:[\\/]/.test(path)) {
466
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
467
+ }
468
+ if (path.startsWith("//")) {
469
+ throw SECURITY_VIOLATION.create({ detail: `Absolute path not allowed: ${path}` });
470
+ }
471
+
472
+ // Normalize the input path (treat leading "/" as workspace-relative).
427
473
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
428
474
 
429
- // Resolve the full path
430
- const fullPath = resolve(join(this.workspaceDir, normalizedPath));
475
+ // Empty path resolves to the workspace dir itself — writing to it would
476
+ // clobber the workspace as a regular file. Reject explicitly.
477
+ if (normalizedPath === "") {
478
+ throw SECURITY_VIOLATION.create({ detail: `Empty path not allowed` });
479
+ }
431
480
 
432
- // Verify the resolved path is within the workspace
481
+ // Resolve the full path lexically first (catches literal "..").
482
+ const fullPath = resolve(join(this.workspaceDir, normalizedPath));
433
483
  const relativePath = relative(this.workspaceDir, fullPath);
434
- if (relativePath.startsWith("..") || !relativePath || relativePath === "..") {
484
+ if (!relativePath || relativePath.startsWith("..") || relativePath === "..") {
435
485
  throw SECURITY_VIOLATION.create({ detail: `Path traversal detected: ${path}` });
436
486
  }
437
487
 
488
+ // Walk each segment and reject any existing symlink along the way.
489
+ // A segment that does not yet exist is fine — it will be created later.
490
+ const relSegments = relativePath === "" ? [] : relativePath.split(/[\\/]/).filter(Boolean);
491
+ let cursor = this.workspaceDir;
492
+ for (const seg of relSegments) {
493
+ cursor = join(cursor, seg);
494
+ try {
495
+ const info = await dntShim.Deno.lstat(cursor);
496
+ if (info.isSymlink) {
497
+ throw SECURITY_VIOLATION.create({
498
+ detail: `Refusing to traverse symlink: ${cursor}`,
499
+ });
500
+ }
501
+ } catch (e) {
502
+ if (e instanceof dntShim.Deno.errors.NotFound) {
503
+ // Segment doesn't exist yet — the rest of the chain will be
504
+ // created under a verified-non-symlink parent, so stop walking.
505
+ break;
506
+ }
507
+ throw e;
508
+ }
509
+ }
510
+
511
+ // Final containment check against the realpath of the parent directory,
512
+ // to defeat any symlink-in-parent we might have missed (e.g. one that
513
+ // appeared mid-walk). If the parent doesn't exist yet, that's fine —
514
+ // the segment walk above already proved every existing ancestor is real.
515
+ try {
516
+ const parentReal = await dntShim.Deno.realPath(dirname(fullPath));
517
+ const workspaceReal = await dntShim.Deno.realPath(this.workspaceDir);
518
+ if (!isWithinDirectory(workspaceReal, parentReal)) {
519
+ throw SECURITY_VIOLATION.create({
520
+ detail: `Resolved parent outside workspace: ${parentReal}`,
521
+ });
522
+ }
523
+ } catch (e) {
524
+ if (!(e instanceof dntShim.Deno.errors.NotFound)) throw e;
525
+ }
526
+
438
527
  return fullPath;
439
528
  }
440
529
 
@@ -442,7 +531,7 @@ export class WorkspaceSync {
442
531
  * Read a file from the workspace
443
532
  */
444
533
  async readFile(path: string): Promise<string> {
445
- const localPath = this.resolveSafePath(path);
534
+ const localPath = await this.resolveSafePath(path);
446
535
  return await dntShim.Deno.readTextFile(localPath);
447
536
  }
448
537
 
@@ -450,10 +539,10 @@ export class WorkspaceSync {
450
539
  * Write a file to the workspace
451
540
  */
452
541
  async writeFile(path: string, content: string): Promise<void> {
453
- const localPath = this.resolveSafePath(path);
542
+ const localPath = await this.resolveSafePath(path);
454
543
 
455
544
  // Ensure directory exists
456
- const dir = localPath.substring(0, localPath.lastIndexOf("/"));
545
+ const dir = dirname(localPath);
457
546
  await dntShim.Deno.mkdir(dir, { recursive: true });
458
547
 
459
548
  await dntShim.Deno.writeTextFile(localPath, content);
@@ -463,7 +552,7 @@ export class WorkspaceSync {
463
552
  * Delete a file from the workspace
464
553
  */
465
554
  async deleteFile(path: string): Promise<void> {
466
- const localPath = this.resolveSafePath(path);
555
+ const localPath = await this.resolveSafePath(path);
467
556
  await dntShim.Deno.remove(localPath);
468
557
  }
469
558
 
@@ -472,7 +561,7 @@ export class WorkspaceSync {
472
561
  */
473
562
  async fileExists(path: string): Promise<boolean> {
474
563
  try {
475
- const localPath = this.resolveSafePath(path);
564
+ const localPath = await this.resolveSafePath(path);
476
565
  await dntShim.Deno.stat(localPath);
477
566
  return true;
478
567
  } catch (_) {