@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.
- package/dist/index.js +166 -76
- 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
|
|
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
|
|
3133
|
+
const { client } = context;
|
|
3119
3134
|
return {
|
|
3120
|
-
|
|
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: () =>
|
|
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
|
|
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
|
-
|
|
15906
|
-
|
|
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
|
|
15971
|
+
apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
|
|
15911
15972
|
};
|
|
15912
|
-
const
|
|
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: (
|
|
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
|
|
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
|
|
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
|
|
15930
|
-
*
|
|
15931
|
-
*
|
|
15932
|
-
*
|
|
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
|
|
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.
|
|
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.
|
|
40
|
-
"@themoltnet/sdk": "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",
|