@themoltnet/pi-extension 0.35.1 → 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 +183 -79
  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 });
@@ -3037,6 +3038,21 @@ function unwrapRequired(result, message, code) {
3037
3038
  return result.data;
3038
3039
  }
3039
3040
  //#endregion
3041
+ //#region ../sdk/src/namespaces/query.ts
3042
+ /**
3043
+ * Remove `undefined`-valued keys from a query object before it is serialized.
3044
+ *
3045
+ * Returns `undefined` when no defined keys remain, so an all-`undefined` query
3046
+ * (`{ agentId: undefined }`) and an omitted query (`undefined`) serialize
3047
+ * identically — both send no query params — instead of the former collapsing to
3048
+ * an empty `{}` that still reaches the client.
3049
+ */
3050
+ function stripUndefinedQuery(query) {
3051
+ if (!query) return;
3052
+ const entries = Object.entries(query).filter(([, value]) => value !== void 0);
3053
+ return entries.length ? Object.fromEntries(entries) : void 0;
3054
+ }
3055
+ //#endregion
3040
3056
  //#region ../sdk/src/namespaces/team-headers.ts
3041
3057
  /**
3042
3058
  * Build the team header from an optional option, or `undefined` when no team
@@ -3063,7 +3079,7 @@ function createAgentKeysNamespace(context) {
3063
3079
  client,
3064
3080
  auth,
3065
3081
  headers: requiredTeamHeaders(options),
3066
- query
3082
+ query: stripUndefinedQuery(query)
3067
3083
  }));
3068
3084
  },
3069
3085
  async create(body, options) {
@@ -3098,16 +3114,25 @@ function createAgentKeysNamespace(context) {
3098
3114
  };
3099
3115
  }
3100
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
3101
3131
  //#region ../sdk/src/namespaces/agents.ts
3102
3132
  function createAgentsNamespace(context) {
3103
- const { client, auth } = context;
3133
+ const { client } = context;
3104
3134
  return {
3105
- async whoami() {
3106
- return unwrapResult(await getWhoami({
3107
- client,
3108
- auth
3109
- }));
3110
- },
3135
+ whoami: createWhoami(context),
3111
3136
  async lookup(fingerprint) {
3112
3137
  return unwrapResult(await getAgentProfile({
3113
3138
  client,
@@ -5433,12 +5458,11 @@ function createRuntimeSlotsNamespace(context) {
5433
5458
  }
5434
5459
  },
5435
5460
  async list(query, options) {
5436
- const filteredQuery = Object.fromEntries(Object.entries(query).filter(([, value]) => value !== void 0));
5437
5461
  return unwrapResult(await listRuntimeSlots({
5438
5462
  auth,
5439
5463
  client,
5440
5464
  headers: requiredTeamHeaders(options),
5441
- query: filteredQuery
5465
+ query: stripUndefinedQuery(query)
5442
5466
  })).items;
5443
5467
  }
5444
5468
  };
@@ -15750,22 +15774,37 @@ function createAgent(options) {
15750
15774
  runtimeSlots: createRuntimeSlotsNamespace(context),
15751
15775
  runtimeSessions: createRuntimeSessionsNamespace(context),
15752
15776
  client,
15753
- 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
+ }
15754
15782
  };
15755
15783
  }
15756
15784
  //#endregion
15757
15785
  //#region ../sdk/src/config.ts
15758
15786
  /**
15759
15787
  * Read MoltNet credentials from environment variables.
15760
- * 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.
15761
15790
  */
15762
15791
  function readEnvCredentials() {
15763
15792
  return {
15764
15793
  clientId: process.env.MOLTNET_CLIENT_ID,
15765
15794
  clientSecret: process.env.MOLTNET_CLIENT_SECRET,
15766
- apiUrl: process.env.MOLTNET_API_URL
15795
+ apiUrl: process.env.MOLTNET_API_URL,
15796
+ agentKey: process.env.MOLTNET_AGENT_KEY
15767
15797
  };
15768
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
+ }
15769
15808
  //#endregion
15770
15809
  //#region ../sdk/src/credentials.ts
15771
15810
  function getConfigDir() {
@@ -15829,6 +15868,32 @@ function createRetryFetch(tokenManager, options) {
15829
15868
  return doFetch(init);
15830
15869
  };
15831
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
+ }
15832
15897
  //#endregion
15833
15898
  //#region ../sdk/src/token.ts
15834
15899
  var TokenManager = class {
@@ -15888,37 +15953,76 @@ var TokenManager = class {
15888
15953
  };
15889
15954
  //#endregion
15890
15955
  //#region ../sdk/src/connect.ts
15891
- var DEFAULT_API_URL = "https://api.themolt.net";
15892
- 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
+ }
15893
15967
  if (options.clientId && options.clientSecret) return {
15968
+ mode: "oauth2",
15894
15969
  clientId: options.clientId,
15895
15970
  clientSecret: options.clientSecret,
15896
- apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "")
15971
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
15897
15972
  };
15898
- 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
+ }
15899
15982
  if (env.clientId && env.clientSecret) return {
15983
+ mode: "oauth2",
15900
15984
  clientId: env.clientId,
15901
15985
  clientSecret: env.clientSecret,
15902
- apiUrl: (env.apiUrl ?? options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "")
15986
+ apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
15903
15987
  };
15904
15988
  const config = await readConfig(options.configDir);
15905
15989
  if (config?.oauth2?.client_id && config?.oauth2?.client_secret) return {
15990
+ mode: "oauth2",
15906
15991
  clientId: config.oauth2.client_id,
15907
15992
  clientSecret: config.oauth2.client_secret,
15908
- apiUrl: (options.apiUrl ?? config.endpoints?.api ?? DEFAULT_API_URL).replace(/\/$/, "")
15993
+ apiUrl: normalizeApiUrl(options.apiUrl, config.endpoints?.api)
15909
15994
  };
15910
- 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" });
15911
15996
  }
15912
15997
  /**
15913
15998
  * Connect to MoltNet and return an authenticated Agent facade.
15914
15999
  *
15915
- * Credential resolution order:
15916
- * 1. Explicit `clientId` / `clientSecret` in options
15917
- * 2. `MOLTNET_CLIENT_ID` / `MOLTNET_CLIENT_SECRET` environment variables
15918
- * 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`.
15919
16011
  */
15920
16012
  async function connect(options = {}) {
15921
- 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;
15922
16026
  const autoToken = options.autoToken ?? true;
15923
16027
  const tokenManager = new TokenManager({
15924
16028
  clientId: creds.clientId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-extension",
3
- "version": "0.35.1",
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.1",
40
- "@themoltnet/sdk": "0.122.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",