openclaw-pine-voice 0.1.10 → 0.3.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/dist/auth.d.ts +8 -0
- package/dist/auth.js +63 -37
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -2
- package/skills/pine-voice/SKILL.md +61 -0
- package/skills/pine-voice-auth/SKILL.md +25 -1
package/dist/auth.d.ts
CHANGED
|
@@ -8,5 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Delegates to the pine-voice SDK for actual API calls.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Build an updated config that stores credentials and ensures voice tools
|
|
13
|
+
* are present in `tools.allow`. Pure function — no side effects.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildAuthConfig(cfg: Record<string, any>, accessToken: string, userId: string): {
|
|
16
|
+
updatedConfig: Record<string, any>;
|
|
17
|
+
addedTools: string[];
|
|
18
|
+
};
|
|
11
19
|
export declare function registerAuthTools(api: any): void;
|
|
12
20
|
export declare function registerAuthCommands(api: any): void;
|
package/dist/auth.js
CHANGED
|
@@ -16,6 +16,53 @@ import { PineVoice, AuthError } from "pine-voice";
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
const pendingAuth = new Map(); // email → requestToken
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
+
// Shared helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const VOICE_TOOLS = [
|
|
22
|
+
"pine_voice_call_and_wait",
|
|
23
|
+
"pine_voice_call",
|
|
24
|
+
"pine_voice_call_status",
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Build an updated config that stores credentials and ensures voice tools
|
|
28
|
+
* are present in `tools.allow`. Pure function — no side effects.
|
|
29
|
+
*/
|
|
30
|
+
export function buildAuthConfig(cfg, accessToken, userId) {
|
|
31
|
+
const plugins = (cfg.plugins ?? {});
|
|
32
|
+
const entries = (plugins.entries ?? {});
|
|
33
|
+
const pluginEntry = (entries["openclaw-pine-voice"] ?? {});
|
|
34
|
+
const tools = (cfg.tools ?? {});
|
|
35
|
+
const existingAllow = Array.isArray(tools.allow)
|
|
36
|
+
? tools.allow.filter((t) => typeof t === "string")
|
|
37
|
+
: [];
|
|
38
|
+
const addedTools = VOICE_TOOLS.filter(t => !existingAllow.includes(t));
|
|
39
|
+
const mergedAllow = [...existingAllow, ...addedTools];
|
|
40
|
+
return {
|
|
41
|
+
updatedConfig: {
|
|
42
|
+
...cfg,
|
|
43
|
+
plugins: {
|
|
44
|
+
...plugins,
|
|
45
|
+
entries: {
|
|
46
|
+
...entries,
|
|
47
|
+
"openclaw-pine-voice": {
|
|
48
|
+
...pluginEntry,
|
|
49
|
+
config: {
|
|
50
|
+
...(pluginEntry.config ?? {}),
|
|
51
|
+
access_token: accessToken,
|
|
52
|
+
user_id: userId,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
tools: {
|
|
58
|
+
...tools,
|
|
59
|
+
allow: mergedAllow,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
addedTools: [...addedTools],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
19
66
|
// Tool registration (primary path)
|
|
20
67
|
// ---------------------------------------------------------------------------
|
|
21
68
|
export function registerAuthTools(api) {
|
|
@@ -85,36 +132,22 @@ export function registerAuthTools(api) {
|
|
|
85
132
|
}
|
|
86
133
|
try {
|
|
87
134
|
const { accessToken, userId } = await PineVoice.auth.verifyCode(params.email, requestToken, params.code);
|
|
88
|
-
// Write credentials to openclaw.json
|
|
89
135
|
const cfg = api.runtime.config.loadConfig();
|
|
90
|
-
const
|
|
91
|
-
const entries = (plugins.entries ?? {});
|
|
92
|
-
const pluginEntry = (entries["openclaw-pine-voice"] ?? {});
|
|
93
|
-
const updatedConfig = {
|
|
94
|
-
...cfg,
|
|
95
|
-
plugins: {
|
|
96
|
-
...plugins,
|
|
97
|
-
entries: {
|
|
98
|
-
...entries,
|
|
99
|
-
"openclaw-pine-voice": {
|
|
100
|
-
...pluginEntry,
|
|
101
|
-
config: {
|
|
102
|
-
...(pluginEntry.config ?? {}),
|
|
103
|
-
access_token: accessToken,
|
|
104
|
-
user_id: userId,
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
};
|
|
136
|
+
const { updatedConfig, addedTools } = buildAuthConfig(cfg, accessToken, userId);
|
|
110
137
|
await api.runtime.config.writeConfigFile(updatedConfig);
|
|
111
138
|
pendingAuth.delete(params.email);
|
|
139
|
+
const toolsNote = addedTools.length > 0
|
|
140
|
+
? ` Voice tools (${addedTools.join(", ")}) have been added to tools.allow.`
|
|
141
|
+
: "";
|
|
112
142
|
api.log?.info?.(`pine-voice: auth successful for ${params.email}, credentials saved`);
|
|
143
|
+
if (addedTools.length > 0) {
|
|
144
|
+
api.log?.info?.(`pine-voice: added ${addedTools.join(", ")} to tools.allow`);
|
|
145
|
+
}
|
|
113
146
|
return {
|
|
114
147
|
content: [
|
|
115
148
|
{
|
|
116
149
|
type: "text",
|
|
117
|
-
text:
|
|
150
|
+
text: `Authentication successful! Credentials have been saved to openclaw.json.${toolsNote} ` +
|
|
118
151
|
"Tell the user to restart the gateway for the changes to take effect:\n\n" +
|
|
119
152
|
" openclaw gateway restart",
|
|
120
153
|
},
|
|
@@ -177,21 +210,14 @@ export function registerAuthCommands(api) {
|
|
|
177
210
|
}
|
|
178
211
|
try {
|
|
179
212
|
const { accessToken, userId } = await PineVoice.auth.verifyCode(opts.email, opts.requestToken || "", opts.code);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
console.log(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
console.log(
|
|
188
|
-
console.log(` "user_id": "${userId}"`);
|
|
189
|
-
console.log(` }`);
|
|
190
|
-
console.log(` }`);
|
|
191
|
-
console.log(` }`);
|
|
192
|
-
console.log(` }`);
|
|
193
|
-
console.log("");
|
|
194
|
-
console.log("Then restart the gateway:");
|
|
213
|
+
const cfg = api.runtime.config.loadConfig();
|
|
214
|
+
const { updatedConfig, addedTools } = buildAuthConfig(cfg, accessToken, userId);
|
|
215
|
+
await api.runtime.config.writeConfigFile(updatedConfig);
|
|
216
|
+
console.log("Authentication successful! Credentials saved to openclaw.json.");
|
|
217
|
+
if (addedTools.length > 0) {
|
|
218
|
+
console.log(`Voice tools (${addedTools.join(", ")}) added to tools.allow.`);
|
|
219
|
+
}
|
|
220
|
+
console.log("\nRestart the gateway for changes to take effect:");
|
|
195
221
|
console.log(" openclaw gateway restart");
|
|
196
222
|
}
|
|
197
223
|
catch (err) {
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-pine-voice",
|
|
3
3
|
"name": "Pine AI Voice Call",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Make phone calls via Pine AI voice agent. The AI agent calls the specified number and carries out the conversation based on your instructions. The voice agent can only speak English, so calls can only be delivered to English-speaking countries. Before calling, gather all information the callee may need for authentication and verification — the agent cannot ask a human for missing info mid-call. Returns the full transcript. Powered by Pine AI.",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-pine-voice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Make phone calls via Pine AI voice agent from OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc",
|
|
28
|
+
"test": "vitest run",
|
|
28
29
|
"prepublishOnly": "npm run build"
|
|
29
30
|
},
|
|
30
31
|
"keywords": [
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"pine-voice": "^0.1.3"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"typescript": "^5.0.0"
|
|
56
|
+
"typescript": "^5.0.0",
|
|
57
|
+
"vitest": "^4.0.18"
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -120,6 +120,67 @@ For calls involving negotiation (bill reduction, rate matching, fee waiver), pro
|
|
|
120
120
|
**Restaurant reservation:**
|
|
121
121
|
"Call the restaurant at +14155559876 and make a reservation for 4 people tonight at 7pm. If 7pm is not available, try 7:30 or 8pm. Name for the reservation: Jane Doe."
|
|
122
122
|
|
|
123
|
+
## Troubleshooting
|
|
124
|
+
|
|
125
|
+
### "Tool pine_voice_call_and_wait not found"
|
|
126
|
+
|
|
127
|
+
The voice tools (`pine_voice_call_and_wait`, `pine_voice_call`, `pine_voice_call_status`) are registered as **optional** tools. They are only available to the AI agent if they are explicitly listed in the `tools.allow` configuration.
|
|
128
|
+
|
|
129
|
+
**Cause:** The tool names are missing from `tools.allow` in `openclaw.json`.
|
|
130
|
+
|
|
131
|
+
**Fix:** Add the voice tools to `tools.allow` in `openclaw.json`. Any of these approaches work:
|
|
132
|
+
|
|
133
|
+
1. **Re-run authentication** — the `pine_voice_auth_verify` tool automatically adds all voice tools to `tools.allow`. Use the `pine-voice-auth` skill to re-authenticate, then restart the gateway.
|
|
134
|
+
|
|
135
|
+
2. **Add tools manually** — edit `openclaw.json` and add the tool names to `tools.allow`:
|
|
136
|
+
```json
|
|
137
|
+
{ "tools": { "allow": ["pine_voice_call_and_wait", "pine_voice_call", "pine_voice_call_status"] } }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
After editing the config, restart the gateway: `openclaw gateway restart`.
|
|
141
|
+
|
|
142
|
+
### "Pine Voice is not authenticated"
|
|
143
|
+
|
|
144
|
+
The plugin has no saved credentials. Run the auth flow using the `pine-voice-auth` skill.
|
|
145
|
+
|
|
146
|
+
### "Pine Voice authentication has expired"
|
|
147
|
+
|
|
148
|
+
The access token has expired. Re-run the auth flow using the `pine-voice-auth` skill to get a fresh token.
|
|
149
|
+
|
|
150
|
+
### "TOKEN_EXPIRED" or 401 errors during a call
|
|
151
|
+
|
|
152
|
+
The token was valid at auth time but has since expired. Re-authenticate using the `pine-voice-auth` skill and restart the gateway.
|
|
153
|
+
|
|
154
|
+
### "SUBSCRIPTION_REQUIRED: Pine AI Pro subscription required"
|
|
155
|
+
|
|
156
|
+
The user's Pine AI account does not have an active Pro subscription, or the subscription has expired/been cancelled. The user must subscribe or renew at https://19pine.ai.
|
|
157
|
+
|
|
158
|
+
### "INSUFFICIENT_CREDITS: At least N credits required"
|
|
159
|
+
|
|
160
|
+
The user's credit balance is too low to initiate a call. Each call requires a minimum of 50 credits (base charge). The user must add credits at https://19pine.ai.
|
|
161
|
+
|
|
162
|
+
### "RATE_LIMITED: You've already called this number N times today"
|
|
163
|
+
|
|
164
|
+
The voice gateway enforces a per-target daily call limit. The user has called the same phone number too many times in one day. Wait until the next day or call a different number.
|
|
165
|
+
|
|
166
|
+
### "RATE_LIMITED: Maximum N concurrent calls exceeded"
|
|
167
|
+
|
|
168
|
+
The user has too many active calls running simultaneously (default limit: 5). Wait for an active call to finish before starting a new one.
|
|
169
|
+
|
|
170
|
+
### "POLICY_VIOLATION" or safety review rejection
|
|
171
|
+
|
|
172
|
+
The gateway's safety review rejected the call request. Common reasons:
|
|
173
|
+
- Objective is too vague (e.g., "just call them")
|
|
174
|
+
- Target number is in an unsupported country
|
|
175
|
+
- Negotiator caller type used without a complete negotiation strategy
|
|
176
|
+
- Number is on the emergency or premium-rate blocklist
|
|
177
|
+
|
|
178
|
+
Read the error message for specifics and adjust the call parameters accordingly.
|
|
179
|
+
|
|
180
|
+
### "PHONE_REQUIRED"
|
|
181
|
+
|
|
182
|
+
The user has not registered a phone number in their Pine AI account. They need to add one in their account settings at https://www.19pine.ai.
|
|
183
|
+
|
|
123
184
|
## HTTP API reference
|
|
124
185
|
|
|
125
186
|
The plugin uses these REST endpoints on the Pine Voice gateway (for transparency — the tools handle this automatically):
|
|
@@ -60,7 +60,7 @@ Call the `pine_voice_auth_verify` tool with the email and code:
|
|
|
60
60
|
pine_voice_auth_verify({ email: "user@example.com", code: "123456" })
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
The tool verifies the code, saves the credentials to `~/.openclaw/openclaw.json
|
|
63
|
+
The tool verifies the code, saves the credentials to `~/.openclaw/openclaw.json`, and automatically adds the voice tools (`pine_voice_call_and_wait`, `pine_voice_call`, `pine_voice_call_status`) to `tools.allow` if they are not already present.
|
|
64
64
|
|
|
65
65
|
**If verification fails:**
|
|
66
66
|
- Invalid code — ask the user to double-check the code and try again (call `pine_voice_auth_verify` with the corrected code).
|
|
@@ -99,6 +99,30 @@ Access tokens expire periodically. When a call fails with `TOKEN_EXPIRED` or a 4
|
|
|
99
99
|
1. Inform the user their token has expired and needs to be refreshed
|
|
100
100
|
2. Re-run this auth flow starting from step 1
|
|
101
101
|
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
### "Tool pine_voice_call_and_wait not found" after successful auth
|
|
105
|
+
|
|
106
|
+
Authentication succeeded but the tools are still not visible. This means the tool names were not added to `tools.allow` during verification (e.g., the config was edited manually afterward).
|
|
107
|
+
|
|
108
|
+
**Fix:** Re-run `pine_voice_auth_verify` which automatically adds the voice tools to `tools.allow`, then restart the gateway. Alternatively, manually add `"pine_voice_call_and_wait"`, `"pine_voice_call"`, and `"pine_voice_call_status"` to the `tools.allow` array in `openclaw.json`.
|
|
109
|
+
|
|
110
|
+
### Auth request fails with 400/422
|
|
111
|
+
|
|
112
|
+
The email is likely not registered with Pine AI. Ask the user to verify the email address or sign up at https://19pine.ai.
|
|
113
|
+
|
|
114
|
+
### "No pending auth request found for this email"
|
|
115
|
+
|
|
116
|
+
The `pine_voice_auth_verify` tool was called without a prior `pine_voice_auth_request`, or the in-memory request token was lost (e.g., gateway restarted between steps). Go back to step 2 and send a new verification code.
|
|
117
|
+
|
|
118
|
+
### Code expired
|
|
119
|
+
|
|
120
|
+
Verification codes expire after a few minutes. If the user took too long, go back to step 2 to send a fresh code.
|
|
121
|
+
|
|
122
|
+
### Gateway restart required after auth
|
|
123
|
+
|
|
124
|
+
Credentials are saved to `openclaw.json` but the gateway loads config at startup. The gateway **must be restarted** (`openclaw gateway restart`) for new credentials to take effect. If voice tools fail immediately after auth, remind the user to restart.
|
|
125
|
+
|
|
102
126
|
## Security notes
|
|
103
127
|
|
|
104
128
|
- Never log or echo the access token in plaintext beyond what is needed
|