@agenthifive/openclaw 0.1.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.
- package/README.md +124 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +136 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt-utils.d.ts +29 -0
- package/dist/jwt-utils.d.ts.map +1 -0
- package/dist/jwt-utils.js +55 -0
- package/dist/jwt-utils.js.map +1 -0
- package/dist/patch-verify.d.ts +28 -0
- package/dist/patch-verify.d.ts.map +1 -0
- package/dist/patch-verify.js +72 -0
- package/dist/patch-verify.js.map +1 -0
- package/dist/pending-approvals.d.ts +55 -0
- package/dist/pending-approvals.d.ts.map +1 -0
- package/dist/pending-approvals.js +95 -0
- package/dist/pending-approvals.js.map +1 -0
- package/dist/prompt-reference.d.ts +51 -0
- package/dist/prompt-reference.d.ts.map +1 -0
- package/dist/prompt-reference.js +645 -0
- package/dist/prompt-reference.js.map +1 -0
- package/dist/register.d.ts +20 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +551 -0
- package/dist/register.js.map +1 -0
- package/dist/runtime.d.ts +66 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +87 -0
- package/dist/runtime.js.map +1 -0
- package/dist/session-context.d.ts +39 -0
- package/dist/session-context.d.ts.map +1 -0
- package/dist/session-context.js +58 -0
- package/dist/session-context.js.map +1 -0
- package/dist/setup-wizard.d.ts +28 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +303 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/tools.d.ts +27 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +128 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vault-action-proxy.d.ts +75 -0
- package/dist/vault-action-proxy.d.ts.map +1 -0
- package/dist/vault-action-proxy.js +152 -0
- package/dist/vault-action-proxy.js.map +1 -0
- package/dist/vault-provider.d.ts +52 -0
- package/dist/vault-provider.d.ts.map +1 -0
- package/dist/vault-provider.js +37 -0
- package/dist/vault-provider.js.map +1 -0
- package/dist/vault-token-manager.d.ts +42 -0
- package/dist/vault-token-manager.d.ts.map +1 -0
- package/dist/vault-token-manager.js +124 -0
- package/dist/vault-token-manager.js.map +1 -0
- package/openclaw.plugin.json +59 -0
- package/package.json +58 -0
- package/patches/README.md +85 -0
- package/patches/model-auth.patch +44 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { importES256Key, exchangeToken } from "./jwt-utils.js";
|
|
2
|
+
export class VaultTokenManager {
|
|
3
|
+
baseUrl;
|
|
4
|
+
agentId;
|
|
5
|
+
privateKeyJWK;
|
|
6
|
+
tokenAudience;
|
|
7
|
+
privateKeyObj = null;
|
|
8
|
+
accessToken = null;
|
|
9
|
+
tokenExpiresAt = 0; // epoch ms
|
|
10
|
+
refreshTimer = null;
|
|
11
|
+
refreshInFlight = null;
|
|
12
|
+
/** Called after every successful token refresh with the new token. */
|
|
13
|
+
onRefresh = null;
|
|
14
|
+
/** Called when token refresh fails with 401 — indicates the agent's key is no longer valid. */
|
|
15
|
+
onAuthFailure = null;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
18
|
+
this.agentId = config.agentId;
|
|
19
|
+
this.privateKeyJWK = config.privateKey;
|
|
20
|
+
this.tokenAudience = (config.tokenAudience ?? this.baseUrl).replace(/\/+$/, "");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Perform the initial token exchange and start the background refresh timer.
|
|
24
|
+
* Must be called (and awaited) before getToken().
|
|
25
|
+
*/
|
|
26
|
+
async init() {
|
|
27
|
+
// Import the JWK once
|
|
28
|
+
this.privateKeyObj = await importES256Key(this.privateKeyJWK);
|
|
29
|
+
// Initial token exchange
|
|
30
|
+
await this.refreshToken();
|
|
31
|
+
// Schedule background refresh — check every 30s, refresh when near expiry
|
|
32
|
+
this.refreshTimer = setInterval(() => {
|
|
33
|
+
if (Date.now() >= this.tokenExpiresAt - 60_000) {
|
|
34
|
+
this.refreshToken().catch((err) => {
|
|
35
|
+
const ttlLeft = Math.max(0, Math.round((this.tokenExpiresAt - Date.now()) / 1000));
|
|
36
|
+
console.error(`[vault-token-manager] refresh failed (current token expires in ${ttlLeft}s, ` +
|
|
37
|
+
`prefix: ${this.accessToken?.slice(0, 4) ?? "none"}): ${err instanceof Error ? err.message : String(err)}`);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}, 30_000);
|
|
41
|
+
// Don't keep the process alive just for token refresh
|
|
42
|
+
if (this.refreshTimer &&
|
|
43
|
+
typeof this.refreshTimer === "object" &&
|
|
44
|
+
"unref" in this.refreshTimer) {
|
|
45
|
+
this.refreshTimer.unref();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the current bearer token. Synchronous — relies on background refresh.
|
|
50
|
+
* Throws if init() hasn't been called.
|
|
51
|
+
*/
|
|
52
|
+
getToken() {
|
|
53
|
+
if (!this.accessToken) {
|
|
54
|
+
throw new Error("VaultTokenManager not initialized — call init() first");
|
|
55
|
+
}
|
|
56
|
+
return this.accessToken;
|
|
57
|
+
}
|
|
58
|
+
/** Stop the background refresh timer. */
|
|
59
|
+
stop() {
|
|
60
|
+
if (this.refreshTimer) {
|
|
61
|
+
clearInterval(this.refreshTimer);
|
|
62
|
+
this.refreshTimer = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Force an immediate token refresh. Called on-demand when a 401 is received.
|
|
67
|
+
* Coalesces concurrent requests — if a refresh is already in flight, callers
|
|
68
|
+
* wait for the same promise instead of hammering the token endpoint.
|
|
69
|
+
*/
|
|
70
|
+
async forceRefresh() {
|
|
71
|
+
if (this.refreshInFlight) {
|
|
72
|
+
try {
|
|
73
|
+
await this.refreshInFlight;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const attempt = this.refreshToken();
|
|
81
|
+
this.refreshInFlight = attempt;
|
|
82
|
+
try {
|
|
83
|
+
await attempt;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
this.refreshInFlight = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async refreshToken() {
|
|
94
|
+
if (!this.privateKeyObj) {
|
|
95
|
+
throw new Error("Private key not imported — call init() first");
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const result = await exchangeToken(this.privateKeyObj, {
|
|
99
|
+
baseUrl: this.baseUrl,
|
|
100
|
+
agentId: this.agentId,
|
|
101
|
+
tokenAudience: this.tokenAudience,
|
|
102
|
+
});
|
|
103
|
+
const previousToken = this.accessToken;
|
|
104
|
+
this.accessToken = result.accessToken;
|
|
105
|
+
this.tokenExpiresAt = Date.now() + result.expiresIn * 1000;
|
|
106
|
+
console.log(`[vault-token-manager] Token refreshed (prefix: ${this.accessToken.slice(0, 4)}..., ` +
|
|
107
|
+
`ttl: ${result.expiresIn}s, previous: ${previousToken ? previousToken.slice(0, 4) + "..." : "none"})`);
|
|
108
|
+
// Notify listener (register.ts uses this to update the shared mutable auth object)
|
|
109
|
+
this.onRefresh?.(this.accessToken);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// Check for 401 specifically
|
|
113
|
+
if (err instanceof Error && err.message.includes("401")) {
|
|
114
|
+
console.error("[vault-token-manager] Token exchange rejected (401). " +
|
|
115
|
+
"The agent's key pair is no longer valid — agent may have been disabled or key rotated. " +
|
|
116
|
+
"Generate a bootstrap secret from the AgentHiFive dashboard (Agents → Bootstrap Secret), " +
|
|
117
|
+
"then run `openclaw configure` to reconnect.");
|
|
118
|
+
this.onAuthFailure?.();
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=vault-token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-token-manager.js","sourceRoot":"","sources":["../src/vault-token-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agenthifive",
|
|
3
|
+
"name": "AgentHiFive Vault",
|
|
4
|
+
"description": "Vault-managed credentials, brokered API proxy, and policy-governed access for AI agents",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"baseUrl": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "AgentHiFive Vault API base URL",
|
|
12
|
+
"default": "https://app.agenthifive.com"
|
|
13
|
+
},
|
|
14
|
+
"auth": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"mode": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["bearer", "agent"],
|
|
20
|
+
"description": "Authentication mode: 'bearer' for opaque token, 'agent' for ES256 JWT"
|
|
21
|
+
},
|
|
22
|
+
"token": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Bearer token (only for mode=bearer)"
|
|
25
|
+
},
|
|
26
|
+
"agentId": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Agent ID (only for mode=agent)"
|
|
29
|
+
},
|
|
30
|
+
"privateKey": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "ES256 private key as base64-encoded JWK (only for mode=agent)"
|
|
33
|
+
},
|
|
34
|
+
"tokenAudience": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Token audience override (optional, defaults to baseUrl)"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["mode"]
|
|
40
|
+
},
|
|
41
|
+
"pollTimeoutMs": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"description": "Approval polling timeout in milliseconds (default: 300000 = 5 min)",
|
|
44
|
+
"default": 300000
|
|
45
|
+
},
|
|
46
|
+
"pollIntervalMs": {
|
|
47
|
+
"type": "number",
|
|
48
|
+
"description": "Approval polling interval in milliseconds (default: 3000 = 3s)",
|
|
49
|
+
"default": 3000
|
|
50
|
+
},
|
|
51
|
+
"connectedProviders": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "type": "string" },
|
|
54
|
+
"description": "List of connected provider names for prompt injection (e.g., ['google', 'microsoft', 'notion'])"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"required": ["auth"]
|
|
58
|
+
}
|
|
59
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agenthifive/openclaw",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./runtime": {
|
|
13
|
+
"types": "./dist/runtime.d.ts",
|
|
14
|
+
"default": "./dist/runtime.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"openclaw": {
|
|
18
|
+
"extensions": ["./dist/register.js"]
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"openclaw.plugin.json",
|
|
23
|
+
"patches"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "tsc -p tsconfig.json && node --experimental-transform-types --test src/__tests__/*.test.ts",
|
|
29
|
+
"lint": "echo Lint: OK"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/supersantux/AgentH5.git",
|
|
35
|
+
"directory": "packages/openclaw"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/supersantux/AgentH5/tree/main/packages/openclaw#readme",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"openclaw",
|
|
40
|
+
"agenthifive",
|
|
41
|
+
"vault",
|
|
42
|
+
"ai-agent",
|
|
43
|
+
"credential-management",
|
|
44
|
+
"oauth",
|
|
45
|
+
"policy-engine",
|
|
46
|
+
"plugin"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"jose": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^20.0.0",
|
|
56
|
+
"typescript": "^5.6.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# AgentHiFive OpenClaw Patches
|
|
2
|
+
|
|
3
|
+
Optional patches that enable credential proxying features in OpenClaw core.
|
|
4
|
+
|
|
5
|
+
**These patches are NOT required for basic functionality.** The AgentHiFive plugin
|
|
6
|
+
works fully without patches — tools, prompt injection, approval flow, and the
|
|
7
|
+
brokered API proxy (Model B) all work out of the box. Patches only enable
|
|
8
|
+
**LLM credential proxying** (routing LLM API calls through the vault).
|
|
9
|
+
|
|
10
|
+
## Available Patches
|
|
11
|
+
|
|
12
|
+
### `model-auth.patch`
|
|
13
|
+
|
|
14
|
+
Adds vault credential resolution to OpenClaw's `resolveApiKeyForProvider()` function
|
|
15
|
+
in `src/agents/model-auth.ts`. This inserts two new resolution tiers before the
|
|
16
|
+
existing local profile lookup:
|
|
17
|
+
|
|
18
|
+
| Tier | What it does | When it activates |
|
|
19
|
+
|------|-------------|-------------------|
|
|
20
|
+
| **Tier 0** | Returns the vault bearer token directly | Provider is in `proxiedProviders` list |
|
|
21
|
+
| **Tier 0.5** | Queries the vault credential provider chain | Always (falls through if no credentials found) |
|
|
22
|
+
|
|
23
|
+
The patch uses `await import("@agenthifive/openclaw/runtime")` wrapped in try/catch,
|
|
24
|
+
so it's a **complete no-op** when the plugin is not installed — safe for vanilla OpenClaw.
|
|
25
|
+
|
|
26
|
+
## How to Apply
|
|
27
|
+
|
|
28
|
+
### Using pnpm patch (recommended)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 1. Start the patch session
|
|
32
|
+
pnpm patch openclaw
|
|
33
|
+
|
|
34
|
+
# 2. Apply the patch to the extracted directory
|
|
35
|
+
# (pnpm will print the temporary directory path)
|
|
36
|
+
cd <temp-directory>
|
|
37
|
+
patch -p1 < /path/to/@agenthifive/openclaw/patches/model-auth.patch
|
|
38
|
+
|
|
39
|
+
# 3. Commit the patch
|
|
40
|
+
pnpm patch-commit <temp-directory>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This adds a `patchedDependencies` entry to your `package.json` that pnpm applies
|
|
44
|
+
automatically on `pnpm install`.
|
|
45
|
+
|
|
46
|
+
### Manual application
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cd /path/to/openclaw
|
|
50
|
+
patch -p1 < node_modules/@agenthifive/openclaw/patches/model-auth.patch
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Note: Manual patches are lost on `npm install` / `pnpm install`. Use `pnpm patch`
|
|
54
|
+
for persistence.
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
|
|
58
|
+
After applying, the AgentHiFive plugin will log at startup:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
AgentHiFive: model-auth patch detected — credential proxying enabled
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Without the patch, you'll see:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
AgentHiFive: model-auth patch not detected. LLM credential proxying is unavailable.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Compatibility
|
|
71
|
+
|
|
72
|
+
| OpenClaw Version | Patch Status |
|
|
73
|
+
|-----------------|-------------|
|
|
74
|
+
| Latest (Mar 2026) | Compatible |
|
|
75
|
+
|
|
76
|
+
The patch targets `resolveApiKeyForProvider()` in `src/agents/model-auth.ts`.
|
|
77
|
+
If OpenClaw restructures this function significantly, the patch may need
|
|
78
|
+
regeneration. Check the [AgentHiFive releases](https://github.com/supersantux/AgentH5/releases)
|
|
79
|
+
for updated patches.
|
|
80
|
+
|
|
81
|
+
## When Patches Become Unnecessary
|
|
82
|
+
|
|
83
|
+
When OpenClaw merges support for `apiKeyOverride` in the `before_model_resolve`
|
|
84
|
+
hook result type, the plugin can use the hook API directly and patches will be
|
|
85
|
+
deprecated. Track progress in the upstream PR.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
--- a/src/agents/model-auth.ts
|
|
2
|
+
+++ b/src/agents/model-auth.ts
|
|
3
|
+
@@ -289,7 +289,41 @@
|
|
4
|
+
}): Promise<ResolvedProviderAuth> {
|
|
5
|
+
const { provider, cfg, profileId, preferredProfile } = params;
|
|
6
|
+
const store = params.store ?? ensureAuthProfileStore(params.agentDir);
|
|
7
|
+
|
|
8
|
+
+ // -- Tier 0: AgentHiFive proxied providers (vault bearer token) --
|
|
9
|
+
+ // When the provider is in proxiedProviders, the vault acts as an LLM proxy.
|
|
10
|
+
+ // Return the managed vault bearer token directly -- no local API key needed.
|
|
11
|
+
+ // @agenthifive/openclaw/runtime -- dynamic import is a no-op when plugin is not installed.
|
|
12
|
+
+ try {
|
|
13
|
+
+ const runtime = await import("@agenthifive/openclaw/runtime");
|
|
14
|
+
+ if (runtime.isInitialized()) {
|
|
15
|
+
+ const proxiedProviders = runtime.getProxiedProviders();
|
|
16
|
+
+ if (proxiedProviders.includes(provider)) {
|
|
17
|
+
+ const vaultToken = runtime.getVaultBearerToken();
|
|
18
|
+
+ if (vaultToken) {
|
|
19
|
+
+ return { apiKey: vaultToken, source: "vault:agent-token", mode: "api-key" };
|
|
20
|
+
+ }
|
|
21
|
+
+ }
|
|
22
|
+
+
|
|
23
|
+
+ // -- Tier 0.5: Credential provider chain --
|
|
24
|
+
+ // Try the vault credential provider before local profiles.
|
|
25
|
+
+ const vaultResult = await runtime.resolveCredential({
|
|
26
|
+
+ kind: "model_provider",
|
|
27
|
+
+ provider,
|
|
28
|
+
+ profileId: preferredProfile,
|
|
29
|
+
+ });
|
|
30
|
+
+ if (vaultResult?.apiKey) {
|
|
31
|
+
+ return {
|
|
32
|
+
+ apiKey: vaultResult.apiKey,
|
|
33
|
+
+ source: "credential-provider:vault",
|
|
34
|
+
+ mode: vaultResult.mode ?? "api-key",
|
|
35
|
+
+ };
|
|
36
|
+
+ }
|
|
37
|
+
+ }
|
|
38
|
+
+ } catch {
|
|
39
|
+
+ // Plugin not installed -- skip vault credential resolution
|
|
40
|
+
+ }
|
|
41
|
+
+
|
|
42
|
+
if (profileId) {
|
|
43
|
+
const resolved = await resolveApiKeyForProfile({
|
|
44
|
+
cfg,
|