openclaw-pine-voice 0.1.6 → 0.1.7
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 +20 -12
- package/dist/auth.d.ts +6 -1
- package/dist/auth.js +137 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +9 -4
- package/dist/tool.js +5 -26
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/pine-voice-auth/SKILL.md +20 -64
package/README.md
CHANGED
|
@@ -87,11 +87,25 @@ Your agent now has access to the Pine Voice call tools.
|
|
|
87
87
|
|
|
88
88
|
### Step 3: Authenticate with Pine AI
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
**Option A: Just ask the agent (recommended)**
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
The easiest way to authenticate is to simply tell your agent:
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
> "Set up Pine Voice authentication"
|
|
95
|
+
|
|
96
|
+
The agent will ask for your Pine AI email, send a verification code, ask you for the code, and save the credentials — all conversationally. It uses two built-in tools (`pine_voice_auth_request` and `pine_voice_auth_verify`) that handle the entire flow and write the credentials to `openclaw.json` automatically. After it's done, restart the gateway:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
openclaw gateway restart
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This also works on first use: if you skip authentication and later ask the agent to make a call, it will detect that credentials are missing and walk you through the same flow.
|
|
103
|
+
|
|
104
|
+
> **Note:** The verification code arrives by email, so you need to be available to provide it. If the agent tries to make a call while you're away, it will be blocked until you complete verification.
|
|
105
|
+
|
|
106
|
+
**Option B: Manual CLI setup**
|
|
107
|
+
|
|
108
|
+
If you prefer to authenticate from the terminal:
|
|
95
109
|
|
|
96
110
|
```bash
|
|
97
111
|
# 1. Request a verification code (sent to your Pine AI account email)
|
|
@@ -101,7 +115,7 @@ openclaw pine-voice auth setup --email you@example.com
|
|
|
101
115
|
openclaw pine-voice auth verify --email you@example.com --request-token <TOKEN> --code 1234
|
|
102
116
|
```
|
|
103
117
|
|
|
104
|
-
The command prints your access token. Add
|
|
118
|
+
The command prints your access token and user ID. Add them to your plugin config in `openclaw.json`:
|
|
105
119
|
|
|
106
120
|
```json
|
|
107
121
|
{
|
|
@@ -118,18 +132,12 @@ The command prints your access token. Add it to your plugin config in `openclaw.
|
|
|
118
132
|
}
|
|
119
133
|
```
|
|
120
134
|
|
|
121
|
-
Then restart the gateway
|
|
135
|
+
Then restart the gateway:
|
|
122
136
|
|
|
123
137
|
```bash
|
|
124
138
|
openclaw gateway restart
|
|
125
139
|
```
|
|
126
140
|
|
|
127
|
-
**Option B: Let the agent handle it on first use**
|
|
128
|
-
|
|
129
|
-
If you skip authentication, the plugin still loads and the tool is visible to your agent. The first time the agent tries to make a call, it will receive an error explaining that authentication is needed. The agent will then guide you through the email verification flow — it will ask for your email, run the auth commands, and configure the token for you.
|
|
130
|
-
|
|
131
|
-
This works, but keep in mind: the email verification code arrives in your inbox, so you need to be available to provide it. If the agent tries to make a call while you're away (e.g., in an automated workflow or overnight), it will be blocked until you complete verification.
|
|
132
|
-
|
|
133
141
|
## Try it out
|
|
134
142
|
|
|
135
143
|
After setup, test that everything works by making a quick call to your own phone:
|
|
@@ -195,7 +203,7 @@ When using `pine_voice_call` + `pine_voice_call_status` (manual):
|
|
|
195
203
|
|
|
196
204
|
| Error | Cause | Fix |
|
|
197
205
|
|---|---|---|
|
|
198
|
-
| TOKEN_EXPIRED | Access token expired |
|
|
206
|
+
| TOKEN_EXPIRED | Access token expired | Ask your agent to re-authenticate, or re-run `openclaw pine-voice auth setup` |
|
|
199
207
|
| SUBSCRIPTION_REQUIRED | Not a Pro subscriber | Subscribe at 19pine.ai |
|
|
200
208
|
| RATE_LIMITED | Too many calls | Wait and try again |
|
|
201
209
|
| INSUFFICIENT_DETAIL | Objective too vague | Provide a more specific call objective |
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth flow for Pine Voice plugin.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Provides two surfaces:
|
|
5
|
+
* 1. Tools (pine_voice_auth_request / pine_voice_auth_verify) — the primary,
|
|
6
|
+
* conversational path where the AI agent drives the flow.
|
|
7
|
+
* 2. CLI commands (openclaw pine-voice auth setup/verify) — a manual fallback.
|
|
4
8
|
*
|
|
5
9
|
* Delegates to the pine-voice SDK for actual API calls.
|
|
6
10
|
*/
|
|
11
|
+
export declare function registerAuthTools(api: any): void;
|
|
7
12
|
export declare function registerAuthCommands(api: any): void;
|
package/dist/auth.js
CHANGED
|
@@ -1,10 +1,145 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth flow for Pine Voice plugin.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Provides two surfaces:
|
|
5
|
+
* 1. Tools (pine_voice_auth_request / pine_voice_auth_verify) — the primary,
|
|
6
|
+
* conversational path where the AI agent drives the flow.
|
|
7
|
+
* 2. CLI commands (openclaw pine-voice auth setup/verify) — a manual fallback.
|
|
4
8
|
*
|
|
5
9
|
* Delegates to the pine-voice SDK for actual API calls.
|
|
6
10
|
*/
|
|
7
|
-
import {
|
|
11
|
+
import { Type } from "@sinclair/typebox";
|
|
12
|
+
import { PineVoice, AuthError } from "pine-voice";
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Module-level state: stores requestToken between the request and verify steps
|
|
15
|
+
// so the AI agent never needs to pass it explicitly.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const pendingAuth = new Map(); // email → requestToken
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Tool registration (primary path)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
export function registerAuthTools(api) {
|
|
22
|
+
// --- Tool: pine_voice_auth_request ---
|
|
23
|
+
api.registerTool({
|
|
24
|
+
name: "pine_voice_auth_request",
|
|
25
|
+
description: "Start Pine Voice authentication. Sends a verification code to the user's " +
|
|
26
|
+
"Pine AI account email. After calling this, ask the user to check their email " +
|
|
27
|
+
"(including spam) and provide the code, then call pine_voice_auth_verify.",
|
|
28
|
+
parameters: Type.Object({
|
|
29
|
+
email: Type.String({ description: "The user's Pine AI account email address" }),
|
|
30
|
+
}),
|
|
31
|
+
async execute(_toolCallId, params) {
|
|
32
|
+
try {
|
|
33
|
+
const { requestToken } = await PineVoice.auth.requestCode(params.email);
|
|
34
|
+
pendingAuth.set(params.email, requestToken);
|
|
35
|
+
api.log?.info?.(`pine-voice: auth code requested for ${params.email}`);
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `Verification code sent to ${params.email}. ` +
|
|
41
|
+
"Ask the user to check their email (including spam folder) and provide the code. " +
|
|
42
|
+
"Then call pine_voice_auth_verify with the email and code.",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
isError: false,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
+
api.log?.error?.(`pine-voice: auth request failed: ${message}`);
|
|
51
|
+
const hint = err instanceof AuthError && err.status >= 400 && err.status < 500
|
|
52
|
+
? " The email may not be registered — the user can sign up at https://19pine.ai."
|
|
53
|
+
: "";
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: `Pine Voice auth request failed: ${message}.${hint}` }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
// --- Tool: pine_voice_auth_verify ---
|
|
62
|
+
api.registerTool({
|
|
63
|
+
name: "pine_voice_auth_verify",
|
|
64
|
+
description: "Complete Pine Voice authentication. Verifies the code the user received by email, " +
|
|
65
|
+
"saves the credentials to openclaw.json, and tells the user to restart the gateway. " +
|
|
66
|
+
"Must be called after pine_voice_auth_request.",
|
|
67
|
+
parameters: Type.Object({
|
|
68
|
+
email: Type.String({ description: "The same email used in pine_voice_auth_request" }),
|
|
69
|
+
code: Type.String({ description: "The verification code from the user's email" }),
|
|
70
|
+
request_token: Type.Optional(Type.String({ description: "Request token from pine_voice_auth_request (usually not needed — resolved automatically)" })),
|
|
71
|
+
}),
|
|
72
|
+
async execute(_toolCallId, params) {
|
|
73
|
+
const requestToken = params.request_token || pendingAuth.get(params.email);
|
|
74
|
+
if (!requestToken) {
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: "No pending auth request found for this email. " +
|
|
80
|
+
"Call pine_voice_auth_request first to send a new verification code.",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
isError: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const { accessToken, userId } = await PineVoice.auth.verifyCode(params.email, requestToken, params.code);
|
|
88
|
+
// Write credentials to openclaw.json
|
|
89
|
+
const cfg = api.runtime.config.loadConfig();
|
|
90
|
+
const plugins = (cfg.plugins ?? {});
|
|
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
|
+
};
|
|
110
|
+
await api.runtime.config.writeConfigFile(updatedConfig);
|
|
111
|
+
pendingAuth.delete(params.email);
|
|
112
|
+
api.log?.info?.(`pine-voice: auth successful for ${params.email}, credentials saved`);
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: "Authentication successful! Credentials have been saved to openclaw.json. " +
|
|
118
|
+
"Tell the user to restart the gateway for the changes to take effect:\n\n" +
|
|
119
|
+
" openclaw gateway restart",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
isError: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
127
|
+
api.log?.error?.(`pine-voice: auth verify failed: ${message}`);
|
|
128
|
+
const isExpired = message.toLowerCase().includes("expired");
|
|
129
|
+
const hint = isExpired
|
|
130
|
+
? " The request token has expired — call pine_voice_auth_request again to send a new code."
|
|
131
|
+
: " Ask the user to double-check the code and try again.";
|
|
132
|
+
return {
|
|
133
|
+
content: [{ type: "text", text: `Pine Voice auth verification failed: ${message}.${hint}` }],
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// CLI registration (manual fallback)
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
8
143
|
export function registerAuthCommands(api) {
|
|
9
144
|
api.registerCli?.(({ program }) => {
|
|
10
145
|
const pineVoice = program.command("pine-voice").description("Pine AI Voice Call plugin");
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Registers:
|
|
5
5
|
* - pine_voice_call tool (initiate a phone call, returns immediately)
|
|
6
6
|
* - pine_voice_call_status tool (check call progress / get results)
|
|
7
|
-
* -
|
|
7
|
+
* - pine_voice_call_and_wait tool (initiate + wait for result)
|
|
8
|
+
* - pine_voice_auth_request tool (start email-based auth)
|
|
9
|
+
* - pine_voice_auth_verify tool (complete auth and save credentials)
|
|
10
|
+
* - pine-voice CLI commands (auth setup/verify — manual fallback)
|
|
8
11
|
*
|
|
9
12
|
* Delegates all API calls to the pine-voice SDK. All safety, billing,
|
|
10
13
|
* and prompt logic lives on Pine's side.
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { registerVoiceCallTools } from "./tool.js";
|
|
2
|
-
import { registerAuthCommands } from "./auth.js";
|
|
2
|
+
import { registerAuthTools, registerAuthCommands } from "./auth.js";
|
|
3
3
|
/**
|
|
4
4
|
* Pine AI Voice Call plugin for OpenClaw.
|
|
5
5
|
*
|
|
6
6
|
* Registers:
|
|
7
7
|
* - pine_voice_call tool (initiate a phone call, returns immediately)
|
|
8
8
|
* - pine_voice_call_status tool (check call progress / get results)
|
|
9
|
-
* -
|
|
9
|
+
* - pine_voice_call_and_wait tool (initiate + wait for result)
|
|
10
|
+
* - pine_voice_auth_request tool (start email-based auth)
|
|
11
|
+
* - pine_voice_auth_verify tool (complete auth and save credentials)
|
|
12
|
+
* - pine-voice CLI commands (auth setup/verify — manual fallback)
|
|
10
13
|
*
|
|
11
14
|
* Delegates all API calls to the pine-voice SDK. All safety, billing,
|
|
12
15
|
* and prompt logic lives on Pine's side.
|
|
@@ -15,9 +18,11 @@ import { registerAuthCommands } from "./auth.js";
|
|
|
15
18
|
* sessions_spawn so the main agent stays responsive during long calls.
|
|
16
19
|
*/
|
|
17
20
|
export default function register(api) {
|
|
18
|
-
// Register voice call tools (initiate + status)
|
|
21
|
+
// Register voice call tools (initiate + status + wait)
|
|
19
22
|
registerVoiceCallTools(api);
|
|
20
|
-
// Register
|
|
23
|
+
// Register auth tools (conversational flow — primary)
|
|
24
|
+
registerAuthTools(api);
|
|
25
|
+
// Register CLI commands for authentication (manual fallback)
|
|
21
26
|
registerAuthCommands(api);
|
|
22
27
|
api.log?.info?.("pine-voice: plugin loaded");
|
|
23
28
|
}
|
package/dist/tool.js
CHANGED
|
@@ -2,34 +2,13 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
import { PineVoice, AuthError } from "pine-voice";
|
|
3
3
|
/** Auth error message returned when credentials are missing. */
|
|
4
4
|
const AUTH_MISSING_MESSAGE = [
|
|
5
|
-
"Pine Voice is not authenticated
|
|
6
|
-
"",
|
|
7
|
-
"
|
|
8
|
-
"",
|
|
9
|
-
" # Step 1: Request a verification code (sent to your Pine AI account email)",
|
|
10
|
-
" openclaw pine-voice auth setup --email <USER_EMAIL>",
|
|
11
|
-
"",
|
|
12
|
-
" # Step 2: Enter the code and request token from Step 1 to get your user ID and access token",
|
|
13
|
-
" openclaw pine-voice auth verify --email <USER_EMAIL> --request-token <TOKEN> --code <CODE>",
|
|
14
|
-
"",
|
|
15
|
-
" # Step 3: Add both values to your plugin config in openclaw.json:",
|
|
16
|
-
' # plugins.entries.openclaw-pine-voice.config.user_id = "<USER_ID>"',
|
|
17
|
-
' # plugins.entries.openclaw-pine-voice.config.access_token = "<TOKEN>"',
|
|
18
|
-
"",
|
|
19
|
-
" # Step 4: Restart the gateway",
|
|
20
|
-
" openclaw gateway restart",
|
|
21
|
-
"",
|
|
22
|
-
"Ask the user for their Pine AI account email to begin. If they don't have a Pine AI account, they can sign up at https://19pine.ai.",
|
|
5
|
+
"Pine Voice is not authenticated. To set up, use the pine_voice_auth_request tool",
|
|
6
|
+
"with the user's Pine AI email address. If they don't have an account, they can",
|
|
7
|
+
"sign up at https://19pine.ai.",
|
|
23
8
|
].join("\n");
|
|
24
9
|
const AUTH_EXPIRED_MESSAGE = [
|
|
25
|
-
"Pine Voice authentication has expired
|
|
26
|
-
"",
|
|
27
|
-
"To re-authenticate, run:",
|
|
28
|
-
" openclaw pine-voice auth setup --email <USER_EMAIL>",
|
|
29
|
-
" openclaw pine-voice auth verify --email <USER_EMAIL> --request-token <TOKEN> --code <CODE>",
|
|
30
|
-
"",
|
|
31
|
-
"Then update user_id and access_token in openclaw.json and restart the gateway.",
|
|
32
|
-
"Ask the user for their Pine AI account email to begin.",
|
|
10
|
+
"Pine Voice authentication has expired. Use pine_voice_auth_request with the",
|
|
11
|
+
"user's email to re-authenticate.",
|
|
33
12
|
].join("\n");
|
|
34
13
|
/** Read plugin config and create a PineVoice SDK client. Returns error response if not authenticated. */
|
|
35
14
|
function getClientOrError(api) {
|
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.1.
|
|
4
|
+
"version": "0.1.7",
|
|
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,11 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pine-voice-auth
|
|
3
|
-
description: Set up or refresh Pine Voice authentication
|
|
3
|
+
description: Set up or refresh Pine Voice authentication via the built-in auth tools.
|
|
4
4
|
metadata:
|
|
5
|
-
{
|
|
6
|
-
"openclaw":
|
|
7
|
-
{ "emoji": "🔑", "requires": { "bins": ["curl", "jq"] } },
|
|
8
|
-
}
|
|
5
|
+
{ "openclaw": { "emoji": "🔑" } }
|
|
9
6
|
---
|
|
10
7
|
|
|
11
8
|
# Pine Voice Auth Setup
|
|
@@ -15,7 +12,7 @@ metadata:
|
|
|
15
12
|
Use this skill when **any** of these are true:
|
|
16
13
|
|
|
17
14
|
- The user asks to set up Pine Voice, configure Pine AI, or authenticate for voice calls
|
|
18
|
-
- A `pine_voice_call` or `pine_voice_call_and_wait` invocation returns "Pine Voice is not authenticated
|
|
15
|
+
- A `pine_voice_call` or `pine_voice_call_and_wait` invocation returns "Pine Voice is not authenticated"
|
|
19
16
|
- A `pine_voice_call` or `pine_voice_call_and_wait` invocation fails with `TOKEN_EXPIRED`, `UNAUTHORIZED`, or a 401 response
|
|
20
17
|
- The user says their Pine Voice token isn't working
|
|
21
18
|
|
|
@@ -24,7 +21,6 @@ Do **not** use this skill for making phone calls — see the `pine-voice` skill
|
|
|
24
21
|
## Prerequisites
|
|
25
22
|
|
|
26
23
|
- The user must have a **Pine AI account** with a Pro subscription (sign up at https://19pine.ai)
|
|
27
|
-
- `curl` and `jq` must be available in the shell
|
|
28
24
|
|
|
29
25
|
## Important: email verification requires user presence
|
|
30
26
|
|
|
@@ -32,16 +28,6 @@ This auth flow sends a verification code to the user's email inbox. The user **m
|
|
|
32
28
|
|
|
33
29
|
**Recommended timing:** Run this flow right after plugin installation or when the user explicitly asks to set up Pine Voice — not during an unattended or automated workflow.
|
|
34
30
|
|
|
35
|
-
## Auth flow overview
|
|
36
|
-
|
|
37
|
-
Pine Voice uses email-based verification. The flow has two API calls with a human step in between:
|
|
38
|
-
|
|
39
|
-
1. **Request** — send the user's email to get a `request_token`
|
|
40
|
-
2. **Wait** — user checks their email for a verification code
|
|
41
|
-
3. **Verify** — send email + request_token + code to get a `user_id` and `access_token`
|
|
42
|
-
4. **Store & restart** — write both values via `openclaw config set` and restart the gateway
|
|
43
|
-
5. **Test** — make a test call to verify everything works
|
|
44
|
-
|
|
45
31
|
## Step-by-step instructions
|
|
46
32
|
|
|
47
33
|
### Step 1: Ask the user for their Pine AI email
|
|
@@ -50,27 +36,15 @@ Ask: "What email address is your Pine AI account registered with?"
|
|
|
50
36
|
|
|
51
37
|
Do not proceed until you have the email.
|
|
52
38
|
|
|
53
|
-
### Step 2:
|
|
39
|
+
### Step 2: Send a verification code
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
Call the `pine_voice_auth_request` tool with the user's email:
|
|
56
42
|
|
|
57
|
-
```bash
|
|
58
|
-
curl -s -X POST https://www.19pine.ai/api/v2/auth/email/request \
|
|
59
|
-
-H "Content-Type: application/json" \
|
|
60
|
-
-d '{"email": "USER_EMAIL"}' | jq .
|
|
61
43
|
```
|
|
62
|
-
|
|
63
|
-
**Expected response:**
|
|
64
|
-
|
|
65
|
-
```json
|
|
66
|
-
{"status": "success", "data": {"request_token": "abc123..."}}
|
|
44
|
+
pine_voice_auth_request({ email: "user@example.com" })
|
|
67
45
|
```
|
|
68
46
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
**If the request fails:**
|
|
72
|
-
- `400` or `422` — the email may not be registered. Ask the user to check their email or sign up at https://19pine.ai.
|
|
73
|
-
- Network error — check connectivity and retry.
|
|
47
|
+
This sends a verification code to their email. If the request fails with a 400/422, the email may not be registered — ask the user to check their email or sign up at https://19pine.ai.
|
|
74
48
|
|
|
75
49
|
### Step 3: Ask the user for the verification code
|
|
76
50
|
|
|
@@ -78,53 +52,35 @@ Tell the user: "I've sent a verification code to your email. Please check your i
|
|
|
78
52
|
|
|
79
53
|
Wait for the user to provide the code. Do not guess or skip this step.
|
|
80
54
|
|
|
81
|
-
### Step 4: Verify the code and
|
|
55
|
+
### Step 4: Verify the code and save credentials
|
|
82
56
|
|
|
83
|
-
|
|
57
|
+
Call the `pine_voice_auth_verify` tool with the email and code:
|
|
84
58
|
|
|
85
|
-
```bash
|
|
86
|
-
curl -s -X POST https://www.19pine.ai/api/v2/auth/email/verify \
|
|
87
|
-
-H "Content-Type: application/json" \
|
|
88
|
-
-d '{"email": "USER_EMAIL", "request_token": "THE_REQUEST_TOKEN", "code": "THE_CODE"}' | jq .
|
|
89
59
|
```
|
|
90
|
-
|
|
91
|
-
**Expected response:**
|
|
92
|
-
|
|
93
|
-
```json
|
|
94
|
-
{"id": "1234567890", "access_token": "eyJ...", ...}
|
|
60
|
+
pine_voice_auth_verify({ email: "user@example.com", code: "123456" })
|
|
95
61
|
```
|
|
96
62
|
|
|
97
|
-
|
|
98
|
-
- **`id`** — the user's Pine user ID
|
|
99
|
-
- **`access_token`** — the access token
|
|
63
|
+
The tool verifies the code, saves the credentials to `~/.openclaw/openclaw.json` automatically, and returns a success message.
|
|
100
64
|
|
|
101
65
|
**If verification fails:**
|
|
102
|
-
-
|
|
103
|
-
-
|
|
66
|
+
- Invalid code — ask the user to double-check the code and try again (call `pine_voice_auth_verify` with the corrected code).
|
|
67
|
+
- Expired token — go back to step 2 to send a new code.
|
|
104
68
|
|
|
105
|
-
### Step 5:
|
|
69
|
+
### Step 5: Restart the gateway
|
|
106
70
|
|
|
107
|
-
|
|
71
|
+
Tell the user to restart the gateway for the new credentials to take effect:
|
|
108
72
|
|
|
109
|
-
|
|
73
|
+
"Credentials saved! Please run this command to activate them:"
|
|
110
74
|
|
|
111
|
-
```bash
|
|
112
|
-
openclaw config set plugins.entries.openclaw-pine-voice.config.access_token "THE_ACCESS_TOKEN"
|
|
113
|
-
openclaw config set plugins.entries.openclaw-pine-voice.config.user_id '"THE_USER_ID"' --json
|
|
114
|
-
openclaw gateway restart
|
|
115
75
|
```
|
|
116
|
-
|
|
117
|
-
You can verify the stored values with:
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
openclaw config get plugins.entries.openclaw-pine-voice.config
|
|
76
|
+
openclaw gateway restart
|
|
121
77
|
```
|
|
122
78
|
|
|
123
|
-
|
|
79
|
+
The gateway **must be restarted** after authentication for the new credentials to take effect.
|
|
124
80
|
|
|
125
81
|
### Step 6: Verify with a test call
|
|
126
82
|
|
|
127
|
-
After
|
|
83
|
+
After the gateway has restarted, suggest the user make a test call to their own phone number to verify everything works end-to-end:
|
|
128
84
|
|
|
129
85
|
"Would you like to test it? I can call your phone to confirm everything is working. Just tell me your phone number."
|
|
130
86
|
|
|
@@ -145,6 +101,6 @@ Access tokens expire periodically. When a call fails with `TOKEN_EXPIRED` or a 4
|
|
|
145
101
|
|
|
146
102
|
## Security notes
|
|
147
103
|
|
|
148
|
-
- Never log or echo the access token in plaintext beyond what is needed
|
|
104
|
+
- Never log or echo the access token in plaintext beyond what is needed
|
|
149
105
|
- The token is stored in a local config file with the same permissions as the user's home directory
|
|
150
106
|
- Do not commit config files containing tokens to version control
|