@omercnet/paseo-omp 0.4.0 → 1.0.0-next.160.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.
Files changed (40) hide show
  1. package/README.md +8 -8
  2. package/client/external-url.ts +2 -11
  3. package/client/omp-config-styles.ts +284 -0
  4. package/client/omp-config-surface.tsx +16 -286
  5. package/client/omp-routing-editor.tsx +1 -3
  6. package/client/provider-launch-settings.tsx +118 -0
  7. package/index.client.tsx +21 -2
  8. package/index.server.ts +10 -0
  9. package/package.json +14 -9
  10. package/paseo-plugin.json +2 -1
  11. package/scripts/prepare-dependencies.mjs +44 -11
  12. package/server/omp-models.ts +2 -6
  13. package/server/package-version.ts +1 -1
  14. package/server/protocol-violation-diagnostics.ts +1 -1
  15. package/server/provider/catalog.ts +2 -1
  16. package/server/provider/config-normalization.ts +19 -3
  17. package/server/provider/connection.ts +30 -0
  18. package/server/provider/host-tools.ts +2 -2
  19. package/server/provider/mcp-transport.ts +9 -2
  20. package/server/provider/omp-rpc-environment.ts +368 -0
  21. package/server/provider/omp-rpc-process.ts +119 -0
  22. package/server/provider/omp-rpc-protocol.ts +1949 -0
  23. package/server/provider/omp-rpc-transport.ts +1373 -0
  24. package/server/provider/omp-rpc-values.ts +31 -0
  25. package/server/provider/omp-rpc.ts +267 -3311
  26. package/server/provider/profile-providers.ts +2 -1
  27. package/server/provider/prompt-payload.ts +253 -0
  28. package/server/provider/registration.ts +10 -2
  29. package/server/provider/session-compaction.ts +187 -0
  30. package/server/provider/session-descriptors.ts +52 -18
  31. package/server/provider/session-events.ts +42 -0
  32. package/server/provider/session-permissions.ts +987 -0
  33. package/server/provider/session-terminal.ts +307 -0
  34. package/server/provider/session-usage.ts +234 -0
  35. package/server/provider/session.ts +145 -1853
  36. package/server/provider/settings.ts +8 -5
  37. package/server/provider/subsessions.ts +30 -9
  38. package/server/provider/timeline-projector.ts +140 -29
  39. package/shared/external-url.ts +0 -9
  40. package/shared/provider-launch-settings.ts +18 -0
@@ -1,11 +1,14 @@
1
1
  import { z } from "zod";
2
+ import {
3
+ environmentNameSchema,
4
+ MAX_INHERITED_ENVIRONMENT_NAMES,
5
+ } from "../../shared/provider-launch-settings";
2
6
 
3
7
  const MAX_COMMAND_PARTS = 64;
4
- const MAX_ENV_ENTRIES = 256;
5
- const MAX_TEXT_BYTES = 64 * 1024;
6
- const MAX_MODEL_SELECTOR_BYTES = 513;
7
8
  const MAX_PATH_BYTES = 4_096;
8
9
  const MAX_RPC_TIMEOUT_MS = 10 * 60 * 1_000;
10
+ const MAX_TEXT_BYTES = 64 * 1024;
11
+ const MAX_MODEL_SELECTOR_BYTES = 513;
9
12
 
10
13
  function boundedString(maxBytes: number) {
11
14
  return z
@@ -18,7 +21,7 @@ const CommandPartSchema = boundedString(MAX_PATH_BYTES).refine(
18
21
  (value) => !value.includes("\0") && !/[\r\n]/u.test(value),
19
22
  "command arguments cannot contain NUL or line breaks",
20
23
  );
21
- const EnvironmentNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u);
24
+ const EnvironmentNameSchema = environmentNameSchema;
22
25
  const EnvironmentValueSchema = z
23
26
  .string()
24
27
  .refine((value) => Buffer.byteLength(value, "utf8") <= MAX_TEXT_BYTES && !value.includes("\0"));
@@ -63,7 +66,7 @@ export const OmpProviderOptionsSchema = z
63
66
  .optional(),
64
67
  inheritEnv: z
65
68
  .array(EnvironmentNameSchema)
66
- .max(MAX_ENV_ENTRIES)
69
+ .max(MAX_INHERITED_ENVIRONMENT_NAMES)
67
70
  .describe("Daemon environment variable names copied at OMP spawn time")
68
71
  .optional(),
69
72
  outputRedaction: OmpOutputRedactionSchema.describe(
@@ -1,15 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
- import { basename, extname } from "node:path";
2
+ import { basename, extname, isAbsolute } from "node:path";
3
3
  import type { ProviderEvent } from "@getpaseo/plugin/server/provider";
4
4
  import { z } from "zod";
5
+ import type { OmpRuntime, OmpRuntimeSession } from "./omp-rpc";
5
6
  import type {
6
7
  OmpAgentSessionEvent,
7
8
  OmpMessage,
8
- OmpRuntime,
9
- OmpRuntimeSession,
10
9
  OmpSubagentEvent,
11
10
  OmpSubagentSnapshot,
12
- } from "./omp-rpc";
11
+ } from "./omp-rpc-protocol";
13
12
  import {
14
13
  BoundedStringSet,
15
14
  boundedJsonBytes,
@@ -882,10 +881,32 @@ export class OmpSubsessionProjector {
882
881
  signal.throwIfAborted();
883
882
  if (this.sessionIdByNativeId.has(snapshot.id)) continue;
884
883
  try {
885
- const history = await waitForReplay(
886
- runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
887
- signal,
888
- );
884
+ let history: ReplayHistory;
885
+ if (snapshot.sessionFile && isAbsolute(snapshot.sessionFile) && this.rootSessionFile) {
886
+ try {
887
+ history = await waitForReplay(
888
+ runtime.readPersistedSubagentTranscript({
889
+ parentSessionFile: this.rootSessionFile,
890
+ childTranscriptId: snapshot.id,
891
+ sessionFile: snapshot.sessionFile,
892
+ cwd: this.cwd,
893
+ signal,
894
+ }),
895
+ signal,
896
+ );
897
+ } catch (error) {
898
+ if (signal.aborted) throw error;
899
+ history = await waitForReplay(
900
+ runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
901
+ signal,
902
+ );
903
+ }
904
+ } else {
905
+ history = await waitForReplay(
906
+ runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
907
+ signal,
908
+ );
909
+ }
889
910
  try {
890
911
  this.accountReplay(history.messages, budget, signal);
891
912
  collected.push({
@@ -899,7 +920,7 @@ export class OmpSubsessionProjector {
899
920
  }
900
921
  } catch (error) {
901
922
  if (signal.aborted) throw error;
902
- collected.push({ snapshot });
923
+ collected.push({ snapshot, sessionFile: snapshot.sessionFile });
903
924
  }
904
925
  }
905
926
  collected.sort((left, right) => {
@@ -6,7 +6,12 @@ import type {
6
6
  } from "@getpaseo/plugin/server/provider";
7
7
  import { OMP_MCP_AUTH_TIMELINE_KIND } from "../../shared/mcp";
8
8
  import { isOmpImageMimeType, isValidImagePayload, type OmpImageMimeType } from "./image";
9
- import { OMP_MAX_CONTENT_PARTS, type OmpMessage, type OmpRpcEvent } from "./omp-rpc";
9
+ import {
10
+ OMP_MAX_CONTENT_PARTS,
11
+ OMP_MESSAGE_REPLAY_POLICIES,
12
+ type OmpMessage,
13
+ type OmpRpcEvent,
14
+ } from "./omp-rpc-protocol";
10
15
  import {
11
16
  boundedJsonBytes,
12
17
  type JsonValue,
@@ -75,6 +80,10 @@ type ToolSnapshot = {
75
80
  specializedRendered: boolean;
76
81
  silent: boolean;
77
82
  };
83
+ type PendingToolStream = {
84
+ output: JsonValue;
85
+ retainedBytes: number;
86
+ };
78
87
 
79
88
  export interface OmpTimelineScheduler {
80
89
  set(callback: () => void | Promise<void>, delayMs: number): unknown;
@@ -134,6 +143,10 @@ function imageBlock(data: string, mimeType: string): StreamBlockSnapshot | undef
134
143
  }
135
144
 
136
145
  type OmpAssistantMessage = Extract<OmpMessage, { role: "assistant" }>;
146
+ type OmpCustomDisplayMessage = Extract<
147
+ OmpMessage,
148
+ { role: "bashExecution" | "pythonExecution" | "custom" | "hookMessage" }
149
+ >;
137
150
 
138
151
  function blockText(
139
152
  message: OmpAssistantMessage,
@@ -345,6 +358,8 @@ type CompactionSlot = {
345
358
 
346
359
  export class OmpTimelineProjector {
347
360
  private readonly tools = new Map<string, ToolSnapshot>();
361
+ private readonly pendingToolStreams = new Map<string, PendingToolStream>();
362
+ private pendingToolStreamBytes = 0;
348
363
  private stream: StreamSnapshot | null = null;
349
364
  private flushTimer: unknown;
350
365
  private currentTurnId: string | null = null;
@@ -417,16 +432,35 @@ export class OmpTimelineProjector {
417
432
  return;
418
433
  }
419
434
  }
435
+ if (
436
+ (event.type === "message_start" ||
437
+ event.type === "message_update" ||
438
+ event.type === "message_end") &&
439
+ OMP_MESSAGE_REPLAY_POLICIES[event.message.role] === "ignore"
440
+ ) {
441
+ return;
442
+ }
420
443
  if (
421
444
  event.type === "todo_reminder" ||
422
445
  event.type === "todo_auto_clear" ||
446
+ event.type === "goal_updated" ||
447
+ event.type === "auto_retry_start" ||
448
+ event.type === "auto_retry_end" ||
449
+ event.type === "retry_fallback_applied" ||
450
+ event.type === "retry_fallback_succeeded" ||
423
451
  event.type === "notice" ||
424
452
  event.type === "extension_ui_request" ||
425
453
  event.type === "auto_compaction_start" ||
426
454
  event.type === "auto_compaction_end" ||
427
455
  event.type === "compaction_start" ||
428
456
  event.type === "compaction_end" ||
429
- event.type === "advisor_yielded"
457
+ event.type === "advisor_yielded" ||
458
+ event.type === "config_warnings_changed" ||
459
+ event.type === "advisor_cost_changed" ||
460
+ event.type === "ttsr_triggered" ||
461
+ event.type === "irc_message" ||
462
+ event.type === "model_changed" ||
463
+ event.type === "thinking_level_changed"
430
464
  ) {
431
465
  this.projectPassive(event);
432
466
  return;
@@ -449,7 +483,12 @@ export class OmpTimelineProjector {
449
483
  this.scheduleFlush();
450
484
  return;
451
485
  case "message_end":
452
- if (event.message.role === "custom" || event.message.role === "bashExecution") {
486
+ if (
487
+ event.message.role === "custom" ||
488
+ event.message.role === "hookMessage" ||
489
+ event.message.role === "bashExecution" ||
490
+ event.message.role === "pythonExecution"
491
+ ) {
453
492
  this.publishCustomMessage(event.message);
454
493
  return;
455
494
  }
@@ -460,6 +499,11 @@ export class OmpTimelineProjector {
460
499
  return;
461
500
  case "tool_execution_start": {
462
501
  this.flush(true);
502
+ const pendingStream = this.pendingToolStreams.get(event.toolCallId);
503
+ if (pendingStream) {
504
+ this.pendingToolStreams.delete(event.toolCallId);
505
+ this.pendingToolStreamBytes -= pendingStream.retainedBytes;
506
+ }
463
507
  if (this.toolIdentitySaturated || this.retiredToolCallIds.has(event.toolCallId)) return;
464
508
  const previous = this.tools.get(event.toolCallId);
465
509
  if (!previous && this.tools.size >= MAX_ACTIVE_TOOLS) return;
@@ -468,7 +512,17 @@ export class OmpTimelineProjector {
468
512
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
469
513
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
470
514
  );
471
- const retainedBytes = boundedJsonBytes(input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
515
+ const inputBytes = boundedJsonBytes(input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
516
+ let output = pendingStream?.output ?? null;
517
+ let retainedBytes = inputBytes + (pendingStream?.retainedBytes ?? 0);
518
+ if (
519
+ pendingStream &&
520
+ this.activeToolBytes - (previous?.retainedBytes ?? 0) + retainedBytes >
521
+ MAX_ACTIVE_TOOL_BYTES
522
+ ) {
523
+ output = null;
524
+ retainedBytes = inputBytes;
525
+ }
472
526
  if (
473
527
  this.activeToolBytes - (previous?.retainedBytes ?? 0) + retainedBytes >
474
528
  MAX_ACTIVE_TOOL_BYTES
@@ -487,7 +541,7 @@ export class OmpTimelineProjector {
487
541
  nativeName: event.toolName,
488
542
  name: this.dataFilter.text(displayName, 256),
489
543
  input,
490
- output: null,
544
+ output,
491
545
  retainedBytes,
492
546
  turnId,
493
547
  generation: this.runtimeGeneration,
@@ -499,16 +553,30 @@ export class OmpTimelineProjector {
499
553
  if (!snapshot.silent) this.publishTool(snapshot, "running");
500
554
  return;
501
555
  }
502
- case "tool_execution_update": {
503
- const previous = this.tools.get(event.toolCallId);
504
- if (!previous) return;
505
- if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
556
+ case "tool_execution_update":
557
+ case "tool_stream_update": {
506
558
  const output = this.dataFilter.json(
507
- event.partialResult,
559
+ event.type === "tool_execution_update" ? event.partialResult : event.update,
508
560
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
509
561
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
510
562
  );
511
563
  const outputBytes = boundedJsonBytes(output, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
564
+ const previous = this.tools.get(event.toolCallId);
565
+ if (!previous) {
566
+ if (event.type !== "tool_stream_update") return;
567
+ const pending = this.pendingToolStreams.get(event.toolCallId);
568
+ if (!pending && this.pendingToolStreams.size >= MAX_ACTIVE_TOOLS) return;
569
+ if (
570
+ this.pendingToolStreamBytes - (pending?.retainedBytes ?? 0) + outputBytes >
571
+ MAX_ACTIVE_TOOL_BYTES
572
+ ) {
573
+ return;
574
+ }
575
+ this.pendingToolStreamBytes += outputBytes - (pending?.retainedBytes ?? 0);
576
+ this.pendingToolStreams.set(event.toolCallId, { output, retainedBytes: outputBytes });
577
+ return;
578
+ }
579
+ if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
512
580
  const inputBytes = boundedJsonBytes(previous.input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
513
581
  const retainedBytes = inputBytes + outputBytes;
514
582
  if (this.activeToolBytes - previous.retainedBytes + retainedBytes > MAX_ACTIVE_TOOL_BYTES)
@@ -600,6 +668,10 @@ export class OmpTimelineProjector {
600
668
  this.publishRetryFallback(event);
601
669
  return;
602
670
  }
671
+ if (event.type === "irc_message") {
672
+ this.publishCustomMessage(event.message as OmpCustomDisplayMessage);
673
+ return;
674
+ }
603
675
  if (event.type === "notice") {
604
676
  this.noticeSequence += 1;
605
677
  this.publish({
@@ -831,6 +903,7 @@ export class OmpTimelineProjector {
831
903
  this.activeToolBytes = 0;
832
904
  this.replayToolCalls.clear();
833
905
  this.tools.clear();
906
+ this.clearPendingToolStreams();
834
907
  this.commandText = "";
835
908
  this.commandPublishedText = "";
836
909
  this.revertEntryByToken.clear();
@@ -839,8 +912,9 @@ export class OmpTimelineProjector {
839
912
 
840
913
  projectReplayMessage(message: OmpMessage): void {
841
914
  if (this.closed) return;
842
- const nativeIdentity = assistantIdentity(message);
843
915
  this.replaySequence += 1;
916
+ if (OMP_MESSAGE_REPLAY_POLICIES[message.role] === "ignore") return;
917
+
844
918
  if (message.role === "user") {
845
919
  this.replayToolCalls.clear();
846
920
  if (this.replayTurnId) this.finishTurn(this.replayTurnId);
@@ -861,7 +935,9 @@ export class OmpTimelineProjector {
861
935
  }
862
936
  return;
863
937
  }
938
+
864
939
  if (message.role === "assistant") {
940
+ const nativeIdentity = assistantIdentity(message);
865
941
  this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
866
942
  this.projectingReplay = true;
867
943
  try {
@@ -880,6 +956,7 @@ export class OmpTimelineProjector {
880
956
  if (nativeIdentity) this.rememberReplayOccurrence(nativeIdentity, message);
881
957
  return;
882
958
  }
959
+
883
960
  this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
884
961
  if (message.role === "toolResult") {
885
962
  const replayCall = this.replayToolCalls.get(message.toolCallId);
@@ -910,10 +987,18 @@ export class OmpTimelineProjector {
910
987
  );
911
988
  return;
912
989
  }
913
- if (message.role === "bashExecution") {
990
+
991
+ if (
992
+ message.role === "bashExecution" ||
993
+ message.role === "pythonExecution" ||
994
+ message.role === "custom" ||
995
+ message.role === "hookMessage"
996
+ ) {
914
997
  this.publishCustomMessage(message);
915
- this.finishTurn(this.replayTurnId);
916
- this.replayTurnId = null;
998
+ if (message.role === "bashExecution" || message.role === "pythonExecution") {
999
+ this.finishTurn(this.replayTurnId);
1000
+ this.replayTurnId = null;
1001
+ }
917
1002
  }
918
1003
  }
919
1004
 
@@ -1086,6 +1171,7 @@ export class OmpTimelineProjector {
1086
1171
  this.clearFlushTimer();
1087
1172
  this.stream = null;
1088
1173
  this.tools.clear();
1174
+ this.clearPendingToolStreams();
1089
1175
  this.replayToolCalls.clear();
1090
1176
  this.activeToolBytes = 0;
1091
1177
  this.replayCandidates.clear();
@@ -1126,6 +1212,11 @@ export class OmpTimelineProjector {
1126
1212
  }
1127
1213
  this.activeToolBytes = 0;
1128
1214
  this.tools.clear();
1215
+ this.clearPendingToolStreams();
1216
+ }
1217
+ private clearPendingToolStreams(): void {
1218
+ this.pendingToolStreams.clear();
1219
+ this.pendingToolStreamBytes = 0;
1129
1220
  }
1130
1221
  private ensureTurn(turnId: string): void {
1131
1222
  if (this.currentTurnId === turnId) return;
@@ -1350,9 +1441,17 @@ export class OmpTimelineProjector {
1350
1441
  return undefined;
1351
1442
  }
1352
1443
 
1353
- private publishCustomMessage(message: OmpMessage): void {
1354
- if (message.display === false) return;
1355
- const rawType = message.customType ?? message.role;
1444
+ private publishCustomMessage(message: OmpCustomDisplayMessage): void {
1445
+ if (
1446
+ (message.role === "custom" || message.role === "hookMessage") &&
1447
+ message.display === false
1448
+ ) {
1449
+ return;
1450
+ }
1451
+ const rawType =
1452
+ message.role === "custom" || message.role === "hookMessage"
1453
+ ? (message.customType ?? message.role)
1454
+ : message.role;
1356
1455
  const publicType = this.dataFilter.text(rawType, 256);
1357
1456
  const lowerType = rawType.toLowerCase();
1358
1457
  const details = jsonRecord(this.dataFilter.json(message.details ?? null));
@@ -1361,7 +1460,9 @@ export class OmpTimelineProjector {
1361
1460
  const id = nativeIdentity
1362
1461
  ? `omp:custom:${createHash("sha256").update(nativeIdentity).digest("base64url").slice(0, 12)}`
1363
1462
  : `omp:custom:${this.customSequence}`;
1364
- const contentParts = Array.isArray(message.content) ? message.content : [];
1463
+ const messageContent =
1464
+ message.role === "custom" || message.role === "hookMessage" ? message.content : undefined;
1465
+ const contentParts = Array.isArray(messageContent) ? messageContent : [];
1365
1466
  const imageResult = nativeImageResult(
1366
1467
  {
1367
1468
  content:
@@ -1373,16 +1474,26 @@ export class OmpTimelineProjector {
1373
1474
  this.dataFilter,
1374
1475
  );
1375
1476
  const content =
1376
- typeof message.content === "string"
1377
- ? message.content
1477
+ typeof messageContent === "string"
1478
+ ? messageContent
1378
1479
  : contentParts
1379
1480
  .flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
1380
1481
  .join("\n\n");
1381
- if (message.role === "bashExecution" || /bash|shell|python/u.test(lowerType)) {
1482
+ const execution = message.role === "bashExecution" || message.role === "pythonExecution";
1483
+ if (execution || /bash|shell|python/u.test(lowerType)) {
1484
+ const nativeCommand =
1485
+ message.role === "bashExecution"
1486
+ ? message.command
1487
+ : message.role === "pythonExecution"
1488
+ ? message.code
1489
+ : undefined;
1382
1490
  const command = this.dataFilter.text(
1383
- message.command ?? firstString(details, "command", "input") ?? publicType,
1491
+ nativeCommand ?? firstString(details, "command", "input") ?? publicType,
1384
1492
  );
1385
- const output = message.output ?? content;
1493
+ const output = execution ? (message.output ?? content) : content;
1494
+ const exitCode = execution ? message.exitCode : undefined;
1495
+ const cancelled = execution ? message.cancelled : undefined;
1496
+ const truncated = execution ? message.truncated : undefined;
1386
1497
  this.publish({
1387
1498
  type: "tool_call",
1388
1499
  id,
@@ -1393,21 +1504,21 @@ export class OmpTimelineProjector {
1393
1504
  command,
1394
1505
  ...(firstString(details, "cwd") ? { cwd: firstString(details, "cwd") } : {}),
1395
1506
  ...(output ? { output: this.dataFilter.text(output, MAX_STREAM_TEXT_LENGTH) } : {}),
1396
- ...(typeof message.exitCode === "number" || message.exitCode === null
1397
- ? { exitCode: message.exitCode }
1507
+ ...(typeof exitCode === "number" || exitCode === null
1508
+ ? { exitCode }
1398
1509
  : typeof details?.exitCode === "number"
1399
1510
  ? { exitCode: details.exitCode }
1400
1511
  : {}),
1401
1512
  },
1402
- ...(message.cancelled !== undefined || message.truncated !== undefined
1513
+ ...(cancelled !== undefined || truncated !== undefined
1403
1514
  ? {
1404
1515
  metadata: {
1405
- ...(message.cancelled !== undefined ? { cancelled: message.cancelled } : {}),
1406
- ...(message.truncated !== undefined ? { truncated: message.truncated } : {}),
1516
+ ...(cancelled !== undefined ? { cancelled } : {}),
1517
+ ...(truncated !== undefined ? { truncated } : {}),
1407
1518
  },
1408
1519
  }
1409
1520
  : {}),
1410
- status: message.cancelled ? "canceled" : "completed",
1521
+ status: cancelled ? "canceled" : "completed",
1411
1522
  error: null,
1412
1523
  });
1413
1524
  if (imageResult) this.publishImages(id, publicType, imageResult.image);
@@ -1,12 +1,3 @@
1
- export type ExternalUrlOpener = (url: string) => Promise<void>;
2
-
3
- export function selectExternalUrlOpener(
4
- paseoOpener: ExternalUrlOpener | undefined,
5
- fallback: ExternalUrlOpener,
6
- ): ExternalUrlOpener {
7
- return paseoOpener ?? fallback;
8
- }
9
-
10
1
  export function validatedHttpUrl(value: string): string {
11
2
  let url: URL;
12
3
  try {
@@ -0,0 +1,18 @@
1
+ import { defineSettings } from "@getpaseo/plugin";
2
+ import { z } from "zod";
3
+
4
+ export const MAX_INHERITED_ENVIRONMENT_NAMES = 256;
5
+ export const environmentNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u);
6
+
7
+ export const providerLaunchSettingsSchema = z.object({
8
+ inheritEnv: z.array(environmentNameSchema).max(MAX_INHERITED_ENVIRONMENT_NAMES).default([]),
9
+ });
10
+
11
+ export type ProviderLaunchSettings = z.infer<typeof providerLaunchSettingsSchema>;
12
+
13
+ export const providerLaunchSettings = defineSettings({
14
+ id: "provider-launch",
15
+ scope: "host",
16
+ version: 1,
17
+ schema: providerLaunchSettingsSchema,
18
+ });