@rynfar/meridian 1.64.0 → 1.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  __commonJS,
3
+ __esm,
3
4
  __require,
4
5
  __toESM
5
6
  } from "./cli-p9swy5t3.js";
@@ -460,11 +461,185 @@ var require_cross_spawn = __commonJS((exports, module) => {
460
461
  module.exports._enoent = enoent;
461
462
  });
462
463
 
464
+ // src/proxy/priorityAttestation.ts
465
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
466
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
467
+ import { homedir, platform } from "node:os";
468
+ import { dirname, join } from "node:path";
469
+ function configDirectory() {
470
+ return process.env.MERIDIAN_CONFIG_DIR ?? join(homedir(), ".config", "meridian");
471
+ }
472
+ function priorityAttestationKeyPath() {
473
+ return join(configDirectory(), PRIORITY_ATTESTATION_KEY_FILE);
474
+ }
475
+ function decodeCanonicalBase64Url(raw) {
476
+ if (!/^[A-Za-z0-9_-]+$/.test(raw))
477
+ return;
478
+ const decoded = Buffer.from(raw, "base64url");
479
+ return decoded.toString("base64url") === raw ? decoded : undefined;
480
+ }
481
+ function decodePriorityAttestationKey(raw) {
482
+ if (raw === undefined)
483
+ return;
484
+ const decoded = decodeCanonicalBase64Url(raw.trim());
485
+ return decoded?.length === 32 ? decoded : undefined;
486
+ }
487
+ function loadPriorityAttestationKey() {
488
+ const fromEnv = process.env[PRIORITY_ATTESTATION_KEY_ENV];
489
+ if (fromEnv !== undefined)
490
+ return decodePriorityAttestationKey(fromEnv);
491
+ try {
492
+ return decodePriorityAttestationKey(readFileSync(priorityAttestationKeyPath(), "utf8"));
493
+ } catch {
494
+ return;
495
+ }
496
+ }
497
+ function readRequiredFileKey(path) {
498
+ let raw;
499
+ try {
500
+ raw = readFileSync(path, "utf8");
501
+ } catch {
502
+ throw new InvalidPriorityAttestationKeyError(path);
503
+ }
504
+ const key = decodePriorityAttestationKey(raw);
505
+ if (!key)
506
+ throw new InvalidPriorityAttestationKeyError(path);
507
+ return key;
508
+ }
509
+ function ensurePriorityAttestationKey() {
510
+ const fromEnv = process.env[PRIORITY_ATTESTATION_KEY_ENV];
511
+ if (fromEnv !== undefined) {
512
+ const key2 = decodePriorityAttestationKey(fromEnv);
513
+ if (!key2)
514
+ throw new InvalidPriorityAttestationKeyError(PRIORITY_ATTESTATION_KEY_ENV);
515
+ return { key: key2 };
516
+ }
517
+ const path = priorityAttestationKeyPath();
518
+ try {
519
+ const key2 = readRequiredFileKey(path);
520
+ if (platform() !== "win32")
521
+ chmodSync(path, 384);
522
+ return { key: key2, path };
523
+ } catch (error) {
524
+ if (!(error instanceof InvalidPriorityAttestationKeyError))
525
+ throw error;
526
+ try {
527
+ readFileSync(path);
528
+ throw error;
529
+ } catch (readError) {
530
+ if (readError === error)
531
+ throw error;
532
+ }
533
+ }
534
+ mkdirSync(dirname(path), { recursive: true });
535
+ const encoded = randomBytes(32).toString("base64url");
536
+ try {
537
+ writeFileSync(path, `${encoded}
538
+ `, { encoding: "utf8", flag: "wx", mode: 384 });
539
+ } catch (error) {
540
+ const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : undefined;
541
+ if (code !== "EEXIST")
542
+ throw error;
543
+ }
544
+ const key = readRequiredFileKey(path);
545
+ if (platform() !== "win32")
546
+ chmodSync(path, 384);
547
+ return { key, path };
548
+ }
549
+ function isRecord(value) {
550
+ return typeof value === "object" && value !== null && !Array.isArray(value);
551
+ }
552
+ function isSafeAgentId(value) {
553
+ return value.length >= 1 && value.length <= 64 && value.trim() === value && /^[\x20-\x7E]+$/.test(value);
554
+ }
555
+ function parseCanonicalPayload(raw) {
556
+ if (Buffer.byteLength(raw) > MAX_PAYLOAD_BYTES)
557
+ return;
558
+ let value;
559
+ try {
560
+ value = JSON.parse(raw);
561
+ } catch {
562
+ return;
563
+ }
564
+ if (!isRecord(value))
565
+ return;
566
+ if (Object.keys(value).join(",") !== "v,g,s,a,t,iat")
567
+ return;
568
+ if (value.v !== 1)
569
+ return;
570
+ if (value.g !== "oc1" && value.g !== "oc2b18314")
571
+ return;
572
+ if (typeof value.s !== "string" || !SAFE_ID_PATTERN.test(value.s))
573
+ return;
574
+ if (typeof value.a !== "string" || !isSafeAgentId(value.a))
575
+ return;
576
+ if (typeof value.t !== "string" || !TURN_DIGEST_PATTERN.test(value.t))
577
+ return;
578
+ if (typeof value.iat !== "number" || !Number.isSafeInteger(value.iat) || value.iat < 0)
579
+ return;
580
+ const canonical = JSON.stringify({
581
+ v: 1,
582
+ g: value.g,
583
+ s: value.s,
584
+ a: value.a,
585
+ t: value.t,
586
+ iat: value.iat
587
+ });
588
+ if (canonical !== raw)
589
+ return;
590
+ return {
591
+ generation: value.g,
592
+ sessionId: value.s,
593
+ agentId: value.a,
594
+ turnId: value.t,
595
+ issuedAt: value.iat
596
+ };
597
+ }
598
+ function verifyPriorityAttestation(token, key = loadPriorityAttestationKey() ?? Buffer.alloc(0), nowSeconds = Math.floor(Date.now() / 1000)) {
599
+ if (!token || key.length !== 32 || Buffer.byteLength(token) > MAX_HEADER_BYTES)
600
+ return;
601
+ const match = TOKEN_PATTERN.exec(token);
602
+ if (!match?.[1] || !match[2])
603
+ return;
604
+ const payloadBytes = decodeCanonicalBase64Url(match[1]);
605
+ const suppliedMac = decodeCanonicalBase64Url(match[2]);
606
+ if (!payloadBytes || !suppliedMac || suppliedMac.length !== 32)
607
+ return;
608
+ const payloadRaw = payloadBytes.toString("utf8");
609
+ if (Buffer.from(payloadRaw, "utf8").compare(payloadBytes) !== 0)
610
+ return;
611
+ const payload = parseCanonicalPayload(payloadRaw);
612
+ if (!payload)
613
+ return;
614
+ if (payload.issuedAt < nowSeconds - MAX_PAST_AGE_SECONDS)
615
+ return;
616
+ if (payload.issuedAt > nowSeconds + MAX_FUTURE_SKEW_SECONDS)
617
+ return;
618
+ const expectedMac = createHmac("sha256", key).update(MAC_DOMAIN).update(payloadRaw).digest();
619
+ if (!timingSafeEqual(expectedMac, suppliedMac))
620
+ return;
621
+ return payload;
622
+ }
623
+ var PRIORITY_ATTESTATION_HEADER = "x-meridian-opencode-turn", PRIORITY_ATTESTATION_KEY_ENV = "MERIDIAN_OPENCODE_ATTESTATION_KEY", PRIORITY_ATTESTATION_KEY_FILE = "opencode-turn.key", TOKEN_PATTERN, MAC_DOMAIN = "meridian.opencode.turn.v1\x00", MAX_HEADER_BYTES = 768, MAX_PAYLOAD_BYTES = 384, MAX_PAST_AGE_SECONDS = 120, MAX_FUTURE_SKEW_SECONDS = 30, SAFE_ID_PATTERN, TURN_DIGEST_PATTERN, InvalidPriorityAttestationKeyError;
624
+ var init_priorityAttestation = __esm(() => {
625
+ TOKEN_PATTERN = /^v1\.([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]{43})$/;
626
+ SAFE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
627
+ TURN_DIGEST_PATTERN = /^[A-Za-z0-9_-]{43}$/;
628
+ InvalidPriorityAttestationKeyError = class InvalidPriorityAttestationKeyError extends Error {
629
+ keyPath;
630
+ constructor(keyPath) {
631
+ super(`OpenCode routing attestation key is invalid at ${keyPath}`);
632
+ this.keyPath = keyPath;
633
+ this.name = "InvalidPriorityAttestationKeyError";
634
+ }
635
+ };
636
+ });
637
+
463
638
  // src/proxy/setup.ts
464
639
  var import_cross_spawn = __toESM(require_cross_spawn(), 1);
465
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
466
- import { homedir, platform } from "os";
467
- import { basename, dirname, join } from "path";
640
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
641
+ import { homedir as homedir2, platform as platform2 } from "os";
642
+ import { basename, dirname as dirname2, join as join2 } from "path";
468
643
  import { fileURLToPath } from "url";
469
644
  import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
470
645
 
@@ -534,6 +709,8 @@ class LRUMap {
534
709
  }
535
710
 
536
711
  // src/proxy/setup.ts
712
+ init_priorityAttestation();
713
+
537
714
  class UnparseableConfigError extends Error {
538
715
  configPath;
539
716
  constructor(configPath) {
@@ -574,40 +751,40 @@ function opencodeConfigDirectory() {
574
751
  if (process.env.OPENCODE_CONFIG_DIR)
575
752
  return process.env.OPENCODE_CONFIG_DIR;
576
753
  if (process.env.XDG_CONFIG_HOME)
577
- return join(process.env.XDG_CONFIG_HOME, "opencode");
578
- if (platform() === "win32" && process.env.APPDATA)
579
- return join(process.env.APPDATA, "opencode");
580
- return join(homedir(), ".config", "opencode");
754
+ return join2(process.env.XDG_CONFIG_HOME, "opencode");
755
+ if (platform2() === "win32" && process.env.APPDATA)
756
+ return join2(process.env.APPDATA, "opencode");
757
+ return join2(homedir2(), ".config", "opencode");
581
758
  }
582
759
  function findOpencodeConfigPath() {
583
760
  const dir = opencodeConfigDirectory();
584
- const jsonPath = join(dir, "opencode.json");
585
- const jsoncPath = join(dir, "opencode.jsonc");
761
+ const jsonPath = join2(dir, "opencode.json");
762
+ const jsoncPath = join2(dir, "opencode.jsonc");
586
763
  return !existsSync(jsonPath) && existsSync(jsoncPath) ? jsoncPath : jsonPath;
587
764
  }
588
765
  function siblingOpencodeConfigPath(configPath) {
589
766
  const name = basename(configPath);
590
767
  if (name === "opencode.json")
591
- return join(dirname(configPath), "opencode.jsonc");
768
+ return join2(dirname2(configPath), "opencode.jsonc");
592
769
  if (name === "opencode.jsonc")
593
- return join(dirname(configPath), "opencode.json");
770
+ return join2(dirname2(configPath), "opencode.json");
594
771
  return;
595
772
  }
596
773
  function findPluginPath(fromUrl) {
597
- const dir = dirname(fileURLToPath(fromUrl));
598
- return join(dir, "..", "plugin", "meridian.ts");
774
+ const dir = dirname2(fileURLToPath(fromUrl));
775
+ return join2(dir, "..", "plugin", "meridian.ts");
599
776
  }
600
777
  var SUPPORTED_OPENCODE_V2_VERSION = "0.0.0-beta-18314";
601
778
  function findV2PluginPath(fromUrl) {
602
779
  const entryPath = fileURLToPath(fromUrl);
603
- const dir = dirname(entryPath);
780
+ const dir = dirname2(entryPath);
604
781
  if (entryPath.endsWith(".ts")) {
605
- const sourcePlugin = join(dir, "..", "plugin", "meridian-v2.ts");
782
+ const sourcePlugin = join2(dir, "..", "plugin", "meridian-v2.ts");
606
783
  if (existsSync(sourcePlugin))
607
784
  return sourcePlugin;
608
785
  throw new MissingV2PluginError(sourcePlugin);
609
786
  }
610
- const bundledPlugin = join(dir, "meridian-v2.js");
787
+ const bundledPlugin = join2(dir, "meridian-v2.js");
611
788
  if (existsSync(bundledPlugin))
612
789
  return bundledPlugin;
613
790
  throw new MissingV2PluginError(bundledPlugin);
@@ -672,7 +849,7 @@ function checkPluginConfigured(configPath, expectedPluginPath) {
672
849
  return paths.some((path) => {
673
850
  if (!existsSync(path))
674
851
  return false;
675
- const config = parseOpencodeConfig(readFileSync(path, "utf-8"));
852
+ const config = parseOpencodeConfig(readFileSync2(path, "utf-8"));
676
853
  if (config === null)
677
854
  return false;
678
855
  const plugins = [
@@ -702,12 +879,12 @@ function notePluginlessOpenCodeRequest(input) {
702
879
  }
703
880
  function runSetup(pluginPath, configPath, generation = "v1") {
704
881
  const path = configPath ?? findOpencodeConfigPath();
705
- const dir = dirname(path);
882
+ const dir = dirname2(path);
706
883
  const targetField = generation === "v2" ? "plugins" : "plugin";
707
884
  const otherField = generation === "v2" ? "plugin" : "plugins";
708
885
  const siblingPath = siblingOpencodeConfigPath(path);
709
886
  if (siblingPath && existsSync(siblingPath)) {
710
- const siblingConfig = parseOpencodeConfig(readFileSync(siblingPath, "utf-8"));
887
+ const siblingConfig = parseOpencodeConfig(readFileSync2(siblingPath, "utf-8"));
711
888
  if (siblingConfig === null)
712
889
  throw new UnparseableConfigError(siblingPath);
713
890
  const siblingPlugins = [
@@ -719,13 +896,14 @@ function runSetup(pluginPath, configPath, generation = "v1") {
719
896
  }
720
897
  }
721
898
  if (!existsSync(path)) {
899
+ ensurePriorityAttestationKey();
722
900
  if (!existsSync(dir))
723
- mkdirSync(dir, { recursive: true });
724
- writeFileSync(path, `${JSON.stringify({ [targetField]: [pluginPath] }, null, 2)}
901
+ mkdirSync2(dir, { recursive: true });
902
+ writeFileSync2(path, `${JSON.stringify({ [targetField]: [pluginPath] }, null, 2)}
725
903
  `, "utf-8");
726
904
  return { configPath: path, pluginPath, alreadyConfigured: false, removedStale: [], created: true };
727
905
  }
728
- const text = readFileSync(path, "utf-8");
906
+ const text = readFileSync2(path, "utf-8");
729
907
  const config = parseOpencodeConfig(text);
730
908
  if (config === null) {
731
909
  throw new UnparseableConfigError(path);
@@ -753,8 +931,9 @@ function runSetup(pluginPath, configPath, generation = "v1") {
753
931
  if (Array.isArray(config[otherField]) && otherMeridian.length > 0) {
754
932
  updated = applyEdits(updated, modify(updated, [otherField], otherPlugins, { formattingOptions }));
755
933
  }
756
- writeFileSync(path, updated, "utf-8");
934
+ ensurePriorityAttestationKey();
935
+ writeFileSync2(path, updated, "utf-8");
757
936
  return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
758
937
  }
759
938
 
760
- export { LRUMap, UnparseableConfigError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSION, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
939
+ export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSION, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
@@ -18,6 +18,12 @@ function getRoutingMode(raw) {
18
18
  return "priority";
19
19
  return "active";
20
20
  }
21
+ function getPriorityFailbackPolicy(raw) {
22
+ return raw?.toLowerCase() === "next-user-turn" ? "next-user-turn" : "new-conversation";
23
+ }
24
+ function shouldPromotePriorityAssignment(input) {
25
+ return input.policy === "next-user-turn" && input.requestKind === "human" && input.requestId !== undefined && input.requestId !== input.assignment.requestId;
26
+ }
21
27
  function rendezvousScore(sessionKey, profileId) {
22
28
  const digest = createHash("sha256").update(`${sessionKey}\x00${profileId}`).digest();
23
29
  return digest.readBigUInt64BE(0);
@@ -54,13 +60,14 @@ function resolvePriorityOrder(configuredIds, orderSetting) {
54
60
  return { order, unknown };
55
61
  }
56
62
  function choosePriorityProfile(order, isExhausted) {
57
- if (order.length === 0)
63
+ const preferred = order[0];
64
+ if (preferred === undefined)
58
65
  return;
59
66
  for (const id of order) {
60
67
  if (!isExhausted(id))
61
68
  return { id, allExhausted: false };
62
69
  }
63
- return { id: order[0], allExhausted: true };
70
+ return { id: preferred, allExhausted: true };
64
71
  }
65
72
 
66
73
  class ProfileExhaustion {
@@ -121,6 +128,12 @@ class AssignmentStore {
121
128
  this.entries.delete(oldest);
122
129
  }
123
130
  }
131
+ compareAndSet(key, expected, value) {
132
+ if (this.entries.get(key) !== expected)
133
+ return false;
134
+ this.set(key, value);
135
+ return true;
136
+ }
124
137
  get size() {
125
138
  return this.entries.size;
126
139
  }
@@ -258,4 +271,4 @@ function listProfiles(profiles, defaultProfile) {
258
271
  }));
259
272
  }
260
273
 
261
- export { getRoutingMode, resolvePriorityOrder, choosePriorityProfile, ProfileExhaustion, AssignmentStore, resolveCooldownUntil, loadProfilesFromDisk, setActiveProfile, getActiveProfileId, resetActiveProfile, restoreActiveProfile, enableDiskProfileDiscovery, getEffectiveProfiles, hasProfiles, resolveProfile, listProfiles };
274
+ export { getRoutingMode, getPriorityFailbackPolicy, shouldPromotePriorityAssignment, resolvePriorityOrder, choosePriorityProfile, ProfileExhaustion, AssignmentStore, resolveCooldownUntil, loadProfilesFromDisk, setActiveProfile, getActiveProfileId, resetActiveProfile, restoreActiveProfile, enableDiskProfileDiscovery, getEffectiveProfiles, hasProfiles, resolveProfile, listProfiles };
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-4ghkaajs.js";
5
- import"./cli-m0p2bc8v.js";
4
+ } from "./cli-7cts44b5.js";
5
+ import"./cli-pdpry6q0.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-8yp89fan.js";
8
8
  import {
@@ -10,7 +10,7 @@ import {
10
10
  } from "./cli-0ed6j0vk.js";
11
11
  import"./cli-khhjyk04.js";
12
12
  import"./cli-vj9cv18n.js";
13
- import"./cli-pbvm9kc2.js";
13
+ import"./cli-n8t34zmq.js";
14
14
  import {
15
15
  __require
16
16
  } from "./cli-p9swy5t3.js";
@@ -93,7 +93,7 @@ if (args[0] === "setup") {
93
93
  runSetup,
94
94
  SUPPORTED_OPENCODE_V2_VERSION,
95
95
  UnparseableConfigError
96
- } = await import("./setup-4v3zg3c0.js");
96
+ } = await import("./setup-8fwgwhqh.js");
97
97
  const forceV1 = args.includes("--v1");
98
98
  const forceV2 = args.includes("--v2");
99
99
  if (forceV1 && forceV2) {
@@ -205,7 +205,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
205
205
  return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
206
206
  }) {
207
207
  try {
208
- const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-4v3zg3c0.js");
208
+ const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-8fwgwhqh.js");
209
209
  const configPath = findOpencodeConfigPath();
210
210
  const { existsSync } = await import("fs");
211
211
  if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
@@ -230,7 +230,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
230
230
  console.error("\x1B[33m⚠ Could not verify Claude auth status. If requests fail, run: claude login\x1B[0m");
231
231
  }
232
232
  if (!profiles) {
233
- const { enableDiskProfileDiscovery } = await import("./profiles-0pyeqayw.js");
233
+ const { enableDiskProfileDiscovery } = await import("./profiles-4ajzjqhm.js");
234
234
  enableDiskProfileDiscovery();
235
235
  }
236
236
  const proxy = await start({ port, host, idleTimeoutSeconds, pluginDir, pluginConfigPath, profiles, defaultProfile, version, installProcessErrorHandlers: true });