@themoltnet/legreffier 0.1.0 → 0.2.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 (3) hide show
  1. package/README.md +16 -17
  2. package/dist/index.js +1225 -384
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -2,13 +2,12 @@
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 { join, basename } from "node:path";
5
+ import { join } from "node:path";
6
6
  import { useState, useEffect, useReducer, useRef } from "react";
7
7
  import figlet from "figlet";
8
- import { readFile, writeFile, mkdir, chmod, rm } from "node:fs/promises";
8
+ import { readFile, mkdir, writeFile, chmod, rm } from "node:fs/promises";
9
9
  import { homedir } from "node:os";
10
- import { randomBytes as randomBytes$1, createHash } from "crypto";
11
- import { execSync } from "node:child_process";
10
+ import { createHash, randomBytes as randomBytes$1 } from "crypto";
12
11
  import open from "open";
13
12
  const colors = {
14
13
  // Primary — teal/cyan (The Network)
@@ -350,13 +349,13 @@ function CliSummaryBox({
350
349
  }
351
350
  ) });
352
351
  }
353
- const jsonBodySerializer = {
352
+ const jsonBodySerializer$1 = {
354
353
  bodySerializer: (body) => JSON.stringify(
355
354
  body,
356
355
  (_key, value) => typeof value === "bigint" ? value.toString() : value
357
356
  )
358
357
  };
359
- const createSseClient = ({
358
+ const createSseClient$1 = ({
360
359
  onRequest,
361
360
  onSseError,
362
361
  onSseEvent,
@@ -490,7 +489,7 @@ const createSseClient = ({
490
489
  const stream = createStream();
491
490
  return { stream };
492
491
  };
493
- const separatorArrayExplode = (style) => {
492
+ const separatorArrayExplode$1 = (style) => {
494
493
  switch (style) {
495
494
  case "label":
496
495
  return ".";
@@ -502,7 +501,7 @@ const separatorArrayExplode = (style) => {
502
501
  return "&";
503
502
  }
504
503
  };
505
- const separatorArrayNoExplode = (style) => {
504
+ const separatorArrayNoExplode$1 = (style) => {
506
505
  switch (style) {
507
506
  case "form":
508
507
  return ",";
@@ -514,7 +513,7 @@ const separatorArrayNoExplode = (style) => {
514
513
  return ",";
515
514
  }
516
515
  };
517
- const separatorObjectExplode = (style) => {
516
+ const separatorObjectExplode$1 = (style) => {
518
517
  switch (style) {
519
518
  case "label":
520
519
  return ".";
@@ -526,7 +525,7 @@ const separatorObjectExplode = (style) => {
526
525
  return "&";
527
526
  }
528
527
  };
529
- const serializeArrayParam = ({
528
+ const serializeArrayParam$1 = ({
530
529
  allowReserved,
531
530
  explode,
532
531
  name: name2,
@@ -534,7 +533,7 @@ const serializeArrayParam = ({
534
533
  value
535
534
  }) => {
536
535
  if (!explode) {
537
- const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
536
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode$1(style));
538
537
  switch (style) {
539
538
  case "label":
540
539
  return `.${joinedValues2}`;
@@ -546,12 +545,12 @@ const serializeArrayParam = ({
546
545
  return `${name2}=${joinedValues2}`;
547
546
  }
548
547
  }
549
- const separator = separatorArrayExplode(style);
548
+ const separator = separatorArrayExplode$1(style);
550
549
  const joinedValues = value.map((v) => {
551
550
  if (style === "label" || style === "simple") {
552
551
  return allowReserved ? v : encodeURIComponent(v);
553
552
  }
554
- return serializePrimitiveParam({
553
+ return serializePrimitiveParam$1({
555
554
  allowReserved,
556
555
  name: name2,
557
556
  value: v
@@ -559,7 +558,7 @@ const serializeArrayParam = ({
559
558
  }).join(separator);
560
559
  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
561
560
  };
562
- const serializePrimitiveParam = ({
561
+ const serializePrimitiveParam$1 = ({
563
562
  allowReserved,
564
563
  name: name2,
565
564
  value
@@ -574,7 +573,7 @@ const serializePrimitiveParam = ({
574
573
  }
575
574
  return `${name2}=${allowReserved ? value : encodeURIComponent(value)}`;
576
575
  };
577
- const serializeObjectParam = ({
576
+ const serializeObjectParam$1 = ({
578
577
  allowReserved,
579
578
  explode,
580
579
  name: name2,
@@ -606,9 +605,9 @@ const serializeObjectParam = ({
606
605
  return joinedValues2;
607
606
  }
608
607
  }
609
- const separator = separatorObjectExplode(style);
608
+ const separator = separatorObjectExplode$1(style);
610
609
  const joinedValues = Object.entries(value).map(
611
- ([key, v]) => serializePrimitiveParam({
610
+ ([key, v]) => serializePrimitiveParam$1({
612
611
  allowReserved,
613
612
  name: style === "deepObject" ? `${name2}[${key}]` : key,
614
613
  value: v
@@ -616,10 +615,10 @@ const serializeObjectParam = ({
616
615
  ).join(separator);
617
616
  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
618
617
  };
619
- const PATH_PARAM_RE = /\{[^{}]+\}/g;
620
- const defaultPathSerializer = ({ path, url: _url }) => {
618
+ const PATH_PARAM_RE$1 = /\{[^{}]+\}/g;
619
+ const defaultPathSerializer$1 = ({ path, url: _url }) => {
621
620
  let url = _url;
622
- const matches = _url.match(PATH_PARAM_RE);
621
+ const matches = _url.match(PATH_PARAM_RE$1);
623
622
  if (matches) {
624
623
  for (const match of matches) {
625
624
  let explode = false;
@@ -643,14 +642,14 @@ const defaultPathSerializer = ({ path, url: _url }) => {
643
642
  if (Array.isArray(value)) {
644
643
  url = url.replace(
645
644
  match,
646
- serializeArrayParam({ explode, name: name2, style, value })
645
+ serializeArrayParam$1({ explode, name: name2, style, value })
647
646
  );
648
647
  continue;
649
648
  }
650
649
  if (typeof value === "object") {
651
650
  url = url.replace(
652
651
  match,
653
- serializeObjectParam({
652
+ serializeObjectParam$1({
654
653
  explode,
655
654
  name: name2,
656
655
  style,
@@ -663,7 +662,7 @@ const defaultPathSerializer = ({ path, url: _url }) => {
663
662
  if (style === "matrix") {
664
663
  url = url.replace(
665
664
  match,
666
- `;${serializePrimitiveParam({
665
+ `;${serializePrimitiveParam$1({
667
666
  name: name2,
668
667
  value
669
668
  })}`
@@ -678,7 +677,7 @@ const defaultPathSerializer = ({ path, url: _url }) => {
678
677
  }
679
678
  return url;
680
679
  };
681
- const getUrl = ({
680
+ const getUrl$1 = ({
682
681
  baseUrl,
683
682
  path,
684
683
  query,
@@ -688,7 +687,7 @@ const getUrl = ({
688
687
  const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
689
688
  let url = (baseUrl ?? "") + pathUrl;
690
689
  if (path) {
691
- url = defaultPathSerializer({ path, url });
690
+ url = defaultPathSerializer$1({ path, url });
692
691
  }
693
692
  let search = query ? querySerializer(query) : "";
694
693
  if (search.startsWith("?")) {
@@ -699,7 +698,7 @@ const getUrl = ({
699
698
  }
700
699
  return url;
701
700
  };
702
- function getValidRequestBody(options) {
701
+ function getValidRequestBody$1(options) {
703
702
  const hasBody = options.body !== void 0;
704
703
  const isSerializedBody = hasBody && options.bodySerializer;
705
704
  if (isSerializedBody) {
@@ -714,7 +713,7 @@ function getValidRequestBody(options) {
714
713
  }
715
714
  return void 0;
716
715
  }
717
- const getAuthToken = async (auth, callback) => {
716
+ const getAuthToken$1 = async (auth, callback) => {
718
717
  const token = typeof callback === "function" ? await callback(auth) : callback;
719
718
  if (!token) {
720
719
  return;
@@ -727,7 +726,7 @@ const getAuthToken = async (auth, callback) => {
727
726
  }
728
727
  return token;
729
728
  };
730
- const createQuerySerializer = ({
729
+ const createQuerySerializer$1 = ({
731
730
  parameters = {},
732
731
  ...args
733
732
  } = {}) => {
@@ -741,7 +740,7 @@ const createQuerySerializer = ({
741
740
  }
742
741
  const options = parameters[name2] || args;
743
742
  if (Array.isArray(value)) {
744
- const serializedArray = serializeArrayParam({
743
+ const serializedArray = serializeArrayParam$1({
745
744
  allowReserved: options.allowReserved,
746
745
  explode: true,
747
746
  name: name2,
@@ -751,7 +750,7 @@ const createQuerySerializer = ({
751
750
  });
752
751
  if (serializedArray) search.push(serializedArray);
753
752
  } else if (typeof value === "object") {
754
- const serializedObject = serializeObjectParam({
753
+ const serializedObject = serializeObjectParam$1({
755
754
  allowReserved: options.allowReserved,
756
755
  explode: true,
757
756
  name: name2,
@@ -761,7 +760,7 @@ const createQuerySerializer = ({
761
760
  });
762
761
  if (serializedObject) search.push(serializedObject);
763
762
  } else {
764
- const serializedPrimitive = serializePrimitiveParam({
763
+ const serializedPrimitive = serializePrimitiveParam$1({
765
764
  allowReserved: options.allowReserved,
766
765
  name: name2,
767
766
  value
@@ -774,7 +773,7 @@ const createQuerySerializer = ({
774
773
  };
775
774
  return querySerializer;
776
775
  };
777
- const getParseAs = (contentType) => {
776
+ const getParseAs$1 = (contentType) => {
778
777
  if (!contentType) {
779
778
  return "stream";
780
779
  }
@@ -798,7 +797,7 @@ const getParseAs = (contentType) => {
798
797
  }
799
798
  return;
800
799
  };
801
- const checkForExistence = (options, name2) => {
800
+ const checkForExistence$1 = (options, name2) => {
802
801
  if (!name2) {
803
802
  return false;
804
803
  }
@@ -807,15 +806,15 @@ const checkForExistence = (options, name2) => {
807
806
  }
808
807
  return false;
809
808
  };
810
- const setAuthParams = async ({
809
+ const setAuthParams$1 = async ({
811
810
  security,
812
811
  ...options
813
812
  }) => {
814
813
  for (const auth of security) {
815
- if (checkForExistence(options, auth.name)) {
814
+ if (checkForExistence$1(options, auth.name)) {
816
815
  continue;
817
816
  }
818
- const token = await getAuthToken(auth, options.auth);
817
+ const token = await getAuthToken$1(auth, options.auth);
819
818
  if (!token) {
820
819
  continue;
821
820
  }
@@ -837,35 +836,35 @@ const setAuthParams = async ({
837
836
  }
838
837
  }
839
838
  };
840
- const buildUrl = (options) => getUrl({
839
+ const buildUrl$1 = (options) => getUrl$1({
841
840
  baseUrl: options.baseUrl,
842
841
  path: options.path,
843
842
  query: options.query,
844
- querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
843
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer$1(options.querySerializer),
845
844
  url: options.url
846
845
  });
847
- const mergeConfigs = (a, b) => {
846
+ const mergeConfigs$1 = (a, b) => {
848
847
  const config = { ...a, ...b };
849
848
  if (config.baseUrl?.endsWith("/")) {
850
849
  config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
851
850
  }
852
- config.headers = mergeHeaders(a.headers, b.headers);
851
+ config.headers = mergeHeaders$1(a.headers, b.headers);
853
852
  return config;
854
853
  };
855
- const headersEntries = (headers) => {
854
+ const headersEntries$1 = (headers) => {
856
855
  const entries = [];
857
856
  headers.forEach((value, key) => {
858
857
  entries.push([key, value]);
859
858
  });
860
859
  return entries;
861
860
  };
862
- const mergeHeaders = (...headers) => {
861
+ const mergeHeaders$1 = (...headers) => {
863
862
  const mergedHeaders = new Headers();
864
863
  for (const header of headers) {
865
864
  if (!header) {
866
865
  continue;
867
866
  }
868
- const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
867
+ const iterator = header instanceof Headers ? headersEntries$1(header) : Object.entries(header);
869
868
  for (const [key, value] of iterator) {
870
869
  if (value === null) {
871
870
  mergedHeaders.delete(key);
@@ -883,7 +882,7 @@ const mergeHeaders = (...headers) => {
883
882
  }
884
883
  return mergedHeaders;
885
884
  };
886
- class Interceptors {
885
+ let Interceptors$1 = class Interceptors {
887
886
  fns = [];
888
887
  clear() {
889
888
  this.fns = [];
@@ -916,13 +915,13 @@ class Interceptors {
916
915
  this.fns.push(fn);
917
916
  return this.fns.length - 1;
918
917
  }
919
- }
920
- const createInterceptors = () => ({
921
- error: new Interceptors(),
922
- request: new Interceptors(),
923
- response: new Interceptors()
918
+ };
919
+ const createInterceptors$1 = () => ({
920
+ error: new Interceptors$1(),
921
+ request: new Interceptors$1(),
922
+ response: new Interceptors$1()
924
923
  });
925
- const defaultQuerySerializer = createQuerySerializer({
924
+ const defaultQuerySerializer$1 = createQuerySerializer$1({
926
925
  allowReserved: false,
927
926
  array: {
928
927
  explode: true,
@@ -933,34 +932,34 @@ const defaultQuerySerializer = createQuerySerializer({
933
932
  style: "deepObject"
934
933
  }
935
934
  });
936
- const defaultHeaders = {
935
+ const defaultHeaders$1 = {
937
936
  "Content-Type": "application/json"
938
937
  };
939
- const createConfig = (override = {}) => ({
940
- ...jsonBodySerializer,
941
- headers: defaultHeaders,
938
+ const createConfig$1 = (override = {}) => ({
939
+ ...jsonBodySerializer$1,
940
+ headers: defaultHeaders$1,
942
941
  parseAs: "auto",
943
- querySerializer: defaultQuerySerializer,
942
+ querySerializer: defaultQuerySerializer$1,
944
943
  ...override
945
944
  });
946
- const createClient = (config = {}) => {
947
- let _config = mergeConfigs(createConfig(), config);
945
+ const createClient$1 = (config = {}) => {
946
+ let _config = mergeConfigs$1(createConfig$1(), config);
948
947
  const getConfig = () => ({ ..._config });
949
948
  const setConfig = (config2) => {
950
- _config = mergeConfigs(_config, config2);
949
+ _config = mergeConfigs$1(_config, config2);
951
950
  return getConfig();
952
951
  };
953
- const interceptors = createInterceptors();
952
+ const interceptors = createInterceptors$1();
954
953
  const beforeRequest = async (options) => {
955
954
  const opts = {
956
955
  ..._config,
957
956
  ...options,
958
957
  fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
959
- headers: mergeHeaders(_config.headers, options.headers),
958
+ headers: mergeHeaders$1(_config.headers, options.headers),
960
959
  serializedBody: void 0
961
960
  };
962
961
  if (opts.security) {
963
- await setAuthParams({
962
+ await setAuthParams$1({
964
963
  ...opts,
965
964
  security: opts.security
966
965
  });
@@ -974,7 +973,7 @@ const createClient = (config = {}) => {
974
973
  if (opts.body === void 0 || opts.serializedBody === "") {
975
974
  opts.headers.delete("Content-Type");
976
975
  }
977
- const url = buildUrl(opts);
976
+ const url = buildUrl$1(opts);
978
977
  return { opts, url };
979
978
  };
980
979
  const request = async (options) => {
@@ -982,7 +981,7 @@ const createClient = (config = {}) => {
982
981
  const requestInit = {
983
982
  redirect: "follow",
984
983
  ...opts,
985
- body: getValidRequestBody(opts)
984
+ body: getValidRequestBody$1(opts)
986
985
  };
987
986
  let request2 = new Request(url, requestInit);
988
987
  for (const fn of interceptors.request.fns) {
@@ -1026,7 +1025,7 @@ const createClient = (config = {}) => {
1026
1025
  response
1027
1026
  };
1028
1027
  if (response.ok) {
1029
- const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
1028
+ const parseAs = (opts.parseAs === "auto" ? getParseAs$1(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
1030
1029
  if (response.status === 204 || response.headers.get("Content-Length") === "0") {
1031
1030
  let emptyData;
1032
1031
  switch (parseAs) {
@@ -1104,7 +1103,7 @@ const createClient = (config = {}) => {
1104
1103
  const makeMethodFn = (method) => (options) => request({ ...options, method });
1105
1104
  const makeSseFn = (method) => async (options) => {
1106
1105
  const { opts, url } = await beforeRequest(options);
1107
- return createSseClient({
1106
+ return createSseClient$1({
1108
1107
  ...opts,
1109
1108
  body: opts.body,
1110
1109
  headers: opts.headers,
@@ -1122,7 +1121,7 @@ const createClient = (config = {}) => {
1122
1121
  });
1123
1122
  };
1124
1123
  return {
1125
- buildUrl,
1124
+ buildUrl: buildUrl$1,
1126
1125
  connect: makeMethodFn("CONNECT"),
1127
1126
  delete: makeMethodFn("DELETE"),
1128
1127
  get: makeMethodFn("GET"),
@@ -1149,8 +1148,8 @@ const createClient = (config = {}) => {
1149
1148
  trace: makeMethodFn("TRACE")
1150
1149
  };
1151
1150
  };
1152
- const client = createClient(
1153
- createConfig({ baseUrl: "https://api.themolt.net" })
1151
+ const client = createClient$1(
1152
+ createConfig$1({ baseUrl: "https://api.themolt.net" })
1154
1153
  );
1155
1154
  const startLegreffierOnboarding = (options) => (options.client ?? client).post({
1156
1155
  url: "/public/legreffier/start",
@@ -1161,85 +1160,6 @@ const startLegreffierOnboarding = (options) => (options.client ?? client).post({
1161
1160
  }
1162
1161
  });
1163
1162
  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
1163
  /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
1244
1164
  const ed25519_CURVE = {
1245
1165
  p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
@@ -1716,175 +1636,901 @@ const wNAF = (n) => {
1716
1636
  }
1717
1637
  return { p, f };
1718
1638
  };
1719
- etc.sha512Sync = (...m) => {
1720
- const hash = createHash("sha512");
1721
- m.forEach((msg) => hash.update(msg));
1722
- return hash.digest();
1639
+ var __defProp = Object.defineProperty;
1640
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1641
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1642
+ const jsonBodySerializer = {
1643
+ bodySerializer: (body) => JSON.stringify(
1644
+ body,
1645
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
1646
+ )
1723
1647
  };
1724
- const DOMAIN_PREFIX = "moltnet:v1";
1725
- function buildSigningBytes(message, nonce) {
1726
- const msgHash = createHash("sha256").update(Buffer.from(message, "utf-8")).digest();
1727
- const nonceBytes = Buffer.from(nonce, "utf-8");
1728
- const prefix = Buffer.from(DOMAIN_PREFIX, "utf-8");
1729
- const buf = Buffer.alloc(
1730
- prefix.length + 4 + msgHash.length + 4 + nonceBytes.length
1731
- );
1732
- let offset = 0;
1733
- prefix.copy(buf, offset);
1734
- offset += prefix.length;
1735
- buf.writeUInt32BE(msgHash.length, offset);
1736
- offset += 4;
1737
- msgHash.copy(buf, offset);
1738
- offset += msgHash.length;
1739
- buf.writeUInt32BE(nonceBytes.length, offset);
1740
- offset += 4;
1741
- nonceBytes.copy(buf, offset);
1742
- return new Uint8Array(buf);
1743
- }
1744
- const cryptoService = {
1745
- /**
1746
- * Generate a new Ed25519 keypair
1747
- */
1748
- async generateKeyPair() {
1749
- const privateKeyBytes = utils.randomPrivateKey();
1750
- const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
1751
- const privateKey = Buffer.from(privateKeyBytes).toString("base64");
1752
- const publicKey = `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
1753
- const fingerprint = this.generateFingerprint(publicKeyBytes);
1754
- return { publicKey, privateKey, fingerprint };
1755
- },
1756
- /**
1757
- * Generate human-readable fingerprint from public key
1758
- * Format: A1B2-C3D4-E5F6-G7H8 (first 16 hex chars of SHA256)
1759
- */
1760
- generateFingerprint(publicKeyBytes) {
1761
- const hash = createHash("sha256").update(publicKeyBytes).digest("hex");
1762
- const segments = hash.slice(0, 16).toUpperCase().match(/.{4}/g) ?? [];
1763
- return segments.join("-");
1764
- },
1765
- /**
1766
- * Parse public key from string format
1767
- */
1768
- parsePublicKey(publicKey) {
1769
- const base64 = publicKey.replace(/^ed25519:/, "");
1770
- return new Uint8Array(Buffer.from(base64, "base64"));
1771
- },
1772
- /**
1773
- * Sign a message with private key
1774
- */
1775
- async sign(message, privateKeyBase64) {
1776
- const privateKeyBytes = new Uint8Array(
1777
- Buffer.from(privateKeyBase64, "base64")
1778
- );
1779
- const messageBytes = new TextEncoder().encode(message);
1780
- const signature = await signAsync(messageBytes, privateKeyBytes);
1781
- return Buffer.from(signature).toString("base64");
1782
- },
1783
- /**
1784
- * Verify a signature against a message and public key
1785
- */
1786
- async verify(message, signature, publicKey) {
1787
- try {
1788
- const publicKeyBytes = this.parsePublicKey(publicKey);
1789
- const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
1790
- const messageBytes = new TextEncoder().encode(message);
1791
- return await verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
1792
- } catch {
1793
- return false;
1794
- }
1795
- },
1796
- /**
1797
- * Sign a (message, nonce) pair using deterministic pre-hash.
1798
- * Uses buildSigningBytes for domain separation and canonical serialization.
1799
- */
1800
- async signWithNonce(message, nonce, privateKeyBase64) {
1801
- const privateKeyBytes = new Uint8Array(
1802
- Buffer.from(privateKeyBase64, "base64")
1803
- );
1804
- const signingBytes = buildSigningBytes(message, nonce);
1805
- const signature = await signAsync(signingBytes, privateKeyBytes);
1806
- return Buffer.from(signature).toString("base64");
1807
- },
1808
- /**
1809
- * Verify a signature produced by signWithNonce.
1810
- */
1811
- async verifyWithNonce(message, nonce, signature, publicKey) {
1812
- try {
1813
- const publicKeyBytes = this.parsePublicKey(publicKey);
1814
- const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
1815
- const signingBytes = buildSigningBytes(message, nonce);
1816
- return await verifyAsync(signatureBytes, signingBytes, publicKeyBytes);
1817
- } catch {
1818
- return false;
1819
- }
1820
- },
1821
- /**
1822
- * Create a signed message object
1823
- */
1824
- async createSignedMessage(message, privateKeyBase64, publicKey) {
1825
- const signature = await this.sign(message, privateKeyBase64);
1826
- return { message, signature, publicKey };
1827
- },
1828
- /**
1829
- * Verify a signed message object
1830
- */
1831
- async verifySignedMessage(signedMessage) {
1832
- return this.verify(
1833
- signedMessage.message,
1834
- signedMessage.signature,
1835
- signedMessage.publicKey
1836
- );
1837
- },
1838
- /**
1839
- * Generate a random challenge for authentication
1840
- */
1841
- generateChallenge() {
1842
- return `moltnet:challenge:${randomBytes$1(32).toString("hex")}:${Date.now()}`;
1843
- },
1844
- /**
1845
- * Derive public key from private key
1846
- */
1847
- async derivePublicKey(privateKeyBase64) {
1848
- const privateKeyBytes = new Uint8Array(
1849
- Buffer.from(privateKeyBase64, "base64")
1648
+ const createSseClient = ({
1649
+ onRequest,
1650
+ onSseError,
1651
+ onSseEvent,
1652
+ responseTransformer,
1653
+ responseValidator,
1654
+ sseDefaultRetryDelay,
1655
+ sseMaxRetryAttempts,
1656
+ sseMaxRetryDelay,
1657
+ sseSleepFn,
1658
+ url,
1659
+ ...options
1660
+ }) => {
1661
+ let lastEventId;
1662
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1663
+ const createStream = async function* () {
1664
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
1665
+ let attempt = 0;
1666
+ const signal = options.signal ?? new AbortController().signal;
1667
+ while (true) {
1668
+ if (signal.aborted) break;
1669
+ attempt++;
1670
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
1671
+ if (lastEventId !== void 0) {
1672
+ headers.set("Last-Event-ID", lastEventId);
1673
+ }
1674
+ try {
1675
+ const requestInit = {
1676
+ redirect: "follow",
1677
+ ...options,
1678
+ body: options.serializedBody,
1679
+ headers,
1680
+ signal
1681
+ };
1682
+ let request = new Request(url, requestInit);
1683
+ if (onRequest) {
1684
+ request = await onRequest(url, requestInit);
1685
+ }
1686
+ const _fetch = options.fetch ?? globalThis.fetch;
1687
+ const response = await _fetch(request);
1688
+ if (!response.ok)
1689
+ throw new Error(
1690
+ `SSE failed: ${response.status} ${response.statusText}`
1691
+ );
1692
+ if (!response.body) throw new Error("No body in SSE response");
1693
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
1694
+ let buffer = "";
1695
+ const abortHandler = () => {
1696
+ try {
1697
+ reader.cancel();
1698
+ } catch {
1699
+ }
1700
+ };
1701
+ signal.addEventListener("abort", abortHandler);
1702
+ try {
1703
+ while (true) {
1704
+ const { done, value } = await reader.read();
1705
+ if (done) break;
1706
+ buffer += value;
1707
+ buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1708
+ const chunks = buffer.split("\n\n");
1709
+ buffer = chunks.pop() ?? "";
1710
+ for (const chunk of chunks) {
1711
+ const lines = chunk.split("\n");
1712
+ const dataLines = [];
1713
+ let eventName;
1714
+ for (const line of lines) {
1715
+ if (line.startsWith("data:")) {
1716
+ dataLines.push(line.replace(/^data:\s*/, ""));
1717
+ } else if (line.startsWith("event:")) {
1718
+ eventName = line.replace(/^event:\s*/, "");
1719
+ } else if (line.startsWith("id:")) {
1720
+ lastEventId = line.replace(/^id:\s*/, "");
1721
+ } else if (line.startsWith("retry:")) {
1722
+ const parsed = Number.parseInt(
1723
+ line.replace(/^retry:\s*/, ""),
1724
+ 10
1725
+ );
1726
+ if (!Number.isNaN(parsed)) {
1727
+ retryDelay = parsed;
1728
+ }
1729
+ }
1730
+ }
1731
+ let data;
1732
+ let parsedJson = false;
1733
+ if (dataLines.length) {
1734
+ const rawData = dataLines.join("\n");
1735
+ try {
1736
+ data = JSON.parse(rawData);
1737
+ parsedJson = true;
1738
+ } catch {
1739
+ data = rawData;
1740
+ }
1741
+ }
1742
+ if (parsedJson) {
1743
+ if (responseValidator) {
1744
+ await responseValidator(data);
1745
+ }
1746
+ if (responseTransformer) {
1747
+ data = await responseTransformer(data);
1748
+ }
1749
+ }
1750
+ onSseEvent == null ? void 0 : onSseEvent({
1751
+ data,
1752
+ event: eventName,
1753
+ id: lastEventId,
1754
+ retry: retryDelay
1755
+ });
1756
+ if (dataLines.length) {
1757
+ yield data;
1758
+ }
1759
+ }
1760
+ }
1761
+ } finally {
1762
+ signal.removeEventListener("abort", abortHandler);
1763
+ reader.releaseLock();
1764
+ }
1765
+ break;
1766
+ } catch (error) {
1767
+ onSseError == null ? void 0 : onSseError(error);
1768
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
1769
+ break;
1770
+ }
1771
+ const backoff = Math.min(
1772
+ retryDelay * 2 ** (attempt - 1),
1773
+ sseMaxRetryDelay ?? 3e4
1774
+ );
1775
+ await sleep(backoff);
1776
+ }
1777
+ }
1778
+ };
1779
+ const stream = createStream();
1780
+ return { stream };
1781
+ };
1782
+ const separatorArrayExplode = (style) => {
1783
+ switch (style) {
1784
+ case "label":
1785
+ return ".";
1786
+ case "matrix":
1787
+ return ";";
1788
+ case "simple":
1789
+ return ",";
1790
+ default:
1791
+ return "&";
1792
+ }
1793
+ };
1794
+ const separatorArrayNoExplode = (style) => {
1795
+ switch (style) {
1796
+ case "form":
1797
+ return ",";
1798
+ case "pipeDelimited":
1799
+ return "|";
1800
+ case "spaceDelimited":
1801
+ return "%20";
1802
+ default:
1803
+ return ",";
1804
+ }
1805
+ };
1806
+ const separatorObjectExplode = (style) => {
1807
+ switch (style) {
1808
+ case "label":
1809
+ return ".";
1810
+ case "matrix":
1811
+ return ";";
1812
+ case "simple":
1813
+ return ",";
1814
+ default:
1815
+ return "&";
1816
+ }
1817
+ };
1818
+ const serializeArrayParam = ({
1819
+ allowReserved,
1820
+ explode,
1821
+ name: name2,
1822
+ style,
1823
+ value
1824
+ }) => {
1825
+ if (!explode) {
1826
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
1827
+ switch (style) {
1828
+ case "label":
1829
+ return `.${joinedValues2}`;
1830
+ case "matrix":
1831
+ return `;${name2}=${joinedValues2}`;
1832
+ case "simple":
1833
+ return joinedValues2;
1834
+ default:
1835
+ return `${name2}=${joinedValues2}`;
1836
+ }
1837
+ }
1838
+ const separator = separatorArrayExplode(style);
1839
+ const joinedValues = value.map((v) => {
1840
+ if (style === "label" || style === "simple") {
1841
+ return allowReserved ? v : encodeURIComponent(v);
1842
+ }
1843
+ return serializePrimitiveParam({
1844
+ allowReserved,
1845
+ name: name2,
1846
+ value: v
1847
+ });
1848
+ }).join(separator);
1849
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
1850
+ };
1851
+ const serializePrimitiveParam = ({
1852
+ allowReserved,
1853
+ name: name2,
1854
+ value
1855
+ }) => {
1856
+ if (value === void 0 || value === null) {
1857
+ return "";
1858
+ }
1859
+ if (typeof value === "object") {
1860
+ throw new Error(
1861
+ "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these."
1850
1862
  );
1851
- const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
1852
- return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
1853
- },
1854
- /**
1855
- * Get fingerprint from public key string
1856
- */
1857
- getFingerprintFromPublicKey(publicKey) {
1858
- const publicKeyBytes = this.parsePublicKey(publicKey);
1859
- return this.generateFingerprint(publicKeyBytes);
1860
- },
1861
- /**
1862
- * Create a proof of identity ownership (for DCR metadata)
1863
- */
1864
- async createIdentityProof(identityId, privateKeyBase64) {
1865
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1866
- const message = `moltnet:register:${identityId}:${timestamp}`;
1867
- const signature = await this.sign(message, privateKeyBase64);
1868
- return { message, signature, timestamp };
1869
- },
1870
- /**
1871
- * Verify an identity proof
1872
- */
1873
- async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
1874
- const isValid = await this.verify(
1875
- proof.message,
1876
- proof.signature,
1877
- publicKey
1863
+ }
1864
+ return `${name2}=${allowReserved ? value : encodeURIComponent(value)}`;
1865
+ };
1866
+ const serializeObjectParam = ({
1867
+ allowReserved,
1868
+ explode,
1869
+ name: name2,
1870
+ style,
1871
+ value,
1872
+ valueOnly
1873
+ }) => {
1874
+ if (value instanceof Date) {
1875
+ return valueOnly ? value.toISOString() : `${name2}=${value.toISOString()}`;
1876
+ }
1877
+ if (style !== "deepObject" && !explode) {
1878
+ let values2 = [];
1879
+ Object.entries(value).forEach(([key, v]) => {
1880
+ values2 = [
1881
+ ...values2,
1882
+ key,
1883
+ allowReserved ? v : encodeURIComponent(v)
1884
+ ];
1885
+ });
1886
+ const joinedValues2 = values2.join(",");
1887
+ switch (style) {
1888
+ case "form":
1889
+ return `${name2}=${joinedValues2}`;
1890
+ case "label":
1891
+ return `.${joinedValues2}`;
1892
+ case "matrix":
1893
+ return `;${name2}=${joinedValues2}`;
1894
+ default:
1895
+ return joinedValues2;
1896
+ }
1897
+ }
1898
+ const separator = separatorObjectExplode(style);
1899
+ const joinedValues = Object.entries(value).map(
1900
+ ([key, v]) => serializePrimitiveParam({
1901
+ allowReserved,
1902
+ name: style === "deepObject" ? `${name2}[${key}]` : key,
1903
+ value: v
1904
+ })
1905
+ ).join(separator);
1906
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
1907
+ };
1908
+ const PATH_PARAM_RE = /\{[^{}]+\}/g;
1909
+ const defaultPathSerializer = ({ path, url: _url }) => {
1910
+ let url = _url;
1911
+ const matches = _url.match(PATH_PARAM_RE);
1912
+ if (matches) {
1913
+ for (const match of matches) {
1914
+ let explode = false;
1915
+ let name2 = match.substring(1, match.length - 1);
1916
+ let style = "simple";
1917
+ if (name2.endsWith("*")) {
1918
+ explode = true;
1919
+ name2 = name2.substring(0, name2.length - 1);
1920
+ }
1921
+ if (name2.startsWith(".")) {
1922
+ name2 = name2.substring(1);
1923
+ style = "label";
1924
+ } else if (name2.startsWith(";")) {
1925
+ name2 = name2.substring(1);
1926
+ style = "matrix";
1927
+ }
1928
+ const value = path[name2];
1929
+ if (value === void 0 || value === null) {
1930
+ continue;
1931
+ }
1932
+ if (Array.isArray(value)) {
1933
+ url = url.replace(
1934
+ match,
1935
+ serializeArrayParam({ explode, name: name2, style, value })
1936
+ );
1937
+ continue;
1938
+ }
1939
+ if (typeof value === "object") {
1940
+ url = url.replace(
1941
+ match,
1942
+ serializeObjectParam({
1943
+ explode,
1944
+ name: name2,
1945
+ style,
1946
+ value,
1947
+ valueOnly: true
1948
+ })
1949
+ );
1950
+ continue;
1951
+ }
1952
+ if (style === "matrix") {
1953
+ url = url.replace(
1954
+ match,
1955
+ `;${serializePrimitiveParam({
1956
+ name: name2,
1957
+ value
1958
+ })}`
1959
+ );
1960
+ continue;
1961
+ }
1962
+ const replaceValue = encodeURIComponent(
1963
+ style === "label" ? `.${value}` : value
1964
+ );
1965
+ url = url.replace(match, replaceValue);
1966
+ }
1967
+ }
1968
+ return url;
1969
+ };
1970
+ const getUrl = ({
1971
+ baseUrl,
1972
+ path,
1973
+ query,
1974
+ querySerializer,
1975
+ url: _url
1976
+ }) => {
1977
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
1978
+ let url = (baseUrl ?? "") + pathUrl;
1979
+ if (path) {
1980
+ url = defaultPathSerializer({ path, url });
1981
+ }
1982
+ let search = query ? querySerializer(query) : "";
1983
+ if (search.startsWith("?")) {
1984
+ search = search.substring(1);
1985
+ }
1986
+ if (search) {
1987
+ url += `?${search}`;
1988
+ }
1989
+ return url;
1990
+ };
1991
+ function getValidRequestBody(options) {
1992
+ const hasBody = options.body !== void 0;
1993
+ const isSerializedBody = hasBody && options.bodySerializer;
1994
+ if (isSerializedBody) {
1995
+ if ("serializedBody" in options) {
1996
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
1997
+ return hasSerializedBody ? options.serializedBody : null;
1998
+ }
1999
+ return options.body !== "" ? options.body : null;
2000
+ }
2001
+ if (hasBody) {
2002
+ return options.body;
2003
+ }
2004
+ return void 0;
2005
+ }
2006
+ const getAuthToken = async (auth, callback) => {
2007
+ const token = typeof callback === "function" ? await callback(auth) : callback;
2008
+ if (!token) {
2009
+ return;
2010
+ }
2011
+ if (auth.scheme === "bearer") {
2012
+ return `Bearer ${token}`;
2013
+ }
2014
+ if (auth.scheme === "basic") {
2015
+ return `Basic ${btoa(token)}`;
2016
+ }
2017
+ return token;
2018
+ };
2019
+ const createQuerySerializer = ({
2020
+ parameters = {},
2021
+ ...args
2022
+ } = {}) => {
2023
+ const querySerializer = (queryParams) => {
2024
+ const search = [];
2025
+ if (queryParams && typeof queryParams === "object") {
2026
+ for (const name2 in queryParams) {
2027
+ const value = queryParams[name2];
2028
+ if (value === void 0 || value === null) {
2029
+ continue;
2030
+ }
2031
+ const options = parameters[name2] || args;
2032
+ if (Array.isArray(value)) {
2033
+ const serializedArray = serializeArrayParam({
2034
+ allowReserved: options.allowReserved,
2035
+ explode: true,
2036
+ name: name2,
2037
+ style: "form",
2038
+ value,
2039
+ ...options.array
2040
+ });
2041
+ if (serializedArray) search.push(serializedArray);
2042
+ } else if (typeof value === "object") {
2043
+ const serializedObject = serializeObjectParam({
2044
+ allowReserved: options.allowReserved,
2045
+ explode: true,
2046
+ name: name2,
2047
+ style: "deepObject",
2048
+ value,
2049
+ ...options.object
2050
+ });
2051
+ if (serializedObject) search.push(serializedObject);
2052
+ } else {
2053
+ const serializedPrimitive = serializePrimitiveParam({
2054
+ allowReserved: options.allowReserved,
2055
+ name: name2,
2056
+ value
2057
+ });
2058
+ if (serializedPrimitive) search.push(serializedPrimitive);
2059
+ }
2060
+ }
2061
+ }
2062
+ return search.join("&");
2063
+ };
2064
+ return querySerializer;
2065
+ };
2066
+ const getParseAs = (contentType) => {
2067
+ var _a2;
2068
+ if (!contentType) {
2069
+ return "stream";
2070
+ }
2071
+ const cleanContent = (_a2 = contentType.split(";")[0]) == null ? void 0 : _a2.trim();
2072
+ if (!cleanContent) {
2073
+ return;
2074
+ }
2075
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
2076
+ return "json";
2077
+ }
2078
+ if (cleanContent === "multipart/form-data") {
2079
+ return "formData";
2080
+ }
2081
+ if (["application/", "audio/", "image/", "video/"].some(
2082
+ (type) => cleanContent.startsWith(type)
2083
+ )) {
2084
+ return "blob";
2085
+ }
2086
+ if (cleanContent.startsWith("text/")) {
2087
+ return "text";
2088
+ }
2089
+ return;
2090
+ };
2091
+ const checkForExistence = (options, name2) => {
2092
+ var _a2, _b;
2093
+ if (!name2) {
2094
+ return false;
2095
+ }
2096
+ if (options.headers.has(name2) || ((_a2 = options.query) == null ? void 0 : _a2[name2]) || ((_b = options.headers.get("Cookie")) == null ? void 0 : _b.includes(`${name2}=`))) {
2097
+ return true;
2098
+ }
2099
+ return false;
2100
+ };
2101
+ const setAuthParams = async ({
2102
+ security,
2103
+ ...options
2104
+ }) => {
2105
+ for (const auth of security) {
2106
+ if (checkForExistence(options, auth.name)) {
2107
+ continue;
2108
+ }
2109
+ const token = await getAuthToken(auth, options.auth);
2110
+ if (!token) {
2111
+ continue;
2112
+ }
2113
+ const name2 = auth.name ?? "Authorization";
2114
+ switch (auth.in) {
2115
+ case "query":
2116
+ if (!options.query) {
2117
+ options.query = {};
2118
+ }
2119
+ options.query[name2] = token;
2120
+ break;
2121
+ case "cookie":
2122
+ options.headers.append("Cookie", `${name2}=${token}`);
2123
+ break;
2124
+ case "header":
2125
+ default:
2126
+ options.headers.set(name2, token);
2127
+ break;
2128
+ }
2129
+ }
2130
+ };
2131
+ const buildUrl = (options) => getUrl({
2132
+ baseUrl: options.baseUrl,
2133
+ path: options.path,
2134
+ query: options.query,
2135
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
2136
+ url: options.url
2137
+ });
2138
+ const mergeConfigs = (a, b) => {
2139
+ var _a2;
2140
+ const config = { ...a, ...b };
2141
+ if ((_a2 = config.baseUrl) == null ? void 0 : _a2.endsWith("/")) {
2142
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
2143
+ }
2144
+ config.headers = mergeHeaders(a.headers, b.headers);
2145
+ return config;
2146
+ };
2147
+ const headersEntries = (headers) => {
2148
+ const entries = [];
2149
+ headers.forEach((value, key) => {
2150
+ entries.push([key, value]);
2151
+ });
2152
+ return entries;
2153
+ };
2154
+ const mergeHeaders = (...headers) => {
2155
+ const mergedHeaders = new Headers();
2156
+ for (const header of headers) {
2157
+ if (!header) {
2158
+ continue;
2159
+ }
2160
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
2161
+ for (const [key, value] of iterator) {
2162
+ if (value === null) {
2163
+ mergedHeaders.delete(key);
2164
+ } else if (Array.isArray(value)) {
2165
+ for (const v of value) {
2166
+ mergedHeaders.append(key, v);
2167
+ }
2168
+ } else if (value !== void 0) {
2169
+ mergedHeaders.set(
2170
+ key,
2171
+ typeof value === "object" ? JSON.stringify(value) : value
2172
+ );
2173
+ }
2174
+ }
2175
+ }
2176
+ return mergedHeaders;
2177
+ };
2178
+ class Interceptors2 {
2179
+ constructor() {
2180
+ __publicField(this, "fns", []);
2181
+ }
2182
+ clear() {
2183
+ this.fns = [];
2184
+ }
2185
+ eject(id) {
2186
+ const index = this.getInterceptorIndex(id);
2187
+ if (this.fns[index]) {
2188
+ this.fns[index] = null;
2189
+ }
2190
+ }
2191
+ exists(id) {
2192
+ const index = this.getInterceptorIndex(id);
2193
+ return Boolean(this.fns[index]);
2194
+ }
2195
+ getInterceptorIndex(id) {
2196
+ if (typeof id === "number") {
2197
+ return this.fns[id] ? id : -1;
2198
+ }
2199
+ return this.fns.indexOf(id);
2200
+ }
2201
+ update(id, fn) {
2202
+ const index = this.getInterceptorIndex(id);
2203
+ if (this.fns[index]) {
2204
+ this.fns[index] = fn;
2205
+ return id;
2206
+ }
2207
+ return false;
2208
+ }
2209
+ use(fn) {
2210
+ this.fns.push(fn);
2211
+ return this.fns.length - 1;
2212
+ }
2213
+ }
2214
+ const createInterceptors = () => ({
2215
+ error: new Interceptors2(),
2216
+ request: new Interceptors2(),
2217
+ response: new Interceptors2()
2218
+ });
2219
+ const defaultQuerySerializer = createQuerySerializer({
2220
+ allowReserved: false,
2221
+ array: {
2222
+ explode: true,
2223
+ style: "form"
2224
+ },
2225
+ object: {
2226
+ explode: true,
2227
+ style: "deepObject"
2228
+ }
2229
+ });
2230
+ const defaultHeaders = {
2231
+ "Content-Type": "application/json"
2232
+ };
2233
+ const createConfig = (override = {}) => ({
2234
+ ...jsonBodySerializer,
2235
+ headers: defaultHeaders,
2236
+ parseAs: "auto",
2237
+ querySerializer: defaultQuerySerializer,
2238
+ ...override
2239
+ });
2240
+ const createClient = (config = {}) => {
2241
+ let _config = mergeConfigs(createConfig(), config);
2242
+ const getConfig = () => ({ ..._config });
2243
+ const setConfig = (config2) => {
2244
+ _config = mergeConfigs(_config, config2);
2245
+ return getConfig();
2246
+ };
2247
+ const interceptors = createInterceptors();
2248
+ const beforeRequest = async (options) => {
2249
+ const opts = {
2250
+ ..._config,
2251
+ ...options,
2252
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
2253
+ headers: mergeHeaders(_config.headers, options.headers),
2254
+ serializedBody: void 0
2255
+ };
2256
+ if (opts.security) {
2257
+ await setAuthParams({
2258
+ ...opts,
2259
+ security: opts.security
2260
+ });
2261
+ }
2262
+ if (opts.requestValidator) {
2263
+ await opts.requestValidator(opts);
2264
+ }
2265
+ if (opts.body !== void 0 && opts.bodySerializer) {
2266
+ opts.serializedBody = opts.bodySerializer(opts.body);
2267
+ }
2268
+ if (opts.body === void 0 || opts.serializedBody === "") {
2269
+ opts.headers.delete("Content-Type");
2270
+ }
2271
+ const url = buildUrl(opts);
2272
+ return { opts, url };
2273
+ };
2274
+ const request = async (options) => {
2275
+ const { opts, url } = await beforeRequest(options);
2276
+ const requestInit = {
2277
+ redirect: "follow",
2278
+ ...opts,
2279
+ body: getValidRequestBody(opts)
2280
+ };
2281
+ let request2 = new Request(url, requestInit);
2282
+ for (const fn of interceptors.request.fns) {
2283
+ if (fn) {
2284
+ request2 = await fn(request2, opts);
2285
+ }
2286
+ }
2287
+ const _fetch = opts.fetch;
2288
+ let response;
2289
+ try {
2290
+ response = await _fetch(request2);
2291
+ } catch (error2) {
2292
+ let finalError2 = error2;
2293
+ for (const fn of interceptors.error.fns) {
2294
+ if (fn) {
2295
+ finalError2 = await fn(
2296
+ error2,
2297
+ void 0,
2298
+ request2,
2299
+ opts
2300
+ );
2301
+ }
2302
+ }
2303
+ finalError2 = finalError2 || {};
2304
+ if (opts.throwOnError) {
2305
+ throw finalError2;
2306
+ }
2307
+ return opts.responseStyle === "data" ? void 0 : {
2308
+ error: finalError2,
2309
+ request: request2,
2310
+ response: void 0
2311
+ };
2312
+ }
2313
+ for (const fn of interceptors.response.fns) {
2314
+ if (fn) {
2315
+ response = await fn(response, request2, opts);
2316
+ }
2317
+ }
2318
+ const result = {
2319
+ request: request2,
2320
+ response
2321
+ };
2322
+ if (response.ok) {
2323
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
2324
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
2325
+ let emptyData;
2326
+ switch (parseAs) {
2327
+ case "arrayBuffer":
2328
+ case "blob":
2329
+ case "text":
2330
+ emptyData = await response[parseAs]();
2331
+ break;
2332
+ case "formData":
2333
+ emptyData = new FormData();
2334
+ break;
2335
+ case "stream":
2336
+ emptyData = response.body;
2337
+ break;
2338
+ case "json":
2339
+ default:
2340
+ emptyData = {};
2341
+ break;
2342
+ }
2343
+ return opts.responseStyle === "data" ? emptyData : {
2344
+ data: emptyData,
2345
+ ...result
2346
+ };
2347
+ }
2348
+ let data;
2349
+ switch (parseAs) {
2350
+ case "arrayBuffer":
2351
+ case "blob":
2352
+ case "formData":
2353
+ case "json":
2354
+ case "text":
2355
+ data = await response[parseAs]();
2356
+ break;
2357
+ case "stream":
2358
+ return opts.responseStyle === "data" ? response.body : {
2359
+ data: response.body,
2360
+ ...result
2361
+ };
2362
+ }
2363
+ if (parseAs === "json") {
2364
+ if (opts.responseValidator) {
2365
+ await opts.responseValidator(data);
2366
+ }
2367
+ if (opts.responseTransformer) {
2368
+ data = await opts.responseTransformer(data);
2369
+ }
2370
+ }
2371
+ return opts.responseStyle === "data" ? data : {
2372
+ data,
2373
+ ...result
2374
+ };
2375
+ }
2376
+ const textError = await response.text();
2377
+ let jsonError;
2378
+ try {
2379
+ jsonError = JSON.parse(textError);
2380
+ } catch {
2381
+ }
2382
+ const error = jsonError ?? textError;
2383
+ let finalError = error;
2384
+ for (const fn of interceptors.error.fns) {
2385
+ if (fn) {
2386
+ finalError = await fn(error, response, request2, opts);
2387
+ }
2388
+ }
2389
+ finalError = finalError || {};
2390
+ if (opts.throwOnError) {
2391
+ throw finalError;
2392
+ }
2393
+ return opts.responseStyle === "data" ? void 0 : {
2394
+ error: finalError,
2395
+ ...result
2396
+ };
2397
+ };
2398
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
2399
+ const makeSseFn = (method) => async (options) => {
2400
+ const { opts, url } = await beforeRequest(options);
2401
+ return createSseClient({
2402
+ ...opts,
2403
+ body: opts.body,
2404
+ headers: opts.headers,
2405
+ method,
2406
+ onRequest: async (url2, init) => {
2407
+ let request2 = new Request(url2, init);
2408
+ for (const fn of interceptors.request.fns) {
2409
+ if (fn) {
2410
+ request2 = await fn(request2, opts);
2411
+ }
2412
+ }
2413
+ return request2;
2414
+ },
2415
+ url
2416
+ });
2417
+ };
2418
+ return {
2419
+ buildUrl,
2420
+ connect: makeMethodFn("CONNECT"),
2421
+ delete: makeMethodFn("DELETE"),
2422
+ get: makeMethodFn("GET"),
2423
+ getConfig,
2424
+ head: makeMethodFn("HEAD"),
2425
+ interceptors,
2426
+ options: makeMethodFn("OPTIONS"),
2427
+ patch: makeMethodFn("PATCH"),
2428
+ post: makeMethodFn("POST"),
2429
+ put: makeMethodFn("PUT"),
2430
+ request,
2431
+ setConfig,
2432
+ sse: {
2433
+ connect: makeSseFn("CONNECT"),
2434
+ delete: makeSseFn("DELETE"),
2435
+ get: makeSseFn("GET"),
2436
+ head: makeSseFn("HEAD"),
2437
+ options: makeSseFn("OPTIONS"),
2438
+ patch: makeSseFn("PATCH"),
2439
+ post: makeSseFn("POST"),
2440
+ put: makeSseFn("PUT"),
2441
+ trace: makeSseFn("TRACE")
2442
+ },
2443
+ trace: makeMethodFn("TRACE")
2444
+ };
2445
+ };
2446
+ createClient(
2447
+ createConfig({ baseUrl: "https://api.themolt.net" })
2448
+ );
2449
+ class MoltNetError extends Error {
2450
+ constructor(message, options) {
2451
+ super(message);
2452
+ __publicField(this, "code");
2453
+ __publicField(this, "statusCode");
2454
+ __publicField(this, "detail");
2455
+ this.name = "MoltNetError";
2456
+ this.code = options.code;
2457
+ this.statusCode = options.statusCode;
2458
+ this.detail = options.detail;
2459
+ }
2460
+ }
2461
+ function problemToError(problem, statusCode) {
2462
+ return new MoltNetError(problem.title ?? "Request failed", {
2463
+ code: problem.type ?? problem.code ?? "UNKNOWN",
2464
+ statusCode,
2465
+ detail: problem.detail
2466
+ });
2467
+ }
2468
+ async function writeMcpConfig(mcpConfig, dir2) {
2469
+ const targetDir = dir2 ?? process.cwd();
2470
+ const filePath = join(targetDir, ".mcp.json");
2471
+ let existing = {};
2472
+ try {
2473
+ const content = await readFile(filePath, "utf-8");
2474
+ existing = JSON.parse(content);
2475
+ } catch {
2476
+ }
2477
+ const merged = {
2478
+ ...existing,
2479
+ mcpServers: {
2480
+ ...existing.mcpServers ?? {},
2481
+ ...mcpConfig.mcpServers
2482
+ }
2483
+ };
2484
+ await writeFile(filePath, JSON.stringify(merged, null, 2) + "\n");
2485
+ return filePath;
2486
+ }
2487
+ function getConfigDir() {
2488
+ return join(homedir(), ".config", "moltnet");
2489
+ }
2490
+ function getConfigPath(configDir) {
2491
+ return join(configDir ?? getConfigDir(), "moltnet.json");
2492
+ }
2493
+ async function readConfig(configDir) {
2494
+ const dir2 = configDir ?? getConfigDir();
2495
+ try {
2496
+ const content = await readFile(join(dir2, "moltnet.json"), "utf-8");
2497
+ return JSON.parse(content);
2498
+ } catch {
2499
+ }
2500
+ try {
2501
+ const content = await readFile(join(dir2, "credentials.json"), "utf-8");
2502
+ console.warn(
2503
+ "Warning: credentials.json is deprecated. New writes use moltnet.json. Support will be removed in 3 minor versions."
1878
2504
  );
1879
- if (!isValid) return false;
1880
- const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
1881
- if (!proof.message.startsWith(expectedPrefix)) return false;
1882
- const proofTime = new Date(proof.timestamp).getTime();
1883
- const now = Date.now();
1884
- const fiveMinutes = 5 * 60 * 1e3;
1885
- if (now - proofTime > fiveMinutes) return false;
1886
- return true;
2505
+ return JSON.parse(content);
2506
+ } catch {
2507
+ return null;
2508
+ }
2509
+ }
2510
+ async function writeConfig(config, configDir) {
2511
+ const dir2 = configDir ?? getConfigDir();
2512
+ await mkdir(dir2, { recursive: true });
2513
+ const filePath = join(dir2, "moltnet.json");
2514
+ await writeFile(filePath, JSON.stringify(config, null, 2) + "\n", {
2515
+ mode: 384
2516
+ });
2517
+ await chmod(filePath, 384);
2518
+ return filePath;
2519
+ }
2520
+ async function updateConfigSection(section, data, configDir) {
2521
+ const config = await readConfig(configDir);
2522
+ if (!config) {
2523
+ throw new Error("No config found — run `moltnet register` first");
1887
2524
  }
2525
+ const existing = config[section] ?? {};
2526
+ const updated = { ...existing, ...data };
2527
+ Object.assign(config, { [section]: updated });
2528
+ await writeConfig(config, configDir);
2529
+ }
2530
+ etc.sha512Sync = (...m) => {
2531
+ const hash = createHash("sha512");
2532
+ m.forEach((msg) => hash.update(msg));
2533
+ return hash.digest();
1888
2534
  };
1889
2535
  if (!etc.sha512Sync) {
1890
2536
  etc.sha512Sync = (...m) => {
@@ -1990,19 +2636,25 @@ function toSSHPrivateKey(seedBase64) {
1990
2636
  ].join("\n");
1991
2637
  }
1992
2638
  async function exportSSHKey(opts) {
1993
- const config = await readConfig(opts?.configDir);
2639
+ const config = await readConfig(opts == null ? void 0 : opts.configDir);
1994
2640
  if (!config) {
1995
- throw new Error(`No config found at ${getConfigPath(opts?.configDir)} — run \`moltnet register\` first`);
2641
+ throw new Error(
2642
+ `No config found at ${getConfigPath(opts == null ? void 0 : opts.configDir)} — run \`moltnet register\` first`
2643
+ );
1996
2644
  }
1997
2645
  const privateKeySSH = toSSHPrivateKey(config.keys.private_key);
1998
2646
  const publicKeySSH = toSSHPublicKey(config.keys.public_key);
1999
- const outputDir = opts?.outputDir ?? join(opts?.configDir ?? getConfigDir(), "ssh");
2647
+ const outputDir = (opts == null ? void 0 : opts.outputDir) ?? join((opts == null ? void 0 : opts.configDir) ?? getConfigDir(), "ssh");
2000
2648
  await mkdir(outputDir, { recursive: true });
2001
2649
  const privatePath = join(outputDir, "id_ed25519");
2002
2650
  const publicPath = join(outputDir, "id_ed25519.pub");
2003
2651
  await writeFile(privatePath, privateKeySSH, { mode: 384 });
2004
2652
  await writeFile(publicPath, publicKeySSH, { mode: 420 });
2005
- await updateConfigSection("ssh", { private_key_path: privatePath, public_key_path: publicPath }, opts?.configDir);
2653
+ await updateConfigSection(
2654
+ "ssh",
2655
+ { private_key_path: privatePath, public_key_path: publicPath },
2656
+ opts == null ? void 0 : opts.configDir
2657
+ );
2006
2658
  return { privatePath, publicPath };
2007
2659
  }
2008
2660
  const POLL_INTERVAL_MS = 5e3;
@@ -2020,7 +2672,7 @@ function toErrorMessage(err2) {
2020
2672
  }
2021
2673
  const POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2022
2674
  function makeClient(baseUrl) {
2023
- return createClient({ baseUrl });
2675
+ return createClient$1({ baseUrl });
2024
2676
  }
2025
2677
  async function startOnboarding(baseUrl, body) {
2026
2678
  const client2 = makeClient(baseUrl);
@@ -2067,24 +2719,49 @@ async function pollUntil(baseUrl, workflowId, targetStatuses, onTick) {
2067
2719
  `Timed out waiting for status: ${targetStatuses.join(" or ")}`
2068
2720
  );
2069
2721
  }
2722
+ const SKILL_DIRS = {
2723
+ claude: ".claude/skills"
2724
+ // codex: '.agents/skills',
2725
+ };
2726
+ const SKILL_VERSION = "legreffier-v0.1.0";
2070
2727
  const SKILLS = [
2071
2728
  {
2072
2729
  name: "legreffier",
2073
- url: "https://raw.githubusercontent.com/getlarge/themoltnet/main/.claude/skills/legreffier/SKILL.md"
2730
+ url: `https://raw.githubusercontent.com/getlarge/themoltnet/${SKILL_VERSION}/.claude/skills/legreffier/SKILL.md`
2074
2731
  }
2075
2732
  ];
2076
- async function downloadSkills(repoDir) {
2733
+ async function downloadSkills(repoDir, agentTypes) {
2734
+ const dirs = agentTypes.map((t) => SKILL_DIRS[t]).filter((d) => !!d);
2735
+ if (dirs.length === 0) return;
2077
2736
  for (const skill of SKILLS) {
2078
- const dir2 = join(repoDir, ".claude", "skills", skill.name);
2079
- await mkdir(dir2, { recursive: true });
2080
2737
  const res = await fetch(skill.url);
2081
2738
  if (!res.ok) {
2082
2739
  throw new Error(`Failed to download skill ${skill.name} (${res.status})`);
2083
2740
  }
2084
2741
  const content = await res.text();
2085
- await writeFile(join(dir2, "SKILL.md"), content, "utf-8");
2742
+ for (const skillDir of dirs) {
2743
+ const destDir = join(repoDir, skillDir, skill.name);
2744
+ await mkdir(destDir, { recursive: true });
2745
+ await writeFile(join(destDir, "SKILL.md"), content, "utf-8");
2746
+ }
2086
2747
  }
2087
2748
  }
2749
+ function buildPermissions(agentName) {
2750
+ return [
2751
+ // Read-only git commands used by session activation & commit workflow
2752
+ "Bash(git config *)",
2753
+ "Bash(git diff *)",
2754
+ "Bash(git log *)",
2755
+ "Bash(git rev-parse *)",
2756
+ "Bash(git worktree list)",
2757
+ // Signing CLI
2758
+ "Bash(moltnet sign *)",
2759
+ // Worktree symlink creation
2760
+ "Bash(ln -s *)",
2761
+ // All MCP tools for this agent's server
2762
+ `mcp__${agentName}__*`
2763
+ ];
2764
+ }
2088
2765
  function toEnvPrefix(agentName) {
2089
2766
  return agentName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
2090
2767
  }
@@ -2106,9 +2783,23 @@ async function writeSettingsLocal({
2106
2783
  } catch {
2107
2784
  }
2108
2785
  const prefix = toEnvPrefix(agentName);
2786
+ const existingServers = Array.isArray(
2787
+ existing.enabledMcpjsonServers
2788
+ ) ? existing.enabledMcpjsonServers : [];
2789
+ const enabledMcpjsonServers = existingServers.includes(agentName) ? existingServers : [...existingServers, agentName];
2790
+ const newPerms = buildPermissions(agentName);
2791
+ const existingAllow = Array.isArray(existing.permissions?.allow) ? existing.permissions.allow : [];
2792
+ const mergedAllow = [
2793
+ ...existingAllow,
2794
+ ...newPerms.filter((p) => !existingAllow.includes(p))
2795
+ ];
2109
2796
  const settings = {
2110
2797
  ...existing,
2111
- enableAllProjectMcpServers: true,
2798
+ enabledMcpjsonServers,
2799
+ permissions: {
2800
+ ...existing.permissions,
2801
+ allow: mergedAllow
2802
+ },
2112
2803
  env: {
2113
2804
  ...existing.env,
2114
2805
  [`${prefix}_GITHUB_APP_ID`]: appSlug,
@@ -2120,49 +2811,27 @@ async function writeSettingsLocal({
2120
2811
  };
2121
2812
  await writeFile(filePath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
2122
2813
  }
2123
- function deriveProjectSlug(cwd = process.cwd()) {
2124
- try {
2125
- const origin = execSync("git remote get-url origin", {
2126
- cwd,
2127
- stdio: ["pipe", "pipe", "pipe"]
2128
- }).toString().trim();
2129
- const match = origin.match(/[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
2130
- if (match) {
2131
- return match[1].replace("/", "-");
2132
- }
2133
- } catch {
2134
- }
2135
- return basename(cwd);
2136
- }
2137
- function getStatePath(projectSlug, agentName) {
2138
- return join(
2139
- homedir(),
2140
- ".config",
2141
- "moltnet",
2142
- projectSlug,
2143
- `legreffier-init.${agentName}.state.json`
2144
- );
2814
+ function getStatePath(configDir) {
2815
+ return join(configDir, "legreffier-init.state.json");
2145
2816
  }
2146
- async function readState(projectSlug, agentName) {
2817
+ async function readState(configDir) {
2147
2818
  try {
2148
- const raw = await readFile(getStatePath(projectSlug, agentName), "utf-8");
2819
+ const raw = await readFile(getStatePath(configDir), "utf-8");
2149
2820
  return JSON.parse(raw);
2150
2821
  } catch {
2151
2822
  return null;
2152
2823
  }
2153
2824
  }
2154
- async function writeState(state, projectSlug, agentName) {
2155
- const path = getStatePath(projectSlug, agentName);
2156
- await mkdir(join(homedir(), ".config", "moltnet", projectSlug), {
2157
- recursive: true
2158
- });
2825
+ async function writeState(state, configDir) {
2826
+ await mkdir(configDir, { recursive: true });
2827
+ const path = getStatePath(configDir);
2159
2828
  await writeFile(path, JSON.stringify(state, null, 2) + "\n", {
2160
2829
  mode: 384
2161
2830
  });
2162
2831
  }
2163
- async function clearState(projectSlug, agentName) {
2832
+ async function clearState(configDir) {
2164
2833
  try {
2165
- await rm(getStatePath(projectSlug, agentName));
2834
+ await rm(getStatePath(configDir));
2166
2835
  } catch {
2167
2836
  }
2168
2837
  }
@@ -2172,6 +2841,7 @@ async function runAgentSetupPhase(opts) {
2172
2841
  repoDir,
2173
2842
  configDir,
2174
2843
  agentName,
2844
+ agentTypes,
2175
2845
  publicKey,
2176
2846
  fingerprint,
2177
2847
  appSlug,
@@ -2180,7 +2850,6 @@ async function runAgentSetupPhase(opts) {
2180
2850
  identityId,
2181
2851
  clientId,
2182
2852
  clientSecret,
2183
- projectSlug,
2184
2853
  dispatch
2185
2854
  } = opts;
2186
2855
  dispatch({ type: "phase", phase: "agent_setup" });
@@ -2230,7 +2899,7 @@ async function runAgentSetupPhase(opts) {
2230
2899
  );
2231
2900
  }
2232
2901
  dispatch({ type: "step", key: "skills", status: "running" });
2233
- await downloadSkills(repoDir);
2902
+ await downloadSkills(repoDir, agentTypes);
2234
2903
  dispatch({ type: "step", key: "skills", status: "done" });
2235
2904
  dispatch({ type: "step", key: "settings", status: "running" });
2236
2905
  await writeSettingsLocal({
@@ -2243,7 +2912,7 @@ async function runAgentSetupPhase(opts) {
2243
2912
  clientSecret
2244
2913
  });
2245
2914
  dispatch({ type: "step", key: "settings", status: "done" });
2246
- await clearState(projectSlug, agentName);
2915
+ await clearState(configDir);
2247
2916
  }
2248
2917
  async function exchangeManifestCode(code) {
2249
2918
  const res = await fetch(
@@ -2326,10 +2995,9 @@ async function lookupBotUser(appSlug, { maxRetries = 5, baseDelayMs = 2e3 } = {}
2326
2995
  }
2327
2996
  throw new Error(`GitHub user lookup failed for app "${appSlug}"`);
2328
2997
  }
2329
- async function writePem(pem, appSlug, projectSlug) {
2330
- const dir2 = join(homedir(), ".config", "moltnet", projectSlug);
2331
- await mkdir(dir2, { recursive: true });
2332
- const path = join(dir2, `${appSlug}.pem`);
2998
+ async function writePem(pem, appSlug, configDir) {
2999
+ await mkdir(configDir, { recursive: true });
3000
+ const path = join(configDir, `${appSlug}.pem`);
2333
3001
  await writeFile(path, pem, { mode: 384 });
2334
3002
  await chmod(path, 384);
2335
3003
  return path;
@@ -2361,7 +3029,6 @@ async function runGithubAppPhase(opts) {
2361
3029
  apiUrl: apiUrl2,
2362
3030
  agentName,
2363
3031
  configDir,
2364
- projectSlug,
2365
3032
  publicKey,
2366
3033
  privateKey,
2367
3034
  fingerprint,
@@ -2370,7 +3037,7 @@ async function runGithubAppPhase(opts) {
2370
3037
  dispatch
2371
3038
  } = opts;
2372
3039
  const existingConfig = await readConfig(configDir);
2373
- const existingState = await readState(projectSlug, agentName);
3040
+ const existingState = await readState(configDir);
2374
3041
  if (existingConfig?.github?.app_id) {
2375
3042
  dispatch({ type: "step", key: "githubApp", status: "skipped" });
2376
3043
  dispatch({
@@ -2385,13 +3052,7 @@ async function runGithubAppPhase(opts) {
2385
3052
  };
2386
3053
  }
2387
3054
  if (existingState?.appSlug && existingState?.appId) {
2388
- const pemPath2 = join(
2389
- homedir(),
2390
- ".config",
2391
- "moltnet",
2392
- projectSlug,
2393
- `${existingState.appSlug}.pem`
2394
- );
3055
+ const pemPath2 = join(configDir, `${existingState.appSlug}.pem`);
2395
3056
  dispatch({ type: "step", key: "githubApp", status: "skipped" });
2396
3057
  dispatch({ type: "appSlug", appSlug: existingState.appSlug });
2397
3058
  return {
@@ -2416,7 +3077,7 @@ async function runGithubAppPhase(opts) {
2416
3077
  }
2417
3078
  const ghCreds = await exchangeManifestCode(codeResult.githubCode);
2418
3079
  dispatch({ type: "appSlug", appSlug: ghCreds.appSlug });
2419
- const pemPath = await writePem(ghCreds.pem, ghCreds.appSlug, projectSlug);
3080
+ const pemPath = await writePem(ghCreds.pem, ghCreds.appSlug, configDir);
2420
3081
  await writeState(
2421
3082
  {
2422
3083
  workflowId,
@@ -2428,8 +3089,7 @@ async function runGithubAppPhase(opts) {
2428
3089
  appId: ghCreds.appId,
2429
3090
  appSlug: ghCreds.appSlug
2430
3091
  },
2431
- projectSlug,
2432
- agentName
3092
+ configDir
2433
3093
  );
2434
3094
  dispatch({ type: "step", key: "githubApp", status: "done" });
2435
3095
  return {
@@ -2468,10 +3128,187 @@ async function runGitSetupPhase(opts) {
2468
3128
  );
2469
3129
  dispatch({ type: "step", key: "gitSetup", status: "done" });
2470
3130
  }
3131
+ etc.sha512Sync = (...m) => {
3132
+ const hash = createHash("sha512");
3133
+ m.forEach((msg) => hash.update(msg));
3134
+ return hash.digest();
3135
+ };
3136
+ const DOMAIN_PREFIX = "moltnet:v1";
3137
+ function buildSigningBytes(message, nonce) {
3138
+ const msgHash = createHash("sha256").update(Buffer.from(message, "utf-8")).digest();
3139
+ const nonceBytes = Buffer.from(nonce, "utf-8");
3140
+ const prefix = Buffer.from(DOMAIN_PREFIX, "utf-8");
3141
+ const buf = Buffer.alloc(
3142
+ prefix.length + 4 + msgHash.length + 4 + nonceBytes.length
3143
+ );
3144
+ let offset = 0;
3145
+ prefix.copy(buf, offset);
3146
+ offset += prefix.length;
3147
+ buf.writeUInt32BE(msgHash.length, offset);
3148
+ offset += 4;
3149
+ msgHash.copy(buf, offset);
3150
+ offset += msgHash.length;
3151
+ buf.writeUInt32BE(nonceBytes.length, offset);
3152
+ offset += 4;
3153
+ nonceBytes.copy(buf, offset);
3154
+ return new Uint8Array(buf);
3155
+ }
3156
+ const cryptoService = {
3157
+ /**
3158
+ * Generate a new Ed25519 keypair
3159
+ */
3160
+ async generateKeyPair() {
3161
+ const privateKeyBytes = utils.randomPrivateKey();
3162
+ const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
3163
+ const privateKey = Buffer.from(privateKeyBytes).toString("base64");
3164
+ const publicKey = `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
3165
+ const fingerprint = this.generateFingerprint(publicKeyBytes);
3166
+ return { publicKey, privateKey, fingerprint };
3167
+ },
3168
+ /**
3169
+ * Generate human-readable fingerprint from public key
3170
+ * Format: A1B2-C3D4-E5F6-G7H8 (first 16 hex chars of SHA256)
3171
+ */
3172
+ generateFingerprint(publicKeyBytes) {
3173
+ const hash = createHash("sha256").update(publicKeyBytes).digest("hex");
3174
+ const segments = hash.slice(0, 16).toUpperCase().match(/.{4}/g) ?? [];
3175
+ return segments.join("-");
3176
+ },
3177
+ /**
3178
+ * Parse public key from string format
3179
+ */
3180
+ parsePublicKey(publicKey) {
3181
+ const base64 = publicKey.replace(/^ed25519:/, "");
3182
+ return new Uint8Array(Buffer.from(base64, "base64"));
3183
+ },
3184
+ /**
3185
+ * Sign a message with private key
3186
+ */
3187
+ async sign(message, privateKeyBase64) {
3188
+ const privateKeyBytes = new Uint8Array(
3189
+ Buffer.from(privateKeyBase64, "base64")
3190
+ );
3191
+ const messageBytes = new TextEncoder().encode(message);
3192
+ const signature = await signAsync(messageBytes, privateKeyBytes);
3193
+ return Buffer.from(signature).toString("base64");
3194
+ },
3195
+ /**
3196
+ * Verify a signature against a message and public key
3197
+ */
3198
+ async verify(message, signature, publicKey) {
3199
+ try {
3200
+ const publicKeyBytes = this.parsePublicKey(publicKey);
3201
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
3202
+ const messageBytes = new TextEncoder().encode(message);
3203
+ return await verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
3204
+ } catch {
3205
+ return false;
3206
+ }
3207
+ },
3208
+ /**
3209
+ * Sign a (message, nonce) pair using deterministic pre-hash.
3210
+ * Uses buildSigningBytes for domain separation and canonical serialization.
3211
+ */
3212
+ async signWithNonce(message, nonce, privateKeyBase64) {
3213
+ const privateKeyBytes = new Uint8Array(
3214
+ Buffer.from(privateKeyBase64, "base64")
3215
+ );
3216
+ const signingBytes = buildSigningBytes(message, nonce);
3217
+ const signature = await signAsync(signingBytes, privateKeyBytes);
3218
+ return Buffer.from(signature).toString("base64");
3219
+ },
3220
+ /**
3221
+ * Verify a signature produced by signWithNonce.
3222
+ */
3223
+ async verifyWithNonce(message, nonce, signature, publicKey) {
3224
+ try {
3225
+ const publicKeyBytes = this.parsePublicKey(publicKey);
3226
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
3227
+ const signingBytes = buildSigningBytes(message, nonce);
3228
+ return await verifyAsync(signatureBytes, signingBytes, publicKeyBytes);
3229
+ } catch {
3230
+ return false;
3231
+ }
3232
+ },
3233
+ /**
3234
+ * Create a signed message object
3235
+ */
3236
+ async createSignedMessage(message, privateKeyBase64, publicKey) {
3237
+ const signature = await this.sign(message, privateKeyBase64);
3238
+ return { message, signature, publicKey };
3239
+ },
3240
+ /**
3241
+ * Verify a signed message object
3242
+ */
3243
+ async verifySignedMessage(signedMessage) {
3244
+ return this.verify(
3245
+ signedMessage.message,
3246
+ signedMessage.signature,
3247
+ signedMessage.publicKey
3248
+ );
3249
+ },
3250
+ /**
3251
+ * Generate a random challenge for authentication
3252
+ */
3253
+ generateChallenge() {
3254
+ return `moltnet:challenge:${randomBytes$1(32).toString("hex")}:${Date.now()}`;
3255
+ },
3256
+ /**
3257
+ * Derive public key from private key
3258
+ */
3259
+ async derivePublicKey(privateKeyBase64) {
3260
+ const privateKeyBytes = new Uint8Array(
3261
+ Buffer.from(privateKeyBase64, "base64")
3262
+ );
3263
+ const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
3264
+ return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
3265
+ },
3266
+ /**
3267
+ * Get fingerprint from public key string
3268
+ */
3269
+ getFingerprintFromPublicKey(publicKey) {
3270
+ const publicKeyBytes = this.parsePublicKey(publicKey);
3271
+ return this.generateFingerprint(publicKeyBytes);
3272
+ },
3273
+ /**
3274
+ * Create a proof of identity ownership (for DCR metadata)
3275
+ */
3276
+ async createIdentityProof(identityId, privateKeyBase64) {
3277
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3278
+ const message = `moltnet:register:${identityId}:${timestamp}`;
3279
+ const signature = await this.sign(message, privateKeyBase64);
3280
+ return { message, signature, timestamp };
3281
+ },
3282
+ /**
3283
+ * Verify an identity proof
3284
+ */
3285
+ async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
3286
+ const isValid = await this.verify(
3287
+ proof.message,
3288
+ proof.signature,
3289
+ publicKey
3290
+ );
3291
+ if (!isValid) return false;
3292
+ const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
3293
+ if (!proof.message.startsWith(expectedPrefix)) return false;
3294
+ const proofTime = new Date(proof.timestamp).getTime();
3295
+ const now = Date.now();
3296
+ const fiveMinutes = 5 * 60 * 1e3;
3297
+ if (now - proofTime > fiveMinutes) return false;
3298
+ return true;
3299
+ }
3300
+ };
3301
+ if (!etc.sha512Sync) {
3302
+ etc.sha512Sync = (...m) => {
3303
+ const hash = createHash("sha512");
3304
+ m.forEach((msg) => hash.update(msg));
3305
+ return hash.digest();
3306
+ };
3307
+ }
2471
3308
  async function runIdentityPhase(opts) {
2472
- const { apiUrl: apiUrl2, agentName, configDir, projectSlug, dispatch } = opts;
3309
+ const { apiUrl: apiUrl2, agentName, configDir, dispatch } = opts;
2473
3310
  const existingConfig = await readConfig(configDir);
2474
- const existingState = await readState(projectSlug, agentName);
3311
+ const existingState = await readState(configDir);
2475
3312
  if (existingConfig?.keys?.public_key && existingConfig?.oauth2?.client_id) {
2476
3313
  dispatch({ type: "step", key: "keypair", status: "skipped" });
2477
3314
  dispatch({ type: "step", key: "register", status: "skipped" });
@@ -2527,7 +3364,7 @@ async function runIdentityPhase(opts) {
2527
3364
  skipped: true
2528
3365
  };
2529
3366
  }
2530
- await clearState(projectSlug, agentName);
3367
+ await clearState(configDir);
2531
3368
  }
2532
3369
  const available = await checkAppNameAvailable(agentName);
2533
3370
  if (!available) {
@@ -2554,8 +3391,7 @@ async function runIdentityPhase(opts) {
2554
3391
  agentName,
2555
3392
  phase: "awaiting_github"
2556
3393
  },
2557
- projectSlug,
2558
- agentName
3394
+ configDir
2559
3395
  );
2560
3396
  await writeConfig(
2561
3397
  {
@@ -2614,6 +3450,7 @@ async function runInstallationPhase(opts) {
2614
3450
  clientSecret: result.clientSecret ?? ""
2615
3451
  };
2616
3452
  }
3453
+ const SUPPORTED_AGENTS = ["claude"];
2617
3454
  const AGENTS = [
2618
3455
  {
2619
3456
  id: "claude",
@@ -2643,7 +3480,7 @@ function AgentSelect({ onSelect }) {
2643
3480
  setSelected((i) => i < AGENTS.length - 1 ? i + 1 : i);
2644
3481
  } else if (key.return) {
2645
3482
  const agent2 = AGENTS[selected];
2646
- if (agent2 && agent2.available) {
3483
+ if (agent2?.available && SUPPORTED_AGENTS.includes(agent2.id)) {
2647
3484
  onSelect(agent2.id);
2648
3485
  }
2649
3486
  }
@@ -2929,19 +3766,16 @@ function InitApp({
2929
3766
  void (async () => {
2930
3767
  try {
2931
3768
  const configDir = join(dir2, ".moltnet", name2);
2932
- const projectSlug = deriveProjectSlug(dir2);
2933
3769
  const identity = await runIdentityPhase({
2934
3770
  apiUrl: apiUrl2,
2935
3771
  agentName: name2,
2936
3772
  configDir,
2937
- projectSlug,
2938
3773
  dispatch
2939
3774
  });
2940
3775
  const githubApp = await runGithubAppPhase({
2941
3776
  apiUrl: apiUrl2,
2942
3777
  agentName: name2,
2943
3778
  configDir,
2944
- projectSlug,
2945
3779
  publicKey: identity.publicKey,
2946
3780
  privateKey: identity.privateKey,
2947
3781
  fingerprint: identity.fingerprint,
@@ -2967,6 +3801,7 @@ function InitApp({
2967
3801
  repoDir: dir2,
2968
3802
  configDir,
2969
3803
  agentName: name2,
3804
+ agentTypes: selectedAgent ? [selectedAgent] : [],
2970
3805
  publicKey: identity.publicKey,
2971
3806
  fingerprint: identity.fingerprint,
2972
3807
  appSlug: githubApp.appSlug,
@@ -2975,7 +3810,6 @@ function InitApp({
2975
3810
  identityId: installation.identityId,
2976
3811
  clientId: installation.clientId || identity.clientId,
2977
3812
  clientSecret: installation.clientSecret || identity.clientSecret,
2978
- projectSlug,
2979
3813
  dispatch
2980
3814
  });
2981
3815
  const mcpUrl = apiUrl2.replace("://api.", "://mcp.") + "/mcp";
@@ -3031,7 +3865,6 @@ function InitApp({
3031
3865
  }
3032
3866
  );
3033
3867
  }
3034
- const SUPPORTED_AGENTS = ["claude"];
3035
3868
  const { values } = parseArgs({
3036
3869
  args: process.argv.slice(2),
3037
3870
  options: {
@@ -3051,6 +3884,14 @@ if (!name) {
3051
3884
  );
3052
3885
  process.exit(1);
3053
3886
  }
3887
+ const AGENT_NAME_RE = /^[a-z0-9][a-z0-9-]{0,37}[a-z0-9]$/;
3888
+ if (!AGENT_NAME_RE.test(name)) {
3889
+ process.stderr.write(
3890
+ `Invalid agent name: "${name}". Must be 2-39 lowercase alphanumeric characters or hyphens, starting and ending with a letter or digit.
3891
+ `
3892
+ );
3893
+ process.exit(1);
3894
+ }
3054
3895
  if (agentFlag && !SUPPORTED_AGENTS.includes(agentFlag)) {
3055
3896
  process.stderr.write(
3056
3897
  `Unsupported agent: ${agentFlag}. Supported: ${SUPPORTED_AGENTS.join(", ")}