@themoltnet/legreffier 0.6.0 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +1241 -332
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -2,14 +2,38 @@
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import { parseArgs } from "node:util";
4
4
  import { useInput, Box, Text, useApp, render } from "ink";
5
+ import { execFileSync } from "node:child_process";
5
6
  import { join } from "node:path";
6
7
  import { useState, useEffect, useReducer, useRef } from "react";
7
8
  import figlet from "figlet";
8
9
  import { readFile, writeFile, mkdir, chmod, rm } from "node:fs/promises";
9
10
  import { homedir } from "node:os";
10
- import { randomBytes as randomBytes$2, createHash } from "crypto";
11
+ import { createHash, randomBytes as randomBytes$2 } from "crypto";
11
12
  import { parse, stringify } from "smol-toml";
12
13
  import open from "open";
14
+ const MOLTNET_GITCONFIG_RE = /\.moltnet\/([^/]+)\/gitconfig$/;
15
+ function resolveAgentName(nameFlag, gitConfigGlobal) {
16
+ if (nameFlag) return nameFlag;
17
+ if (gitConfigGlobal) {
18
+ const match = MOLTNET_GITCONFIG_RE.exec(gitConfigGlobal);
19
+ if (match) return match[1];
20
+ }
21
+ throw new Error(
22
+ "agent name required — use --name or set GIT_CONFIG_GLOBAL=.moltnet/<name>/gitconfig"
23
+ );
24
+ }
25
+ function resolveCredentialsPath(agentName, dir2) {
26
+ return join(dir2, ".moltnet", agentName, "moltnet.json");
27
+ }
28
+ function printGitHubToken(agentName, dir2) {
29
+ const credPath = resolveCredentialsPath(agentName, dir2);
30
+ const token = execFileSync(
31
+ "npx",
32
+ ["@themoltnet/cli", "github", "token", "--credentials", credPath],
33
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
34
+ ).trim();
35
+ process.stdout.write(token);
36
+ }
13
37
  const colors = {
14
38
  // Primary — teal/cyan (The Network)
15
39
  primary: {
@@ -350,13 +374,13 @@ function CliSummaryBox({
350
374
  }
351
375
  ) });
352
376
  }
353
- const jsonBodySerializer = {
377
+ const jsonBodySerializer$1 = {
354
378
  bodySerializer: (body) => JSON.stringify(
355
379
  body,
356
380
  (_key, value) => typeof value === "bigint" ? value.toString() : value
357
381
  )
358
382
  };
359
- const createSseClient = ({
383
+ const createSseClient$1 = ({
360
384
  onRequest,
361
385
  onSseError,
362
386
  onSseEvent,
@@ -490,7 +514,7 @@ const createSseClient = ({
490
514
  const stream = createStream();
491
515
  return { stream };
492
516
  };
493
- const separatorArrayExplode = (style) => {
517
+ const separatorArrayExplode$1 = (style) => {
494
518
  switch (style) {
495
519
  case "label":
496
520
  return ".";
@@ -502,7 +526,7 @@ const separatorArrayExplode = (style) => {
502
526
  return "&";
503
527
  }
504
528
  };
505
- const separatorArrayNoExplode = (style) => {
529
+ const separatorArrayNoExplode$1 = (style) => {
506
530
  switch (style) {
507
531
  case "form":
508
532
  return ",";
@@ -514,7 +538,7 @@ const separatorArrayNoExplode = (style) => {
514
538
  return ",";
515
539
  }
516
540
  };
517
- const separatorObjectExplode = (style) => {
541
+ const separatorObjectExplode$1 = (style) => {
518
542
  switch (style) {
519
543
  case "label":
520
544
  return ".";
@@ -526,7 +550,7 @@ const separatorObjectExplode = (style) => {
526
550
  return "&";
527
551
  }
528
552
  };
529
- const serializeArrayParam = ({
553
+ const serializeArrayParam$1 = ({
530
554
  allowReserved,
531
555
  explode,
532
556
  name: name2,
@@ -534,7 +558,7 @@ const serializeArrayParam = ({
534
558
  value
535
559
  }) => {
536
560
  if (!explode) {
537
- const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
561
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode$1(style));
538
562
  switch (style) {
539
563
  case "label":
540
564
  return `.${joinedValues2}`;
@@ -546,12 +570,12 @@ const serializeArrayParam = ({
546
570
  return `${name2}=${joinedValues2}`;
547
571
  }
548
572
  }
549
- const separator = separatorArrayExplode(style);
573
+ const separator = separatorArrayExplode$1(style);
550
574
  const joinedValues = value.map((v) => {
551
575
  if (style === "label" || style === "simple") {
552
576
  return allowReserved ? v : encodeURIComponent(v);
553
577
  }
554
- return serializePrimitiveParam({
578
+ return serializePrimitiveParam$1({
555
579
  allowReserved,
556
580
  name: name2,
557
581
  value: v
@@ -559,7 +583,7 @@ const serializeArrayParam = ({
559
583
  }).join(separator);
560
584
  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
561
585
  };
562
- const serializePrimitiveParam = ({
586
+ const serializePrimitiveParam$1 = ({
563
587
  allowReserved,
564
588
  name: name2,
565
589
  value
@@ -574,7 +598,7 @@ const serializePrimitiveParam = ({
574
598
  }
575
599
  return `${name2}=${allowReserved ? value : encodeURIComponent(value)}`;
576
600
  };
577
- const serializeObjectParam = ({
601
+ const serializeObjectParam$1 = ({
578
602
  allowReserved,
579
603
  explode,
580
604
  name: name2,
@@ -606,9 +630,9 @@ const serializeObjectParam = ({
606
630
  return joinedValues2;
607
631
  }
608
632
  }
609
- const separator = separatorObjectExplode(style);
633
+ const separator = separatorObjectExplode$1(style);
610
634
  const joinedValues = Object.entries(value).map(
611
- ([key, v]) => serializePrimitiveParam({
635
+ ([key, v]) => serializePrimitiveParam$1({
612
636
  allowReserved,
613
637
  name: style === "deepObject" ? `${name2}[${key}]` : key,
614
638
  value: v
@@ -616,10 +640,10 @@ const serializeObjectParam = ({
616
640
  ).join(separator);
617
641
  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
618
642
  };
619
- const PATH_PARAM_RE = /\{[^{}]+\}/g;
620
- const defaultPathSerializer = ({ path, url: _url }) => {
643
+ const PATH_PARAM_RE$1 = /\{[^{}]+\}/g;
644
+ const defaultPathSerializer$1 = ({ path, url: _url }) => {
621
645
  let url = _url;
622
- const matches = _url.match(PATH_PARAM_RE);
646
+ const matches = _url.match(PATH_PARAM_RE$1);
623
647
  if (matches) {
624
648
  for (const match of matches) {
625
649
  let explode = false;
@@ -643,14 +667,14 @@ const defaultPathSerializer = ({ path, url: _url }) => {
643
667
  if (Array.isArray(value)) {
644
668
  url = url.replace(
645
669
  match,
646
- serializeArrayParam({ explode, name: name2, style, value })
670
+ serializeArrayParam$1({ explode, name: name2, style, value })
647
671
  );
648
672
  continue;
649
673
  }
650
674
  if (typeof value === "object") {
651
675
  url = url.replace(
652
676
  match,
653
- serializeObjectParam({
677
+ serializeObjectParam$1({
654
678
  explode,
655
679
  name: name2,
656
680
  style,
@@ -663,7 +687,7 @@ const defaultPathSerializer = ({ path, url: _url }) => {
663
687
  if (style === "matrix") {
664
688
  url = url.replace(
665
689
  match,
666
- `;${serializePrimitiveParam({
690
+ `;${serializePrimitiveParam$1({
667
691
  name: name2,
668
692
  value
669
693
  })}`
@@ -678,7 +702,7 @@ const defaultPathSerializer = ({ path, url: _url }) => {
678
702
  }
679
703
  return url;
680
704
  };
681
- const getUrl = ({
705
+ const getUrl$1 = ({
682
706
  baseUrl,
683
707
  path,
684
708
  query,
@@ -688,7 +712,7 @@ const getUrl = ({
688
712
  const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
689
713
  let url = (baseUrl ?? "") + pathUrl;
690
714
  if (path) {
691
- url = defaultPathSerializer({ path, url });
715
+ url = defaultPathSerializer$1({ path, url });
692
716
  }
693
717
  let search = query ? querySerializer(query) : "";
694
718
  if (search.startsWith("?")) {
@@ -699,7 +723,7 @@ const getUrl = ({
699
723
  }
700
724
  return url;
701
725
  };
702
- function getValidRequestBody(options) {
726
+ function getValidRequestBody$1(options) {
703
727
  const hasBody = options.body !== void 0;
704
728
  const isSerializedBody = hasBody && options.bodySerializer;
705
729
  if (isSerializedBody) {
@@ -714,7 +738,7 @@ function getValidRequestBody(options) {
714
738
  }
715
739
  return void 0;
716
740
  }
717
- const getAuthToken = async (auth, callback) => {
741
+ const getAuthToken$1 = async (auth, callback) => {
718
742
  const token = typeof callback === "function" ? await callback(auth) : callback;
719
743
  if (!token) {
720
744
  return;
@@ -727,7 +751,7 @@ const getAuthToken = async (auth, callback) => {
727
751
  }
728
752
  return token;
729
753
  };
730
- const createQuerySerializer = ({
754
+ const createQuerySerializer$1 = ({
731
755
  parameters = {},
732
756
  ...args
733
757
  } = {}) => {
@@ -741,7 +765,7 @@ const createQuerySerializer = ({
741
765
  }
742
766
  const options = parameters[name2] || args;
743
767
  if (Array.isArray(value)) {
744
- const serializedArray = serializeArrayParam({
768
+ const serializedArray = serializeArrayParam$1({
745
769
  allowReserved: options.allowReserved,
746
770
  explode: true,
747
771
  name: name2,
@@ -751,7 +775,7 @@ const createQuerySerializer = ({
751
775
  });
752
776
  if (serializedArray) search.push(serializedArray);
753
777
  } else if (typeof value === "object") {
754
- const serializedObject = serializeObjectParam({
778
+ const serializedObject = serializeObjectParam$1({
755
779
  allowReserved: options.allowReserved,
756
780
  explode: true,
757
781
  name: name2,
@@ -761,7 +785,7 @@ const createQuerySerializer = ({
761
785
  });
762
786
  if (serializedObject) search.push(serializedObject);
763
787
  } else {
764
- const serializedPrimitive = serializePrimitiveParam({
788
+ const serializedPrimitive = serializePrimitiveParam$1({
765
789
  allowReserved: options.allowReserved,
766
790
  name: name2,
767
791
  value
@@ -774,7 +798,7 @@ const createQuerySerializer = ({
774
798
  };
775
799
  return querySerializer;
776
800
  };
777
- const getParseAs = (contentType) => {
801
+ const getParseAs$1 = (contentType) => {
778
802
  if (!contentType) {
779
803
  return "stream";
780
804
  }
@@ -798,7 +822,7 @@ const getParseAs = (contentType) => {
798
822
  }
799
823
  return;
800
824
  };
801
- const checkForExistence = (options, name2) => {
825
+ const checkForExistence$1 = (options, name2) => {
802
826
  if (!name2) {
803
827
  return false;
804
828
  }
@@ -807,15 +831,15 @@ const checkForExistence = (options, name2) => {
807
831
  }
808
832
  return false;
809
833
  };
810
- const setAuthParams = async ({
834
+ const setAuthParams$1 = async ({
811
835
  security,
812
836
  ...options
813
837
  }) => {
814
838
  for (const auth of security) {
815
- if (checkForExistence(options, auth.name)) {
839
+ if (checkForExistence$1(options, auth.name)) {
816
840
  continue;
817
841
  }
818
- const token = await getAuthToken(auth, options.auth);
842
+ const token = await getAuthToken$1(auth, options.auth);
819
843
  if (!token) {
820
844
  continue;
821
845
  }
@@ -837,35 +861,35 @@ const setAuthParams = async ({
837
861
  }
838
862
  }
839
863
  };
840
- const buildUrl = (options) => getUrl({
864
+ const buildUrl$1 = (options) => getUrl$1({
841
865
  baseUrl: options.baseUrl,
842
866
  path: options.path,
843
867
  query: options.query,
844
- querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
868
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer$1(options.querySerializer),
845
869
  url: options.url
846
870
  });
847
- const mergeConfigs = (a, b) => {
871
+ const mergeConfigs$1 = (a, b) => {
848
872
  const config = { ...a, ...b };
849
873
  if (config.baseUrl?.endsWith("/")) {
850
874
  config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
851
875
  }
852
- config.headers = mergeHeaders(a.headers, b.headers);
876
+ config.headers = mergeHeaders$1(a.headers, b.headers);
853
877
  return config;
854
878
  };
855
- const headersEntries = (headers) => {
879
+ const headersEntries$1 = (headers) => {
856
880
  const entries = [];
857
881
  headers.forEach((value, key) => {
858
882
  entries.push([key, value]);
859
883
  });
860
884
  return entries;
861
885
  };
862
- const mergeHeaders = (...headers) => {
886
+ const mergeHeaders$1 = (...headers) => {
863
887
  const mergedHeaders = new Headers();
864
888
  for (const header of headers) {
865
889
  if (!header) {
866
890
  continue;
867
891
  }
868
- const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
892
+ const iterator = header instanceof Headers ? headersEntries$1(header) : Object.entries(header);
869
893
  for (const [key, value] of iterator) {
870
894
  if (value === null) {
871
895
  mergedHeaders.delete(key);
@@ -883,7 +907,7 @@ const mergeHeaders = (...headers) => {
883
907
  }
884
908
  return mergedHeaders;
885
909
  };
886
- class Interceptors {
910
+ let Interceptors$1 = class Interceptors {
887
911
  fns = [];
888
912
  clear() {
889
913
  this.fns = [];
@@ -916,13 +940,13 @@ class Interceptors {
916
940
  this.fns.push(fn);
917
941
  return this.fns.length - 1;
918
942
  }
919
- }
920
- const createInterceptors = () => ({
921
- error: new Interceptors(),
922
- request: new Interceptors(),
923
- response: new Interceptors()
943
+ };
944
+ const createInterceptors$1 = () => ({
945
+ error: new Interceptors$1(),
946
+ request: new Interceptors$1(),
947
+ response: new Interceptors$1()
924
948
  });
925
- const defaultQuerySerializer = createQuerySerializer({
949
+ const defaultQuerySerializer$1 = createQuerySerializer$1({
926
950
  allowReserved: false,
927
951
  array: {
928
952
  explode: true,
@@ -933,34 +957,34 @@ const defaultQuerySerializer = createQuerySerializer({
933
957
  style: "deepObject"
934
958
  }
935
959
  });
936
- const defaultHeaders = {
960
+ const defaultHeaders$1 = {
937
961
  "Content-Type": "application/json"
938
962
  };
939
- const createConfig = (override = {}) => ({
940
- ...jsonBodySerializer,
941
- headers: defaultHeaders,
963
+ const createConfig$1 = (override = {}) => ({
964
+ ...jsonBodySerializer$1,
965
+ headers: defaultHeaders$1,
942
966
  parseAs: "auto",
943
- querySerializer: defaultQuerySerializer,
967
+ querySerializer: defaultQuerySerializer$1,
944
968
  ...override
945
969
  });
946
- const createClient = (config = {}) => {
947
- let _config = mergeConfigs(createConfig(), config);
970
+ const createClient$1 = (config = {}) => {
971
+ let _config = mergeConfigs$1(createConfig$1(), config);
948
972
  const getConfig = () => ({ ..._config });
949
973
  const setConfig = (config2) => {
950
- _config = mergeConfigs(_config, config2);
974
+ _config = mergeConfigs$1(_config, config2);
951
975
  return getConfig();
952
976
  };
953
- const interceptors = createInterceptors();
977
+ const interceptors = createInterceptors$1();
954
978
  const beforeRequest = async (options) => {
955
979
  const opts = {
956
980
  ..._config,
957
981
  ...options,
958
982
  fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
959
- headers: mergeHeaders(_config.headers, options.headers),
983
+ headers: mergeHeaders$1(_config.headers, options.headers),
960
984
  serializedBody: void 0
961
985
  };
962
986
  if (opts.security) {
963
- await setAuthParams({
987
+ await setAuthParams$1({
964
988
  ...opts,
965
989
  security: opts.security
966
990
  });
@@ -974,7 +998,7 @@ const createClient = (config = {}) => {
974
998
  if (opts.body === void 0 || opts.serializedBody === "") {
975
999
  opts.headers.delete("Content-Type");
976
1000
  }
977
- const url = buildUrl(opts);
1001
+ const url = buildUrl$1(opts);
978
1002
  return { opts, url };
979
1003
  };
980
1004
  const request = async (options) => {
@@ -982,7 +1006,7 @@ const createClient = (config = {}) => {
982
1006
  const requestInit = {
983
1007
  redirect: "follow",
984
1008
  ...opts,
985
- body: getValidRequestBody(opts)
1009
+ body: getValidRequestBody$1(opts)
986
1010
  };
987
1011
  let request2 = new Request(url, requestInit);
988
1012
  for (const fn of interceptors.request.fns) {
@@ -1026,7 +1050,7 @@ const createClient = (config = {}) => {
1026
1050
  response
1027
1051
  };
1028
1052
  if (response.ok) {
1029
- const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
1053
+ const parseAs = (opts.parseAs === "auto" ? getParseAs$1(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
1030
1054
  if (response.status === 204 || response.headers.get("Content-Length") === "0") {
1031
1055
  let emptyData;
1032
1056
  switch (parseAs) {
@@ -1104,7 +1128,7 @@ const createClient = (config = {}) => {
1104
1128
  const makeMethodFn = (method) => (options) => request({ ...options, method });
1105
1129
  const makeSseFn = (method) => async (options) => {
1106
1130
  const { opts, url } = await beforeRequest(options);
1107
- return createSseClient({
1131
+ return createSseClient$1({
1108
1132
  ...opts,
1109
1133
  body: opts.body,
1110
1134
  headers: opts.headers,
@@ -1122,7 +1146,7 @@ const createClient = (config = {}) => {
1122
1146
  });
1123
1147
  };
1124
1148
  return {
1125
- buildUrl,
1149
+ buildUrl: buildUrl$1,
1126
1150
  connect: makeMethodFn("CONNECT"),
1127
1151
  delete: makeMethodFn("DELETE"),
1128
1152
  get: makeMethodFn("GET"),
@@ -1149,8 +1173,8 @@ const createClient = (config = {}) => {
1149
1173
  trace: makeMethodFn("TRACE")
1150
1174
  };
1151
1175
  };
1152
- const client = createClient(
1153
- createConfig({ baseUrl: "https://api.themolt.net" })
1176
+ const client = createClient$1(
1177
+ createConfig$1({ baseUrl: "https://api.themolt.net" })
1154
1178
  );
1155
1179
  const startLegreffierOnboarding = (options) => (options.client ?? client).post({
1156
1180
  url: "/public/legreffier/start",
@@ -1161,85 +1185,6 @@ const startLegreffierOnboarding = (options) => (options.client ?? client).post({
1161
1185
  }
1162
1186
  });
1163
1187
  const getLegreffierOnboardingStatus = (options) => (options.client ?? client).get({ url: "/public/legreffier/status/{workflowId}", ...options });
1164
- class MoltNetError extends Error {
1165
- code;
1166
- statusCode;
1167
- detail;
1168
- constructor(message, options) {
1169
- super(message);
1170
- this.name = "MoltNetError";
1171
- this.code = options.code;
1172
- this.statusCode = options.statusCode;
1173
- this.detail = options.detail;
1174
- }
1175
- }
1176
- function problemToError(problem, statusCode) {
1177
- return new MoltNetError(problem.title ?? "Request failed", {
1178
- code: problem.type ?? problem.code ?? "UNKNOWN",
1179
- statusCode,
1180
- detail: problem.detail
1181
- });
1182
- }
1183
- async function writeMcpConfig(mcpConfig, dir2) {
1184
- const targetDir = dir2 ?? process.cwd();
1185
- const filePath = join(targetDir, ".mcp.json");
1186
- let existing = {};
1187
- try {
1188
- const content = await readFile(filePath, "utf-8");
1189
- existing = JSON.parse(content);
1190
- } catch {
1191
- }
1192
- const merged = {
1193
- ...existing,
1194
- mcpServers: {
1195
- ...existing.mcpServers ?? {},
1196
- ...mcpConfig.mcpServers
1197
- }
1198
- };
1199
- await writeFile(filePath, JSON.stringify(merged, null, 2) + "\n");
1200
- return filePath;
1201
- }
1202
- function getConfigDir() {
1203
- return join(homedir(), ".config", "moltnet");
1204
- }
1205
- function getConfigPath(configDir) {
1206
- return join(configDir ?? getConfigDir(), "moltnet.json");
1207
- }
1208
- async function readConfig(configDir) {
1209
- const dir2 = configDir ?? getConfigDir();
1210
- try {
1211
- const content = await readFile(join(dir2, "moltnet.json"), "utf-8");
1212
- return JSON.parse(content);
1213
- } catch {
1214
- }
1215
- try {
1216
- const content = await readFile(join(dir2, "credentials.json"), "utf-8");
1217
- console.warn("Warning: credentials.json is deprecated. New writes use moltnet.json. Support will be removed in 3 minor versions.");
1218
- return JSON.parse(content);
1219
- } catch {
1220
- return null;
1221
- }
1222
- }
1223
- async function writeConfig(config, configDir) {
1224
- const dir2 = configDir ?? getConfigDir();
1225
- await mkdir(dir2, { recursive: true });
1226
- const filePath = join(dir2, "moltnet.json");
1227
- await writeFile(filePath, JSON.stringify(config, null, 2) + "\n", {
1228
- mode: 384
1229
- });
1230
- await chmod(filePath, 384);
1231
- return filePath;
1232
- }
1233
- async function updateConfigSection(section, data, configDir) {
1234
- const config = await readConfig(configDir);
1235
- if (!config) {
1236
- throw new Error("No config found — run `moltnet register` first");
1237
- }
1238
- const existing = config[section] ?? {};
1239
- const updated = { ...existing, ...data };
1240
- Object.assign(config, { [section]: updated });
1241
- await writeConfig(config, configDir);
1242
- }
1243
1188
  /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
1244
1189
  function isBytes$1(a) {
1245
1190
  return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
@@ -3321,193 +3266,901 @@ const wNAF2 = (n) => {
3321
3266
  }
3322
3267
  return { p, f };
3323
3268
  };
3324
- etc.sha512Sync = (...m) => {
3325
- const hash = createHash("sha512");
3326
- m.forEach((msg) => hash.update(msg));
3327
- return hash.digest();
3269
+ var __defProp = Object.defineProperty;
3270
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3271
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
3272
+ const jsonBodySerializer = {
3273
+ bodySerializer: (body) => JSON.stringify(
3274
+ body,
3275
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
3276
+ )
3328
3277
  };
3329
- const DOMAIN_PREFIX = "moltnet:v1";
3330
- function buildSigningBytes(message, nonce) {
3331
- const msgHash = createHash("sha256").update(Buffer.from(message, "utf-8")).digest();
3332
- const nonceBytes = Buffer.from(nonce, "utf-8");
3333
- const prefix = Buffer.from(DOMAIN_PREFIX, "utf-8");
3334
- const buf = Buffer.alloc(
3335
- prefix.length + 4 + msgHash.length + 4 + nonceBytes.length
3336
- );
3337
- let offset = 0;
3338
- prefix.copy(buf, offset);
3339
- offset += prefix.length;
3340
- buf.writeUInt32BE(msgHash.length, offset);
3341
- offset += 4;
3342
- msgHash.copy(buf, offset);
3343
- offset += msgHash.length;
3344
- buf.writeUInt32BE(nonceBytes.length, offset);
3345
- offset += 4;
3346
- nonceBytes.copy(buf, offset);
3347
- return new Uint8Array(buf);
3348
- }
3349
- const cryptoService = {
3350
- /**
3351
- * Generate a new Ed25519 keypair
3352
- */
3353
- async generateKeyPair() {
3354
- const privateKeyBytes = utils.randomPrivateKey();
3355
- const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
3356
- const privateKey = Buffer.from(privateKeyBytes).toString("base64");
3357
- const publicKey = `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
3358
- const fingerprint = this.generateFingerprint(publicKeyBytes);
3359
- return { publicKey, privateKey, fingerprint };
3360
- },
3361
- /**
3362
- * Generate human-readable fingerprint from public key
3363
- * Format: A1B2-C3D4-E5F6-G7H8 (first 16 hex chars of SHA256)
3364
- */
3365
- generateFingerprint(publicKeyBytes) {
3366
- const hash = createHash("sha256").update(publicKeyBytes).digest("hex");
3367
- const segments = hash.slice(0, 16).toUpperCase().match(/.{4}/g) ?? [];
3368
- return segments.join("-");
3369
- },
3370
- /**
3371
- * Parse public key from string format
3372
- */
3373
- parsePublicKey(publicKey) {
3374
- const base64 = publicKey.replace(/^ed25519:/, "");
3375
- return new Uint8Array(Buffer.from(base64, "base64"));
3376
- },
3377
- /**
3378
- * Sign a message with private key
3379
- */
3380
- async sign(message, privateKeyBase64) {
3381
- const privateKeyBytes = new Uint8Array(
3382
- Buffer.from(privateKeyBase64, "base64")
3383
- );
3384
- const messageBytes = new TextEncoder().encode(message);
3385
- const signature = await signAsync(messageBytes, privateKeyBytes);
3386
- return Buffer.from(signature).toString("base64");
3387
- },
3388
- /**
3389
- * Verify a signature against a message and public key
3390
- */
3391
- async verify(message, signature, publicKey) {
3392
- try {
3393
- const publicKeyBytes = this.parsePublicKey(publicKey);
3394
- const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
3395
- const messageBytes = new TextEncoder().encode(message);
3396
- return await verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
3397
- } catch {
3398
- return false;
3399
- }
3400
- },
3401
- /**
3402
- * Sign a (message, nonce) pair using deterministic pre-hash.
3403
- * Uses buildSigningBytes for domain separation and canonical serialization.
3404
- */
3405
- async signWithNonce(message, nonce, privateKeyBase64) {
3406
- const privateKeyBytes = new Uint8Array(
3407
- Buffer.from(privateKeyBase64, "base64")
3408
- );
3409
- const signingBytes = buildSigningBytes(message, nonce);
3410
- const signature = await signAsync(signingBytes, privateKeyBytes);
3411
- return Buffer.from(signature).toString("base64");
3412
- },
3413
- /**
3414
- * Verify a signature produced by signWithNonce.
3415
- */
3416
- async verifyWithNonce(message, nonce, signature, publicKey) {
3417
- try {
3418
- const publicKeyBytes = this.parsePublicKey(publicKey);
3419
- const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
3420
- const signingBytes = buildSigningBytes(message, nonce);
3421
- return await verifyAsync(signatureBytes, signingBytes, publicKeyBytes);
3422
- } catch {
3423
- return false;
3424
- }
3425
- },
3426
- /**
3427
- * Create a signed message object
3428
- */
3429
- async createSignedMessage(message, privateKeyBase64, publicKey) {
3430
- const signature = await this.sign(message, privateKeyBase64);
3431
- return { message, signature, publicKey };
3432
- },
3433
- /**
3434
- * Verify a signed message object
3435
- */
3436
- async verifySignedMessage(signedMessage) {
3437
- return this.verify(
3438
- signedMessage.message,
3439
- signedMessage.signature,
3440
- signedMessage.publicKey
3441
- );
3442
- },
3443
- /**
3444
- * Generate a random challenge for authentication
3445
- */
3446
- generateChallenge() {
3447
- return `moltnet:challenge:${randomBytes$2(32).toString("hex")}:${Date.now()}`;
3448
- },
3449
- /**
3450
- * Derive public key from private key
3451
- */
3452
- async derivePublicKey(privateKeyBase64) {
3453
- const privateKeyBytes = new Uint8Array(
3454
- Buffer.from(privateKeyBase64, "base64")
3278
+ const createSseClient = ({
3279
+ onRequest,
3280
+ onSseError,
3281
+ onSseEvent,
3282
+ responseTransformer,
3283
+ responseValidator,
3284
+ sseDefaultRetryDelay,
3285
+ sseMaxRetryAttempts,
3286
+ sseMaxRetryDelay,
3287
+ sseSleepFn,
3288
+ url,
3289
+ ...options
3290
+ }) => {
3291
+ let lastEventId;
3292
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
3293
+ const createStream = async function* () {
3294
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
3295
+ let attempt = 0;
3296
+ const signal = options.signal ?? new AbortController().signal;
3297
+ while (true) {
3298
+ if (signal.aborted) break;
3299
+ attempt++;
3300
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
3301
+ if (lastEventId !== void 0) {
3302
+ headers.set("Last-Event-ID", lastEventId);
3303
+ }
3304
+ try {
3305
+ const requestInit = {
3306
+ redirect: "follow",
3307
+ ...options,
3308
+ body: options.serializedBody,
3309
+ headers,
3310
+ signal
3311
+ };
3312
+ let request = new Request(url, requestInit);
3313
+ if (onRequest) {
3314
+ request = await onRequest(url, requestInit);
3315
+ }
3316
+ const _fetch = options.fetch ?? globalThis.fetch;
3317
+ const response = await _fetch(request);
3318
+ if (!response.ok)
3319
+ throw new Error(
3320
+ `SSE failed: ${response.status} ${response.statusText}`
3321
+ );
3322
+ if (!response.body) throw new Error("No body in SSE response");
3323
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
3324
+ let buffer = "";
3325
+ const abortHandler = () => {
3326
+ try {
3327
+ reader.cancel();
3328
+ } catch {
3329
+ }
3330
+ };
3331
+ signal.addEventListener("abort", abortHandler);
3332
+ try {
3333
+ while (true) {
3334
+ const { done, value } = await reader.read();
3335
+ if (done) break;
3336
+ buffer += value;
3337
+ buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
3338
+ const chunks = buffer.split("\n\n");
3339
+ buffer = chunks.pop() ?? "";
3340
+ for (const chunk of chunks) {
3341
+ const lines = chunk.split("\n");
3342
+ const dataLines = [];
3343
+ let eventName;
3344
+ for (const line of lines) {
3345
+ if (line.startsWith("data:")) {
3346
+ dataLines.push(line.replace(/^data:\s*/, ""));
3347
+ } else if (line.startsWith("event:")) {
3348
+ eventName = line.replace(/^event:\s*/, "");
3349
+ } else if (line.startsWith("id:")) {
3350
+ lastEventId = line.replace(/^id:\s*/, "");
3351
+ } else if (line.startsWith("retry:")) {
3352
+ const parsed = Number.parseInt(
3353
+ line.replace(/^retry:\s*/, ""),
3354
+ 10
3355
+ );
3356
+ if (!Number.isNaN(parsed)) {
3357
+ retryDelay = parsed;
3358
+ }
3359
+ }
3360
+ }
3361
+ let data;
3362
+ let parsedJson = false;
3363
+ if (dataLines.length) {
3364
+ const rawData = dataLines.join("\n");
3365
+ try {
3366
+ data = JSON.parse(rawData);
3367
+ parsedJson = true;
3368
+ } catch {
3369
+ data = rawData;
3370
+ }
3371
+ }
3372
+ if (parsedJson) {
3373
+ if (responseValidator) {
3374
+ await responseValidator(data);
3375
+ }
3376
+ if (responseTransformer) {
3377
+ data = await responseTransformer(data);
3378
+ }
3379
+ }
3380
+ onSseEvent == null ? void 0 : onSseEvent({
3381
+ data,
3382
+ event: eventName,
3383
+ id: lastEventId,
3384
+ retry: retryDelay
3385
+ });
3386
+ if (dataLines.length) {
3387
+ yield data;
3388
+ }
3389
+ }
3390
+ }
3391
+ } finally {
3392
+ signal.removeEventListener("abort", abortHandler);
3393
+ reader.releaseLock();
3394
+ }
3395
+ break;
3396
+ } catch (error) {
3397
+ onSseError == null ? void 0 : onSseError(error);
3398
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
3399
+ break;
3400
+ }
3401
+ const backoff = Math.min(
3402
+ retryDelay * 2 ** (attempt - 1),
3403
+ sseMaxRetryDelay ?? 3e4
3404
+ );
3405
+ await sleep(backoff);
3406
+ }
3407
+ }
3408
+ };
3409
+ const stream = createStream();
3410
+ return { stream };
3411
+ };
3412
+ const separatorArrayExplode = (style) => {
3413
+ switch (style) {
3414
+ case "label":
3415
+ return ".";
3416
+ case "matrix":
3417
+ return ";";
3418
+ case "simple":
3419
+ return ",";
3420
+ default:
3421
+ return "&";
3422
+ }
3423
+ };
3424
+ const separatorArrayNoExplode = (style) => {
3425
+ switch (style) {
3426
+ case "form":
3427
+ return ",";
3428
+ case "pipeDelimited":
3429
+ return "|";
3430
+ case "spaceDelimited":
3431
+ return "%20";
3432
+ default:
3433
+ return ",";
3434
+ }
3435
+ };
3436
+ const separatorObjectExplode = (style) => {
3437
+ switch (style) {
3438
+ case "label":
3439
+ return ".";
3440
+ case "matrix":
3441
+ return ";";
3442
+ case "simple":
3443
+ return ",";
3444
+ default:
3445
+ return "&";
3446
+ }
3447
+ };
3448
+ const serializeArrayParam = ({
3449
+ allowReserved,
3450
+ explode,
3451
+ name: name2,
3452
+ style,
3453
+ value
3454
+ }) => {
3455
+ if (!explode) {
3456
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
3457
+ switch (style) {
3458
+ case "label":
3459
+ return `.${joinedValues2}`;
3460
+ case "matrix":
3461
+ return `;${name2}=${joinedValues2}`;
3462
+ case "simple":
3463
+ return joinedValues2;
3464
+ default:
3465
+ return `${name2}=${joinedValues2}`;
3466
+ }
3467
+ }
3468
+ const separator = separatorArrayExplode(style);
3469
+ const joinedValues = value.map((v) => {
3470
+ if (style === "label" || style === "simple") {
3471
+ return allowReserved ? v : encodeURIComponent(v);
3472
+ }
3473
+ return serializePrimitiveParam({
3474
+ allowReserved,
3475
+ name: name2,
3476
+ value: v
3477
+ });
3478
+ }).join(separator);
3479
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
3480
+ };
3481
+ const serializePrimitiveParam = ({
3482
+ allowReserved,
3483
+ name: name2,
3484
+ value
3485
+ }) => {
3486
+ if (value === void 0 || value === null) {
3487
+ return "";
3488
+ }
3489
+ if (typeof value === "object") {
3490
+ throw new Error(
3491
+ "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these."
3455
3492
  );
3456
- const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
3457
- return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
3458
- },
3459
- /**
3460
- * Get fingerprint from public key string
3461
- */
3462
- getFingerprintFromPublicKey(publicKey) {
3463
- const publicKeyBytes = this.parsePublicKey(publicKey);
3464
- return this.generateFingerprint(publicKeyBytes);
3465
- },
3466
- /**
3467
- * Derive an X25519 private key from an Ed25519 private key seed.
3468
- * Uses SHA-512 expansion + clamping per RFC 7748 / RFC 8032.
3469
- */
3470
- deriveX25519PrivateKey(ed25519PrivateKeyBase64) {
3471
- const seed = new Uint8Array(Buffer.from(ed25519PrivateKeyBase64, "base64"));
3472
- const x25519Priv = ed25519.utils.toMontgomerySecret(seed);
3473
- return Buffer.from(x25519Priv).toString("base64");
3474
- },
3475
- /**
3476
- * Derive an X25519 public key from an Ed25519 public key.
3477
- * Uses the Edwards → Montgomery birational map.
3478
- */
3479
- deriveX25519PublicKey(ed25519PublicKey) {
3480
- const edPubBytes = this.parsePublicKey(ed25519PublicKey);
3481
- const x25519Pub = ed25519.utils.toMontgomery(edPubBytes);
3482
- return `x25519:${Buffer.from(x25519Pub).toString("base64")}`;
3483
- },
3484
- /**
3485
- * Create a proof of identity ownership (for DCR metadata)
3486
- */
3487
- async createIdentityProof(identityId, privateKeyBase64) {
3488
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3489
- const message = `moltnet:register:${identityId}:${timestamp}`;
3490
- const signature = await this.sign(message, privateKeyBase64);
3491
- return { message, signature, timestamp };
3493
+ }
3494
+ return `${name2}=${allowReserved ? value : encodeURIComponent(value)}`;
3495
+ };
3496
+ const serializeObjectParam = ({
3497
+ allowReserved,
3498
+ explode,
3499
+ name: name2,
3500
+ style,
3501
+ value,
3502
+ valueOnly
3503
+ }) => {
3504
+ if (value instanceof Date) {
3505
+ return valueOnly ? value.toISOString() : `${name2}=${value.toISOString()}`;
3506
+ }
3507
+ if (style !== "deepObject" && !explode) {
3508
+ let values2 = [];
3509
+ Object.entries(value).forEach(([key, v]) => {
3510
+ values2 = [
3511
+ ...values2,
3512
+ key,
3513
+ allowReserved ? v : encodeURIComponent(v)
3514
+ ];
3515
+ });
3516
+ const joinedValues2 = values2.join(",");
3517
+ switch (style) {
3518
+ case "form":
3519
+ return `${name2}=${joinedValues2}`;
3520
+ case "label":
3521
+ return `.${joinedValues2}`;
3522
+ case "matrix":
3523
+ return `;${name2}=${joinedValues2}`;
3524
+ default:
3525
+ return joinedValues2;
3526
+ }
3527
+ }
3528
+ const separator = separatorObjectExplode(style);
3529
+ const joinedValues = Object.entries(value).map(
3530
+ ([key, v]) => serializePrimitiveParam({
3531
+ allowReserved,
3532
+ name: style === "deepObject" ? `${name2}[${key}]` : key,
3533
+ value: v
3534
+ })
3535
+ ).join(separator);
3536
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
3537
+ };
3538
+ const PATH_PARAM_RE = /\{[^{}]+\}/g;
3539
+ const defaultPathSerializer = ({ path, url: _url }) => {
3540
+ let url = _url;
3541
+ const matches = _url.match(PATH_PARAM_RE);
3542
+ if (matches) {
3543
+ for (const match of matches) {
3544
+ let explode = false;
3545
+ let name2 = match.substring(1, match.length - 1);
3546
+ let style = "simple";
3547
+ if (name2.endsWith("*")) {
3548
+ explode = true;
3549
+ name2 = name2.substring(0, name2.length - 1);
3550
+ }
3551
+ if (name2.startsWith(".")) {
3552
+ name2 = name2.substring(1);
3553
+ style = "label";
3554
+ } else if (name2.startsWith(";")) {
3555
+ name2 = name2.substring(1);
3556
+ style = "matrix";
3557
+ }
3558
+ const value = path[name2];
3559
+ if (value === void 0 || value === null) {
3560
+ continue;
3561
+ }
3562
+ if (Array.isArray(value)) {
3563
+ url = url.replace(
3564
+ match,
3565
+ serializeArrayParam({ explode, name: name2, style, value })
3566
+ );
3567
+ continue;
3568
+ }
3569
+ if (typeof value === "object") {
3570
+ url = url.replace(
3571
+ match,
3572
+ serializeObjectParam({
3573
+ explode,
3574
+ name: name2,
3575
+ style,
3576
+ value,
3577
+ valueOnly: true
3578
+ })
3579
+ );
3580
+ continue;
3581
+ }
3582
+ if (style === "matrix") {
3583
+ url = url.replace(
3584
+ match,
3585
+ `;${serializePrimitiveParam({
3586
+ name: name2,
3587
+ value
3588
+ })}`
3589
+ );
3590
+ continue;
3591
+ }
3592
+ const replaceValue = encodeURIComponent(
3593
+ style === "label" ? `.${value}` : value
3594
+ );
3595
+ url = url.replace(match, replaceValue);
3596
+ }
3597
+ }
3598
+ return url;
3599
+ };
3600
+ const getUrl = ({
3601
+ baseUrl,
3602
+ path,
3603
+ query,
3604
+ querySerializer,
3605
+ url: _url
3606
+ }) => {
3607
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
3608
+ let url = (baseUrl ?? "") + pathUrl;
3609
+ if (path) {
3610
+ url = defaultPathSerializer({ path, url });
3611
+ }
3612
+ let search = query ? querySerializer(query) : "";
3613
+ if (search.startsWith("?")) {
3614
+ search = search.substring(1);
3615
+ }
3616
+ if (search) {
3617
+ url += `?${search}`;
3618
+ }
3619
+ return url;
3620
+ };
3621
+ function getValidRequestBody(options) {
3622
+ const hasBody = options.body !== void 0;
3623
+ const isSerializedBody = hasBody && options.bodySerializer;
3624
+ if (isSerializedBody) {
3625
+ if ("serializedBody" in options) {
3626
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
3627
+ return hasSerializedBody ? options.serializedBody : null;
3628
+ }
3629
+ return options.body !== "" ? options.body : null;
3630
+ }
3631
+ if (hasBody) {
3632
+ return options.body;
3633
+ }
3634
+ return void 0;
3635
+ }
3636
+ const getAuthToken = async (auth, callback) => {
3637
+ const token = typeof callback === "function" ? await callback(auth) : callback;
3638
+ if (!token) {
3639
+ return;
3640
+ }
3641
+ if (auth.scheme === "bearer") {
3642
+ return `Bearer ${token}`;
3643
+ }
3644
+ if (auth.scheme === "basic") {
3645
+ return `Basic ${btoa(token)}`;
3646
+ }
3647
+ return token;
3648
+ };
3649
+ const createQuerySerializer = ({
3650
+ parameters = {},
3651
+ ...args
3652
+ } = {}) => {
3653
+ const querySerializer = (queryParams) => {
3654
+ const search = [];
3655
+ if (queryParams && typeof queryParams === "object") {
3656
+ for (const name2 in queryParams) {
3657
+ const value = queryParams[name2];
3658
+ if (value === void 0 || value === null) {
3659
+ continue;
3660
+ }
3661
+ const options = parameters[name2] || args;
3662
+ if (Array.isArray(value)) {
3663
+ const serializedArray = serializeArrayParam({
3664
+ allowReserved: options.allowReserved,
3665
+ explode: true,
3666
+ name: name2,
3667
+ style: "form",
3668
+ value,
3669
+ ...options.array
3670
+ });
3671
+ if (serializedArray) search.push(serializedArray);
3672
+ } else if (typeof value === "object") {
3673
+ const serializedObject = serializeObjectParam({
3674
+ allowReserved: options.allowReserved,
3675
+ explode: true,
3676
+ name: name2,
3677
+ style: "deepObject",
3678
+ value,
3679
+ ...options.object
3680
+ });
3681
+ if (serializedObject) search.push(serializedObject);
3682
+ } else {
3683
+ const serializedPrimitive = serializePrimitiveParam({
3684
+ allowReserved: options.allowReserved,
3685
+ name: name2,
3686
+ value
3687
+ });
3688
+ if (serializedPrimitive) search.push(serializedPrimitive);
3689
+ }
3690
+ }
3691
+ }
3692
+ return search.join("&");
3693
+ };
3694
+ return querySerializer;
3695
+ };
3696
+ const getParseAs = (contentType) => {
3697
+ var _a2;
3698
+ if (!contentType) {
3699
+ return "stream";
3700
+ }
3701
+ const cleanContent = (_a2 = contentType.split(";")[0]) == null ? void 0 : _a2.trim();
3702
+ if (!cleanContent) {
3703
+ return;
3704
+ }
3705
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
3706
+ return "json";
3707
+ }
3708
+ if (cleanContent === "multipart/form-data") {
3709
+ return "formData";
3710
+ }
3711
+ if (["application/", "audio/", "image/", "video/"].some(
3712
+ (type) => cleanContent.startsWith(type)
3713
+ )) {
3714
+ return "blob";
3715
+ }
3716
+ if (cleanContent.startsWith("text/")) {
3717
+ return "text";
3718
+ }
3719
+ return;
3720
+ };
3721
+ const checkForExistence = (options, name2) => {
3722
+ var _a2, _b;
3723
+ if (!name2) {
3724
+ return false;
3725
+ }
3726
+ if (options.headers.has(name2) || ((_a2 = options.query) == null ? void 0 : _a2[name2]) || ((_b = options.headers.get("Cookie")) == null ? void 0 : _b.includes(`${name2}=`))) {
3727
+ return true;
3728
+ }
3729
+ return false;
3730
+ };
3731
+ const setAuthParams = async ({
3732
+ security,
3733
+ ...options
3734
+ }) => {
3735
+ for (const auth of security) {
3736
+ if (checkForExistence(options, auth.name)) {
3737
+ continue;
3738
+ }
3739
+ const token = await getAuthToken(auth, options.auth);
3740
+ if (!token) {
3741
+ continue;
3742
+ }
3743
+ const name2 = auth.name ?? "Authorization";
3744
+ switch (auth.in) {
3745
+ case "query":
3746
+ if (!options.query) {
3747
+ options.query = {};
3748
+ }
3749
+ options.query[name2] = token;
3750
+ break;
3751
+ case "cookie":
3752
+ options.headers.append("Cookie", `${name2}=${token}`);
3753
+ break;
3754
+ case "header":
3755
+ default:
3756
+ options.headers.set(name2, token);
3757
+ break;
3758
+ }
3759
+ }
3760
+ };
3761
+ const buildUrl = (options) => getUrl({
3762
+ baseUrl: options.baseUrl,
3763
+ path: options.path,
3764
+ query: options.query,
3765
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
3766
+ url: options.url
3767
+ });
3768
+ const mergeConfigs = (a, b) => {
3769
+ var _a2;
3770
+ const config = { ...a, ...b };
3771
+ if ((_a2 = config.baseUrl) == null ? void 0 : _a2.endsWith("/")) {
3772
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
3773
+ }
3774
+ config.headers = mergeHeaders(a.headers, b.headers);
3775
+ return config;
3776
+ };
3777
+ const headersEntries = (headers) => {
3778
+ const entries = [];
3779
+ headers.forEach((value, key) => {
3780
+ entries.push([key, value]);
3781
+ });
3782
+ return entries;
3783
+ };
3784
+ const mergeHeaders = (...headers) => {
3785
+ const mergedHeaders = new Headers();
3786
+ for (const header of headers) {
3787
+ if (!header) {
3788
+ continue;
3789
+ }
3790
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
3791
+ for (const [key, value] of iterator) {
3792
+ if (value === null) {
3793
+ mergedHeaders.delete(key);
3794
+ } else if (Array.isArray(value)) {
3795
+ for (const v of value) {
3796
+ mergedHeaders.append(key, v);
3797
+ }
3798
+ } else if (value !== void 0) {
3799
+ mergedHeaders.set(
3800
+ key,
3801
+ typeof value === "object" ? JSON.stringify(value) : value
3802
+ );
3803
+ }
3804
+ }
3805
+ }
3806
+ return mergedHeaders;
3807
+ };
3808
+ class Interceptors2 {
3809
+ constructor() {
3810
+ __publicField(this, "fns", []);
3811
+ }
3812
+ clear() {
3813
+ this.fns = [];
3814
+ }
3815
+ eject(id) {
3816
+ const index = this.getInterceptorIndex(id);
3817
+ if (this.fns[index]) {
3818
+ this.fns[index] = null;
3819
+ }
3820
+ }
3821
+ exists(id) {
3822
+ const index = this.getInterceptorIndex(id);
3823
+ return Boolean(this.fns[index]);
3824
+ }
3825
+ getInterceptorIndex(id) {
3826
+ if (typeof id === "number") {
3827
+ return this.fns[id] ? id : -1;
3828
+ }
3829
+ return this.fns.indexOf(id);
3830
+ }
3831
+ update(id, fn) {
3832
+ const index = this.getInterceptorIndex(id);
3833
+ if (this.fns[index]) {
3834
+ this.fns[index] = fn;
3835
+ return id;
3836
+ }
3837
+ return false;
3838
+ }
3839
+ use(fn) {
3840
+ this.fns.push(fn);
3841
+ return this.fns.length - 1;
3842
+ }
3843
+ }
3844
+ const createInterceptors = () => ({
3845
+ error: new Interceptors2(),
3846
+ request: new Interceptors2(),
3847
+ response: new Interceptors2()
3848
+ });
3849
+ const defaultQuerySerializer = createQuerySerializer({
3850
+ allowReserved: false,
3851
+ array: {
3852
+ explode: true,
3853
+ style: "form"
3492
3854
  },
3493
- /**
3494
- * Verify an identity proof
3495
- */
3496
- async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
3497
- const isValid = await this.verify(
3498
- proof.message,
3499
- proof.signature,
3500
- publicKey
3855
+ object: {
3856
+ explode: true,
3857
+ style: "deepObject"
3858
+ }
3859
+ });
3860
+ const defaultHeaders = {
3861
+ "Content-Type": "application/json"
3862
+ };
3863
+ const createConfig = (override = {}) => ({
3864
+ ...jsonBodySerializer,
3865
+ headers: defaultHeaders,
3866
+ parseAs: "auto",
3867
+ querySerializer: defaultQuerySerializer,
3868
+ ...override
3869
+ });
3870
+ const createClient = (config = {}) => {
3871
+ let _config = mergeConfigs(createConfig(), config);
3872
+ const getConfig = () => ({ ..._config });
3873
+ const setConfig = (config2) => {
3874
+ _config = mergeConfigs(_config, config2);
3875
+ return getConfig();
3876
+ };
3877
+ const interceptors = createInterceptors();
3878
+ const beforeRequest = async (options) => {
3879
+ const opts = {
3880
+ ..._config,
3881
+ ...options,
3882
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
3883
+ headers: mergeHeaders(_config.headers, options.headers),
3884
+ serializedBody: void 0
3885
+ };
3886
+ if (opts.security) {
3887
+ await setAuthParams({
3888
+ ...opts,
3889
+ security: opts.security
3890
+ });
3891
+ }
3892
+ if (opts.requestValidator) {
3893
+ await opts.requestValidator(opts);
3894
+ }
3895
+ if (opts.body !== void 0 && opts.bodySerializer) {
3896
+ opts.serializedBody = opts.bodySerializer(opts.body);
3897
+ }
3898
+ if (opts.body === void 0 || opts.serializedBody === "") {
3899
+ opts.headers.delete("Content-Type");
3900
+ }
3901
+ const url = buildUrl(opts);
3902
+ return { opts, url };
3903
+ };
3904
+ const request = async (options) => {
3905
+ const { opts, url } = await beforeRequest(options);
3906
+ const requestInit = {
3907
+ redirect: "follow",
3908
+ ...opts,
3909
+ body: getValidRequestBody(opts)
3910
+ };
3911
+ let request2 = new Request(url, requestInit);
3912
+ for (const fn of interceptors.request.fns) {
3913
+ if (fn) {
3914
+ request2 = await fn(request2, opts);
3915
+ }
3916
+ }
3917
+ const _fetch = opts.fetch;
3918
+ let response;
3919
+ try {
3920
+ response = await _fetch(request2);
3921
+ } catch (error2) {
3922
+ let finalError2 = error2;
3923
+ for (const fn of interceptors.error.fns) {
3924
+ if (fn) {
3925
+ finalError2 = await fn(
3926
+ error2,
3927
+ void 0,
3928
+ request2,
3929
+ opts
3930
+ );
3931
+ }
3932
+ }
3933
+ finalError2 = finalError2 || {};
3934
+ if (opts.throwOnError) {
3935
+ throw finalError2;
3936
+ }
3937
+ return opts.responseStyle === "data" ? void 0 : {
3938
+ error: finalError2,
3939
+ request: request2,
3940
+ response: void 0
3941
+ };
3942
+ }
3943
+ for (const fn of interceptors.response.fns) {
3944
+ if (fn) {
3945
+ response = await fn(response, request2, opts);
3946
+ }
3947
+ }
3948
+ const result = {
3949
+ request: request2,
3950
+ response
3951
+ };
3952
+ if (response.ok) {
3953
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
3954
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
3955
+ let emptyData;
3956
+ switch (parseAs) {
3957
+ case "arrayBuffer":
3958
+ case "blob":
3959
+ case "text":
3960
+ emptyData = await response[parseAs]();
3961
+ break;
3962
+ case "formData":
3963
+ emptyData = new FormData();
3964
+ break;
3965
+ case "stream":
3966
+ emptyData = response.body;
3967
+ break;
3968
+ case "json":
3969
+ default:
3970
+ emptyData = {};
3971
+ break;
3972
+ }
3973
+ return opts.responseStyle === "data" ? emptyData : {
3974
+ data: emptyData,
3975
+ ...result
3976
+ };
3977
+ }
3978
+ let data;
3979
+ switch (parseAs) {
3980
+ case "arrayBuffer":
3981
+ case "blob":
3982
+ case "formData":
3983
+ case "json":
3984
+ case "text":
3985
+ data = await response[parseAs]();
3986
+ break;
3987
+ case "stream":
3988
+ return opts.responseStyle === "data" ? response.body : {
3989
+ data: response.body,
3990
+ ...result
3991
+ };
3992
+ }
3993
+ if (parseAs === "json") {
3994
+ if (opts.responseValidator) {
3995
+ await opts.responseValidator(data);
3996
+ }
3997
+ if (opts.responseTransformer) {
3998
+ data = await opts.responseTransformer(data);
3999
+ }
4000
+ }
4001
+ return opts.responseStyle === "data" ? data : {
4002
+ data,
4003
+ ...result
4004
+ };
4005
+ }
4006
+ const textError = await response.text();
4007
+ let jsonError;
4008
+ try {
4009
+ jsonError = JSON.parse(textError);
4010
+ } catch {
4011
+ }
4012
+ const error = jsonError ?? textError;
4013
+ let finalError = error;
4014
+ for (const fn of interceptors.error.fns) {
4015
+ if (fn) {
4016
+ finalError = await fn(error, response, request2, opts);
4017
+ }
4018
+ }
4019
+ finalError = finalError || {};
4020
+ if (opts.throwOnError) {
4021
+ throw finalError;
4022
+ }
4023
+ return opts.responseStyle === "data" ? void 0 : {
4024
+ error: finalError,
4025
+ ...result
4026
+ };
4027
+ };
4028
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
4029
+ const makeSseFn = (method) => async (options) => {
4030
+ const { opts, url } = await beforeRequest(options);
4031
+ return createSseClient({
4032
+ ...opts,
4033
+ body: opts.body,
4034
+ headers: opts.headers,
4035
+ method,
4036
+ onRequest: async (url2, init) => {
4037
+ let request2 = new Request(url2, init);
4038
+ for (const fn of interceptors.request.fns) {
4039
+ if (fn) {
4040
+ request2 = await fn(request2, opts);
4041
+ }
4042
+ }
4043
+ return request2;
4044
+ },
4045
+ url
4046
+ });
4047
+ };
4048
+ return {
4049
+ buildUrl,
4050
+ connect: makeMethodFn("CONNECT"),
4051
+ delete: makeMethodFn("DELETE"),
4052
+ get: makeMethodFn("GET"),
4053
+ getConfig,
4054
+ head: makeMethodFn("HEAD"),
4055
+ interceptors,
4056
+ options: makeMethodFn("OPTIONS"),
4057
+ patch: makeMethodFn("PATCH"),
4058
+ post: makeMethodFn("POST"),
4059
+ put: makeMethodFn("PUT"),
4060
+ request,
4061
+ setConfig,
4062
+ sse: {
4063
+ connect: makeSseFn("CONNECT"),
4064
+ delete: makeSseFn("DELETE"),
4065
+ get: makeSseFn("GET"),
4066
+ head: makeSseFn("HEAD"),
4067
+ options: makeSseFn("OPTIONS"),
4068
+ patch: makeSseFn("PATCH"),
4069
+ post: makeSseFn("POST"),
4070
+ put: makeSseFn("PUT"),
4071
+ trace: makeSseFn("TRACE")
4072
+ },
4073
+ trace: makeMethodFn("TRACE")
4074
+ };
4075
+ };
4076
+ createClient(
4077
+ createConfig({ baseUrl: "https://api.themolt.net" })
4078
+ );
4079
+ class MoltNetError extends Error {
4080
+ constructor(message, options) {
4081
+ super(message);
4082
+ __publicField(this, "code");
4083
+ __publicField(this, "statusCode");
4084
+ __publicField(this, "detail");
4085
+ this.name = "MoltNetError";
4086
+ this.code = options.code;
4087
+ this.statusCode = options.statusCode;
4088
+ this.detail = options.detail;
4089
+ }
4090
+ }
4091
+ function problemToError(problem, statusCode) {
4092
+ return new MoltNetError(problem.title ?? "Request failed", {
4093
+ code: problem.type ?? problem.code ?? "UNKNOWN",
4094
+ statusCode,
4095
+ detail: problem.detail
4096
+ });
4097
+ }
4098
+ async function writeMcpConfig(mcpConfig, dir2) {
4099
+ const targetDir = dir2 ?? process.cwd();
4100
+ const filePath = join(targetDir, ".mcp.json");
4101
+ let existing = {};
4102
+ try {
4103
+ const content = await readFile(filePath, "utf-8");
4104
+ existing = JSON.parse(content);
4105
+ } catch {
4106
+ }
4107
+ const merged = {
4108
+ ...existing,
4109
+ mcpServers: {
4110
+ ...existing.mcpServers ?? {},
4111
+ ...mcpConfig.mcpServers
4112
+ }
4113
+ };
4114
+ await writeFile(filePath, JSON.stringify(merged, null, 2) + "\n");
4115
+ return filePath;
4116
+ }
4117
+ function getConfigDir() {
4118
+ return join(homedir(), ".config", "moltnet");
4119
+ }
4120
+ function getConfigPath(configDir) {
4121
+ return join(configDir ?? getConfigDir(), "moltnet.json");
4122
+ }
4123
+ async function readConfig(configDir) {
4124
+ const dir2 = configDir ?? getConfigDir();
4125
+ try {
4126
+ const content = await readFile(join(dir2, "moltnet.json"), "utf-8");
4127
+ return JSON.parse(content);
4128
+ } catch {
4129
+ }
4130
+ try {
4131
+ const content = await readFile(join(dir2, "credentials.json"), "utf-8");
4132
+ console.warn(
4133
+ "Warning: credentials.json is deprecated. New writes use moltnet.json. Support will be removed in 3 minor versions."
3501
4134
  );
3502
- if (!isValid) return false;
3503
- const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
3504
- if (!proof.message.startsWith(expectedPrefix)) return false;
3505
- const proofTime = new Date(proof.timestamp).getTime();
3506
- const now = Date.now();
3507
- const fiveMinutes = 5 * 60 * 1e3;
3508
- if (now - proofTime > fiveMinutes) return false;
3509
- return true;
4135
+ return JSON.parse(content);
4136
+ } catch {
4137
+ return null;
4138
+ }
4139
+ }
4140
+ async function writeConfig(config, configDir) {
4141
+ const dir2 = configDir ?? getConfigDir();
4142
+ await mkdir(dir2, { recursive: true });
4143
+ const filePath = join(dir2, "moltnet.json");
4144
+ await writeFile(filePath, JSON.stringify(config, null, 2) + "\n", {
4145
+ mode: 384
4146
+ });
4147
+ await chmod(filePath, 384);
4148
+ return filePath;
4149
+ }
4150
+ async function updateConfigSection(section, data, configDir) {
4151
+ const config = await readConfig(configDir);
4152
+ if (!config) {
4153
+ throw new Error("No config found — run `moltnet register` first");
3510
4154
  }
4155
+ const existing = config[section] ?? {};
4156
+ const updated = { ...existing, ...data };
4157
+ Object.assign(config, { [section]: updated });
4158
+ await writeConfig(config, configDir);
4159
+ }
4160
+ etc.sha512Sync = (...m) => {
4161
+ const hash = createHash("sha512");
4162
+ m.forEach((msg) => hash.update(msg));
4163
+ return hash.digest();
3511
4164
  };
3512
4165
  if (!etc.sha512Sync) {
3513
4166
  etc.sha512Sync = (...m) => {
@@ -3613,19 +4266,25 @@ function toSSHPrivateKey(seedBase64) {
3613
4266
  ].join("\n");
3614
4267
  }
3615
4268
  async function exportSSHKey(opts) {
3616
- const config = await readConfig(opts?.configDir);
4269
+ const config = await readConfig(opts == null ? void 0 : opts.configDir);
3617
4270
  if (!config) {
3618
- throw new Error(`No config found at ${getConfigPath(opts?.configDir)} — run \`moltnet register\` first`);
4271
+ throw new Error(
4272
+ `No config found at ${getConfigPath(opts == null ? void 0 : opts.configDir)} — run \`moltnet register\` first`
4273
+ );
3619
4274
  }
3620
4275
  const privateKeySSH = toSSHPrivateKey(config.keys.private_key);
3621
4276
  const publicKeySSH = toSSHPublicKey(config.keys.public_key);
3622
- const outputDir = opts?.outputDir ?? join(opts?.configDir ?? getConfigDir(), "ssh");
4277
+ const outputDir = (opts == null ? void 0 : opts.outputDir) ?? join((opts == null ? void 0 : opts.configDir) ?? getConfigDir(), "ssh");
3623
4278
  await mkdir(outputDir, { recursive: true });
3624
4279
  const privatePath = join(outputDir, "id_ed25519");
3625
4280
  const publicPath = join(outputDir, "id_ed25519.pub");
3626
4281
  await writeFile(privatePath, privateKeySSH, { mode: 384 });
3627
4282
  await writeFile(publicPath, publicKeySSH, { mode: 420 });
3628
- await updateConfigSection("ssh", { private_key_path: privatePath, public_key_path: publicPath }, opts?.configDir);
4283
+ await updateConfigSection(
4284
+ "ssh",
4285
+ { private_key_path: privatePath, public_key_path: publicPath },
4286
+ opts == null ? void 0 : opts.configDir
4287
+ );
3629
4288
  return { privatePath, publicPath };
3630
4289
  }
3631
4290
  const POLL_INTERVAL_MS = 5e3;
@@ -3643,7 +4302,7 @@ function toErrorMessage(err2) {
3643
4302
  }
3644
4303
  const POLL_TIMEOUT_MS = 5 * 60 * 1e3;
3645
4304
  function makeClient(baseUrl) {
3646
- return createClient({ baseUrl });
4305
+ return createClient$1({ baseUrl });
3647
4306
  }
3648
4307
  async function startOnboarding(baseUrl, body) {
3649
4308
  const client2 = makeClient(baseUrl);
@@ -3729,6 +4388,24 @@ async function downloadSkills(repoDir, skillDir) {
3729
4388
  await writeFile(join(destDir, "SKILL.md"), content, "utf-8");
3730
4389
  }
3731
4390
  }
4391
+ function buildGhTokenRule(agentName) {
4392
+ return [
4393
+ "# GitHub CLI Authentication (LeGreffier)",
4394
+ "",
4395
+ `When \`GIT_CONFIG_GLOBAL\` is set to \`.moltnet/${agentName}/gitconfig\`,`,
4396
+ "authenticate all `gh` CLI commands as the GitHub App by prefixing them with:",
4397
+ "",
4398
+ "```bash",
4399
+ 'GH_TOKEN=$(moltnet github token --credentials "$(dirname "$GIT_CONFIG_GLOBAL")/moltnet.json") gh <command>',
4400
+ "```",
4401
+ "",
4402
+ "This ensures `gh pr create`, `gh issue create`, etc. use the",
4403
+ "GitHub App's identity instead of the user's personal token.",
4404
+ "",
4405
+ "The token is short-lived (~1 hour) and generated on each invocation.",
4406
+ ""
4407
+ ].join("\n");
4408
+ }
3732
4409
  function buildPermissions(agentName) {
3733
4410
  return [
3734
4411
  // Read-only git commands used by session activation & commit workflow
@@ -3737,8 +4414,12 @@ function buildPermissions(agentName) {
3737
4414
  "Bash(git log *)",
3738
4415
  "Bash(git rev-parse *)",
3739
4416
  "Bash(git worktree list)",
3740
- // Signing CLI
4417
+ // Signing CLI (native binary)
3741
4418
  "Bash(moltnet sign *)",
4419
+ "Bash(moltnet github token *)",
4420
+ // Signing CLI (npm package — equivalent commands)
4421
+ "Bash(npx @themoltnet/cli sign *)",
4422
+ "Bash(npx @themoltnet/cli github token *)",
3742
4423
  // Worktree symlink creation
3743
4424
  "Bash(ln -s *)",
3744
4425
  // All MCP tools for this agent's server
@@ -3827,6 +4508,15 @@ class ClaudeAdapter {
3827
4508
  clientSecret: opts.clientSecret
3828
4509
  });
3829
4510
  }
4511
+ async writeRules(opts) {
4512
+ const dir2 = join(opts.repoDir, ".claude", "rules");
4513
+ await mkdir(dir2, { recursive: true });
4514
+ await writeFile(
4515
+ join(dir2, "legreffier-gh.md"),
4516
+ buildGhTokenRule(opts.agentName),
4517
+ "utf-8"
4518
+ );
4519
+ }
3830
4520
  }
3831
4521
  class CodexAdapter {
3832
4522
  type = "codex";
@@ -3871,6 +4561,15 @@ class CodexAdapter {
3871
4561
  ];
3872
4562
  await writeFile(join(envDir, "env"), lines.join("\n") + "\n", "utf-8");
3873
4563
  }
4564
+ async writeRules(opts) {
4565
+ const dir2 = join(opts.repoDir, ".codex", "rules");
4566
+ await mkdir(dir2, { recursive: true });
4567
+ await writeFile(
4568
+ join(dir2, "legreffier-gh.md"),
4569
+ buildGhTokenRule(opts.agentName),
4570
+ "utf-8"
4571
+ );
4572
+ }
3874
4573
  }
3875
4574
  const adapters = {
3876
4575
  claude: new ClaudeAdapter(),
@@ -4186,6 +4885,201 @@ async function runGitSetupPhase(opts) {
4186
4885
  );
4187
4886
  dispatch({ type: "step", key: "gitSetup", status: "done" });
4188
4887
  }
4888
+ etc.sha512Sync = (...m) => {
4889
+ const hash = createHash("sha512");
4890
+ m.forEach((msg) => hash.update(msg));
4891
+ return hash.digest();
4892
+ };
4893
+ const DOMAIN_PREFIX = "moltnet:v1";
4894
+ function buildSigningBytes(message, nonce) {
4895
+ const msgHash = createHash("sha256").update(Buffer.from(message, "utf-8")).digest();
4896
+ const nonceBytes = Buffer.from(nonce, "utf-8");
4897
+ const prefix = Buffer.from(DOMAIN_PREFIX, "utf-8");
4898
+ const buf = Buffer.alloc(
4899
+ prefix.length + 4 + msgHash.length + 4 + nonceBytes.length
4900
+ );
4901
+ let offset = 0;
4902
+ prefix.copy(buf, offset);
4903
+ offset += prefix.length;
4904
+ buf.writeUInt32BE(msgHash.length, offset);
4905
+ offset += 4;
4906
+ msgHash.copy(buf, offset);
4907
+ offset += msgHash.length;
4908
+ buf.writeUInt32BE(nonceBytes.length, offset);
4909
+ offset += 4;
4910
+ nonceBytes.copy(buf, offset);
4911
+ return new Uint8Array(buf);
4912
+ }
4913
+ const cryptoService = {
4914
+ /**
4915
+ * Generate a new Ed25519 keypair
4916
+ */
4917
+ async generateKeyPair() {
4918
+ const privateKeyBytes = utils.randomPrivateKey();
4919
+ const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
4920
+ const privateKey = Buffer.from(privateKeyBytes).toString("base64");
4921
+ const publicKey = `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
4922
+ const fingerprint = this.generateFingerprint(publicKeyBytes);
4923
+ return { publicKey, privateKey, fingerprint };
4924
+ },
4925
+ /**
4926
+ * Generate human-readable fingerprint from public key
4927
+ * Format: A1B2-C3D4-E5F6-G7H8 (first 16 hex chars of SHA256)
4928
+ */
4929
+ generateFingerprint(publicKeyBytes) {
4930
+ const hash = createHash("sha256").update(publicKeyBytes).digest("hex");
4931
+ const segments = hash.slice(0, 16).toUpperCase().match(/.{4}/g) ?? [];
4932
+ return segments.join("-");
4933
+ },
4934
+ /**
4935
+ * Parse public key from string format
4936
+ */
4937
+ parsePublicKey(publicKey) {
4938
+ const base64 = publicKey.replace(/^ed25519:/, "");
4939
+ return new Uint8Array(Buffer.from(base64, "base64"));
4940
+ },
4941
+ /**
4942
+ * Sign a message with private key
4943
+ */
4944
+ async sign(message, privateKeyBase64) {
4945
+ const privateKeyBytes = new Uint8Array(
4946
+ Buffer.from(privateKeyBase64, "base64")
4947
+ );
4948
+ const messageBytes = new TextEncoder().encode(message);
4949
+ const signature = await signAsync(messageBytes, privateKeyBytes);
4950
+ return Buffer.from(signature).toString("base64");
4951
+ },
4952
+ /**
4953
+ * Verify a signature against a message and public key
4954
+ */
4955
+ async verify(message, signature, publicKey) {
4956
+ try {
4957
+ const publicKeyBytes = this.parsePublicKey(publicKey);
4958
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
4959
+ const messageBytes = new TextEncoder().encode(message);
4960
+ return await verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
4961
+ } catch {
4962
+ return false;
4963
+ }
4964
+ },
4965
+ /**
4966
+ * Sign a (message, nonce) pair using deterministic pre-hash.
4967
+ * Uses buildSigningBytes for domain separation and canonical serialization.
4968
+ */
4969
+ async signWithNonce(message, nonce, privateKeyBase64) {
4970
+ const privateKeyBytes = new Uint8Array(
4971
+ Buffer.from(privateKeyBase64, "base64")
4972
+ );
4973
+ const signingBytes = buildSigningBytes(message, nonce);
4974
+ const signature = await signAsync(signingBytes, privateKeyBytes);
4975
+ return Buffer.from(signature).toString("base64");
4976
+ },
4977
+ /**
4978
+ * Verify a signature produced by signWithNonce.
4979
+ */
4980
+ async verifyWithNonce(message, nonce, signature, publicKey) {
4981
+ try {
4982
+ const publicKeyBytes = this.parsePublicKey(publicKey);
4983
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
4984
+ const signingBytes = buildSigningBytes(message, nonce);
4985
+ return await verifyAsync(signatureBytes, signingBytes, publicKeyBytes);
4986
+ } catch {
4987
+ return false;
4988
+ }
4989
+ },
4990
+ /**
4991
+ * Create a signed message object
4992
+ */
4993
+ async createSignedMessage(message, privateKeyBase64, publicKey) {
4994
+ const signature = await this.sign(message, privateKeyBase64);
4995
+ return { message, signature, publicKey };
4996
+ },
4997
+ /**
4998
+ * Verify a signed message object
4999
+ */
5000
+ async verifySignedMessage(signedMessage) {
5001
+ return this.verify(
5002
+ signedMessage.message,
5003
+ signedMessage.signature,
5004
+ signedMessage.publicKey
5005
+ );
5006
+ },
5007
+ /**
5008
+ * Generate a random challenge for authentication
5009
+ */
5010
+ generateChallenge() {
5011
+ return `moltnet:challenge:${randomBytes$2(32).toString("hex")}:${Date.now()}`;
5012
+ },
5013
+ /**
5014
+ * Derive public key from private key
5015
+ */
5016
+ async derivePublicKey(privateKeyBase64) {
5017
+ const privateKeyBytes = new Uint8Array(
5018
+ Buffer.from(privateKeyBase64, "base64")
5019
+ );
5020
+ const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
5021
+ return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
5022
+ },
5023
+ /**
5024
+ * Get fingerprint from public key string
5025
+ */
5026
+ getFingerprintFromPublicKey(publicKey) {
5027
+ const publicKeyBytes = this.parsePublicKey(publicKey);
5028
+ return this.generateFingerprint(publicKeyBytes);
5029
+ },
5030
+ /**
5031
+ * Derive an X25519 private key from an Ed25519 private key seed.
5032
+ * Uses SHA-512 expansion + clamping per RFC 7748 / RFC 8032.
5033
+ */
5034
+ deriveX25519PrivateKey(ed25519PrivateKeyBase64) {
5035
+ const seed = new Uint8Array(Buffer.from(ed25519PrivateKeyBase64, "base64"));
5036
+ const x25519Priv = ed25519.utils.toMontgomerySecret(seed);
5037
+ return Buffer.from(x25519Priv).toString("base64");
5038
+ },
5039
+ /**
5040
+ * Derive an X25519 public key from an Ed25519 public key.
5041
+ * Uses the Edwards → Montgomery birational map.
5042
+ */
5043
+ deriveX25519PublicKey(ed25519PublicKey) {
5044
+ const edPubBytes = this.parsePublicKey(ed25519PublicKey);
5045
+ const x25519Pub = ed25519.utils.toMontgomery(edPubBytes);
5046
+ return `x25519:${Buffer.from(x25519Pub).toString("base64")}`;
5047
+ },
5048
+ /**
5049
+ * Create a proof of identity ownership (for DCR metadata)
5050
+ */
5051
+ async createIdentityProof(identityId, privateKeyBase64) {
5052
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
5053
+ const message = `moltnet:register:${identityId}:${timestamp}`;
5054
+ const signature = await this.sign(message, privateKeyBase64);
5055
+ return { message, signature, timestamp };
5056
+ },
5057
+ /**
5058
+ * Verify an identity proof
5059
+ */
5060
+ async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
5061
+ const isValid = await this.verify(
5062
+ proof.message,
5063
+ proof.signature,
5064
+ publicKey
5065
+ );
5066
+ if (!isValid) return false;
5067
+ const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
5068
+ if (!proof.message.startsWith(expectedPrefix)) return false;
5069
+ const proofTime = new Date(proof.timestamp).getTime();
5070
+ const now = Date.now();
5071
+ const fiveMinutes = 5 * 60 * 1e3;
5072
+ if (now - proofTime > fiveMinutes) return false;
5073
+ return true;
5074
+ }
5075
+ };
5076
+ if (!etc.sha512Sync) {
5077
+ etc.sha512Sync = (...m) => {
5078
+ const hash = createHash("sha512");
5079
+ m.forEach((msg) => hash.update(msg));
5080
+ return hash.digest();
5081
+ };
5082
+ }
4189
5083
  async function runIdentityPhase(opts) {
4190
5084
  const { apiUrl: apiUrl2, agentName, configDir, dispatch } = opts;
4191
5085
  const existingConfig = await readConfig(configDir);
@@ -4807,6 +5701,8 @@ function SetupApp({
4807
5701
  written.push(`${agentType}: skills`);
4808
5702
  await adapter.writeSettings(opts);
4809
5703
  written.push(`${agentType}: settings`);
5704
+ await adapter.writeRules(opts);
5705
+ written.push(`${agentType}: gh token rule`);
4810
5706
  }
4811
5707
  setFilesWritten(written);
4812
5708
  setSummary({
@@ -4893,6 +5789,19 @@ const name = values["name"];
4893
5789
  const agentFlags = values["agent"] ?? [];
4894
5790
  const apiUrl = values["api-url"] ?? process.env.MOLTNET_API_URL ?? "https://api.themolt.net";
4895
5791
  const dir = values["dir"] ?? process.cwd();
5792
+ if (subcommand === "github" && positionals[1] === "token") {
5793
+ try {
5794
+ const agentName = resolveAgentName(name, process.env.GIT_CONFIG_GLOBAL);
5795
+ printGitHubToken(agentName, dir);
5796
+ process.exit(0);
5797
+ } catch (err2) {
5798
+ process.stderr.write(
5799
+ `Error: ${err2 instanceof Error ? err2.message : String(err2)}
5800
+ `
5801
+ );
5802
+ process.exit(1);
5803
+ }
5804
+ }
4896
5805
  if (!name) {
4897
5806
  const usage = subcommand === "setup" ? "Usage: legreffier setup --name <agent-name> [--agent claude] [--agent codex] [--dir <path>]" : "Usage: legreffier [init] --name <agent-name> [--agent claude] [--agent codex] [--api-url <url>] [--dir <path>]";
4898
5807
  process.stderr.write(usage + "\n");