@themoltnet/pi-extension 0.35.2 → 0.35.3

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 +166 -76
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -42,6 +42,59 @@ var __copyProps = (to, from, except, desc) => {
42
42
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
43
43
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
44
44
  //#endregion
45
+ //#region ../sdk/src/errors.ts
46
+ var MoltNetError = class extends Error {
47
+ code;
48
+ statusCode;
49
+ detail;
50
+ /**
51
+ * Populated when the server returned a `VALIDATION_FAILED` problem
52
+ * (status 400) with field-level errors. Empty / undefined for every
53
+ * other problem kind. Proposer scripts surface these to operators so
54
+ * they don't have to re-run with curl to see what was rejected.
55
+ */
56
+ validationErrors;
57
+ constructor(message, options) {
58
+ super(message);
59
+ this.name = "MoltNetError";
60
+ this.code = options.code;
61
+ this.statusCode = options.statusCode;
62
+ this.detail = options.detail;
63
+ this.validationErrors = options.validationErrors;
64
+ }
65
+ };
66
+ var NetworkError = class extends MoltNetError {
67
+ constructor(message, options) {
68
+ super(message, {
69
+ code: "NETWORK_ERROR",
70
+ detail: options?.detail
71
+ });
72
+ this.name = "NetworkError";
73
+ }
74
+ };
75
+ var AuthenticationError = class extends MoltNetError {
76
+ constructor(message, options) {
77
+ super(message, {
78
+ code: "AUTH_FAILED",
79
+ statusCode: options?.statusCode,
80
+ detail: options?.detail
81
+ });
82
+ this.name = "AuthenticationError";
83
+ }
84
+ };
85
+ function problemToError(problem, statusCode) {
86
+ const title = problem.title ?? "Request failed";
87
+ const message = problem.detail ? `${title}: ${problem.detail}` : title;
88
+ const rawErrors = problem.errors;
89
+ const validationErrors = Array.isArray(rawErrors) ? rawErrors.filter((e) => typeof e === "object" && e !== null && typeof e.field === "string" && typeof e.message === "string") : void 0;
90
+ return new MoltNetError(message, {
91
+ code: problem.type ?? problem.code ?? "UNKNOWN",
92
+ statusCode,
93
+ detail: problem.detail,
94
+ validationErrors
95
+ });
96
+ }
97
+ //#endregion
45
98
  //#region ../api-client/src/generated/core/bodySerializer.gen.ts
46
99
  var jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
47
100
  Object.entries({
@@ -754,7 +807,7 @@ var rotateAgentKey = (options) => (options.client ?? client).post({
754
807
  ...options
755
808
  });
756
809
  /**
757
- * Get the authenticated agent identity (requires bearer token).
810
+ * Get the authenticated caller identity and context. Works for both agents (identity plus, under agent-key auth, the credential binding) and humans, via bearer, session, or cookie auth.
758
811
  */
759
812
  var getWhoami = (options) => (options?.client ?? client).get({
760
813
  security: [
@@ -2934,63 +2987,11 @@ function createRateLimitFetch(options) {
2934
2987
  });
2935
2988
  }
2936
2989
  //#endregion
2937
- //#region ../sdk/src/errors.ts
2938
- var MoltNetError = class extends Error {
2939
- code;
2940
- statusCode;
2941
- detail;
2942
- /**
2943
- * Populated when the server returned a `VALIDATION_FAILED` problem
2944
- * (status 400) with field-level errors. Empty / undefined for every
2945
- * other problem kind. Proposer scripts surface these to operators so
2946
- * they don't have to re-run with curl to see what was rejected.
2947
- */
2948
- validationErrors;
2949
- constructor(message, options) {
2950
- super(message);
2951
- this.name = "MoltNetError";
2952
- this.code = options.code;
2953
- this.statusCode = options.statusCode;
2954
- this.detail = options.detail;
2955
- this.validationErrors = options.validationErrors;
2956
- }
2957
- };
2958
- var NetworkError = class extends MoltNetError {
2959
- constructor(message, options) {
2960
- super(message, {
2961
- code: "NETWORK_ERROR",
2962
- detail: options?.detail
2963
- });
2964
- this.name = "NetworkError";
2965
- }
2966
- };
2967
- var AuthenticationError = class extends MoltNetError {
2968
- constructor(message, options) {
2969
- super(message, {
2970
- code: "AUTH_FAILED",
2971
- statusCode: options?.statusCode,
2972
- detail: options?.detail
2973
- });
2974
- this.name = "AuthenticationError";
2975
- }
2976
- };
2977
- function problemToError(problem, statusCode) {
2978
- const title = problem.title ?? "Request failed";
2979
- const message = problem.detail ? `${title}: ${problem.detail}` : title;
2980
- const rawErrors = problem.errors;
2981
- const validationErrors = Array.isArray(rawErrors) ? rawErrors.filter((e) => typeof e === "object" && e !== null && typeof e.field === "string" && typeof e.message === "string") : void 0;
2982
- return new MoltNetError(message, {
2983
- code: problem.type ?? problem.code ?? "UNKNOWN",
2984
- statusCode,
2985
- detail: problem.detail,
2986
- validationErrors
2987
- });
2988
- }
2989
- //#endregion
2990
2990
  //#region ../sdk/src/agent-context.ts
2991
2991
  function unwrapResult(result) {
2992
2992
  if (result.error !== void 0 && result.error !== null) {
2993
2993
  const error = result.error;
2994
+ if (error instanceof MoltNetError) throw error;
2994
2995
  if (isProblemDetails(error)) throw problemToError(error, error.status);
2995
2996
  if (error instanceof Error && result.response === void 0) {
2996
2997
  const networkError = new NetworkError(error.message, { detail: error.cause ? stringifyUnknown(error.cause) : void 0 });
@@ -3113,16 +3114,25 @@ function createAgentKeysNamespace(context) {
3113
3114
  };
3114
3115
  }
3115
3116
  //#endregion
3117
+ //#region ../sdk/src/namespaces/whoami.ts
3118
+ /**
3119
+ * Build a `whoami()` accessor bound to an authenticated context. Returns the
3120
+ * caller's identity and context: `subjectType`, `currentTeamId`, and, for an
3121
+ * agent authenticated via an agent key, its `credentialBinding`.
3122
+ */
3123
+ function createWhoami(context) {
3124
+ const { client, auth } = context;
3125
+ return async () => unwrapResult(await getWhoami({
3126
+ client,
3127
+ auth
3128
+ }));
3129
+ }
3130
+ //#endregion
3116
3131
  //#region ../sdk/src/namespaces/agents.ts
3117
3132
  function createAgentsNamespace(context) {
3118
- const { client, auth } = context;
3133
+ const { client } = context;
3119
3134
  return {
3120
- async whoami() {
3121
- return unwrapResult(await getWhoami({
3122
- client,
3123
- auth
3124
- }));
3125
- },
3135
+ whoami: createWhoami(context),
3126
3136
  async lookup(fingerprint) {
3127
3137
  return unwrapResult(await getAgentProfile({
3128
3138
  client,
@@ -15764,22 +15774,37 @@ function createAgent(options) {
15764
15774
  runtimeSlots: createRuntimeSlotsNamespace(context),
15765
15775
  runtimeSessions: createRuntimeSessionsNamespace(context),
15766
15776
  client,
15767
- getToken: () => tokenManager.getToken()
15777
+ getToken: () => {
15778
+ if (tokenManager) return tokenManager.getToken();
15779
+ if (auth) return auth();
15780
+ return Promise.reject(new MoltNetError("No token source configured", { code: "NO_TOKEN_SOURCE" }));
15781
+ }
15768
15782
  };
15769
15783
  }
15770
15784
  //#endregion
15771
15785
  //#region ../sdk/src/config.ts
15772
15786
  /**
15773
15787
  * Read MoltNet credentials from environment variables.
15774
- * Reads MOLTNET_CLIENT_ID, MOLTNET_CLIENT_SECRET, and MOLTNET_API_URL.
15788
+ * Reads MOLTNET_CLIENT_ID, MOLTNET_CLIENT_SECRET, MOLTNET_API_URL, and
15789
+ * MOLTNET_AGENT_KEY.
15775
15790
  */
15776
15791
  function readEnvCredentials() {
15777
15792
  return {
15778
15793
  clientId: process.env.MOLTNET_CLIENT_ID,
15779
15794
  clientSecret: process.env.MOLTNET_CLIENT_SECRET,
15780
- apiUrl: process.env.MOLTNET_API_URL
15795
+ apiUrl: process.env.MOLTNET_API_URL,
15796
+ agentKey: process.env.MOLTNET_AGENT_KEY
15781
15797
  };
15782
15798
  }
15799
+ /**
15800
+ * Resolve the API base URL from an ordered list of candidates, falling back to
15801
+ * the hosted default, with any trailing slash stripped. Candidates are tried in
15802
+ * order — pass them highest-precedence first (typically explicit option, then
15803
+ * env, then config file) so every caller shares one precedence rule.
15804
+ */
15805
+ function normalizeApiUrl(...candidates) {
15806
+ return (candidates.find((c) => c) ?? "https://api.themolt.net").replace(/\/$/, "");
15807
+ }
15783
15808
  //#endregion
15784
15809
  //#region ../sdk/src/credentials.ts
15785
15810
  function getConfigDir() {
@@ -15843,6 +15868,32 @@ function createRetryFetch(tokenManager, options) {
15843
15868
  return doFetch(init);
15844
15869
  };
15845
15870
  }
15871
+ /**
15872
+ * Create a fetch wrapper for agent-key (static-bearer) authentication.
15873
+ *
15874
+ * A static key cannot be refreshed, so there is no token-invalidation/replay
15875
+ * (that half of {@link createRetryFetch} is intentionally omitted). What remains
15876
+ * is orthogonal to token refresh and still matters for a long-running client:
15877
+ *
15878
+ * - **429**: delegates to `createRateLimitFetch` (Retry-After / backoff), unless
15879
+ * `retry` is `false`.
15880
+ * - **401**: the key was rejected (revoked, expired, or not authorized for the
15881
+ * requested team). Rather than silently returning a bare 401 on every call,
15882
+ * throw an actionable {@link AuthenticationError}. The key value is never
15883
+ * included in the message.
15884
+ */
15885
+ function createAgentKeyFetch(retry) {
15886
+ const rateLimitFetch = retry === false ? fetch : createRateLimitFetch({
15887
+ maxRetries: retry?.maxRateLimitRetries,
15888
+ baseDelayMs: retry?.baseDelayMs,
15889
+ maxDelayMs: retry?.maxDelayMs
15890
+ });
15891
+ return async (input, init) => {
15892
+ const response = await rateLimitFetch(input, init);
15893
+ if (response.status === 401) throw new AuthenticationError("agent key rejected (401): the key is revoked, expired, or not authorized for the requested team — re-provision the key.", { statusCode: 401 });
15894
+ return response;
15895
+ };
15896
+ }
15846
15897
  //#endregion
15847
15898
  //#region ../sdk/src/token.ts
15848
15899
  var TokenManager = class {
@@ -15902,37 +15953,76 @@ var TokenManager = class {
15902
15953
  };
15903
15954
  //#endregion
15904
15955
  //#region ../sdk/src/connect.ts
15905
- var DEFAULT_API_URL = "https://api.themolt.net";
15906
- async function resolveCredentials(options) {
15956
+ async function resolveConnection(options) {
15957
+ const env = readEnvCredentials();
15958
+ const explicitAgentKey = options.agentKey?.trim();
15959
+ if (explicitAgentKey) {
15960
+ const config = await readConfig(options.configDir);
15961
+ return {
15962
+ mode: "agentKey",
15963
+ agentKey: explicitAgentKey,
15964
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl, config?.endpoints?.api)
15965
+ };
15966
+ }
15907
15967
  if (options.clientId && options.clientSecret) return {
15968
+ mode: "oauth2",
15908
15969
  clientId: options.clientId,
15909
15970
  clientSecret: options.clientSecret,
15910
- apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "")
15971
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
15911
15972
  };
15912
- const env = readEnvCredentials();
15973
+ const envAgentKey = env.agentKey?.trim();
15974
+ if (envAgentKey) {
15975
+ const config = await readConfig(options.configDir);
15976
+ return {
15977
+ mode: "agentKey",
15978
+ agentKey: envAgentKey,
15979
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl, config?.endpoints?.api)
15980
+ };
15981
+ }
15913
15982
  if (env.clientId && env.clientSecret) return {
15983
+ mode: "oauth2",
15914
15984
  clientId: env.clientId,
15915
15985
  clientSecret: env.clientSecret,
15916
- apiUrl: (env.apiUrl ?? options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "")
15986
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
15917
15987
  };
15918
15988
  const config = await readConfig(options.configDir);
15919
15989
  if (config?.oauth2?.client_id && config?.oauth2?.client_secret) return {
15990
+ mode: "oauth2",
15920
15991
  clientId: config.oauth2.client_id,
15921
15992
  clientSecret: config.oauth2.client_secret,
15922
- apiUrl: (options.apiUrl ?? config.endpoints?.api ?? DEFAULT_API_URL).replace(/\/$/, "")
15993
+ apiUrl: normalizeApiUrl(options.apiUrl, config.endpoints?.api)
15923
15994
  };
15924
- throw new MoltNetError("No credentials found. Provide clientId/clientSecret, set MOLTNET_CLIENT_ID/MOLTNET_CLIENT_SECRET env vars, or run `moltnet register` first.", { code: "NO_CREDENTIALS" });
15995
+ throw new MoltNetError("No credentials found. Provide an agentKey / MOLTNET_AGENT_KEY, clientId/clientSecret, set MOLTNET_CLIENT_ID/MOLTNET_CLIENT_SECRET, or run `moltnet register` first.", { code: "NO_CREDENTIALS" });
15925
15996
  }
15926
15997
  /**
15927
15998
  * Connect to MoltNet and return an authenticated Agent facade.
15928
15999
  *
15929
- * Credential resolution order:
15930
- * 1. Explicit `clientId` / `clientSecret` in options
15931
- * 2. `MOLTNET_CLIENT_ID` / `MOLTNET_CLIENT_SECRET` environment variables
15932
- * 3. Config file (`~/.config/moltnet/moltnet.json`)
16000
+ * Credential resolution, highest precedence first. Explicit in-code options —
16001
+ * of either kind always win over the environment and config file:
16002
+ * 1. Explicit `agentKey` option agent-key mode (static bearer)
16003
+ * 2. Explicit `clientId` / `clientSecret` → OAuth2 client-credentials
16004
+ * 3. `MOLTNET_AGENT_KEY` env → agent-key mode
16005
+ * 4. `MOLTNET_CLIENT_ID` / `MOLTNET_CLIENT_SECRET` env → OAuth2
16006
+ * 5. Config file (`~/.config/moltnet/moltnet.json`) → OAuth2
16007
+ *
16008
+ * In agent-key mode the key is sent directly as a bearer token — no OAuth2
16009
+ * round-trip — and 429 backoff still applies; a rejected key surfaces an
16010
+ * `AuthenticationError`.
15933
16011
  */
15934
16012
  async function connect(options = {}) {
15935
- const creds = await resolveCredentials(options);
16013
+ const resolved = await resolveConnection(options);
16014
+ if (resolved.mode === "agentKey") {
16015
+ const client = createClient({
16016
+ baseUrl: resolved.apiUrl,
16017
+ fetch: createAgentKeyFetch(options.retry)
16018
+ });
16019
+ const auth = () => Promise.resolve(resolved.agentKey);
16020
+ return createAgent({
16021
+ client,
16022
+ auth
16023
+ });
16024
+ }
16025
+ const creds = resolved;
15936
16026
  const autoToken = options.autoToken ?? true;
15937
16027
  const tokenManager = new TokenManager({
15938
16028
  clientId: creds.clientId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-extension",
3
- "version": "0.35.2",
3
+ "version": "0.35.3",
4
4
  "type": "module",
5
5
  "description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
6
6
  "keywords": [
@@ -36,8 +36,8 @@
36
36
  "@earendil-works/gondolin": "^0.9.1",
37
37
  "@opentelemetry/api": "^1.9.0",
38
38
  "typebox": "^1.2.8",
39
- "@themoltnet/agent-runtime": "0.36.2",
40
- "@themoltnet/sdk": "0.123.0"
39
+ "@themoltnet/agent-runtime": "0.36.3",
40
+ "@themoltnet/sdk": "0.124.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@earendil-works/pi-coding-agent": ">=0.74.0",