@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.
- package/dist/index.js +183 -79
- 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 });
|
|
@@ -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
|
|
3133
|
+
const { client } = context;
|
|
3104
3134
|
return {
|
|
3105
|
-
|
|
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:
|
|
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: () =>
|
|
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
|
|
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
|
-
|
|
15892
|
-
|
|
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
|
|
15971
|
+
apiUrl: normalizeApiUrl(options.apiUrl, env.apiUrl)
|
|
15897
15972
|
};
|
|
15898
|
-
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
|
+
}
|
|
15899
15982
|
if (env.clientId && env.clientSecret) return {
|
|
15983
|
+
mode: "oauth2",
|
|
15900
15984
|
clientId: env.clientId,
|
|
15901
15985
|
clientSecret: env.clientSecret,
|
|
15902
|
-
apiUrl: (
|
|
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
|
|
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
|
|
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
|
|
15916
|
-
*
|
|
15917
|
-
*
|
|
15918
|
-
*
|
|
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
|
|
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.
|
|
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",
|