@clawhive/openclaw-plugin 0.2.4 → 0.2.5
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 +23 -2
- package/dist/src/authorization.d.ts +10 -0
- package/dist/src/authorization.js +58 -3
- package/dist/src/client.d.ts +8 -0
- package/dist/src/client.js +14 -0
- package/dist/src/config.d.ts +5 -0
- package/dist/src/config.js +15 -0
- package/dist/src/deviceCode.js +2 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ After plugin install, run:
|
|
|
33
33
|
openclaw clawhive authorize
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The command prints
|
|
36
|
+
The command prints an app authorization QR plus fallback `user_code`, waits for approval in the signed-in ClawHive app, and persists the resulting `pluginToken` and `userId` into the OpenClaw config. Then restart the gateway.
|
|
37
37
|
|
|
38
38
|
On the next `openclaw gateway run`:
|
|
39
39
|
|
|
@@ -44,6 +44,25 @@ On the next `openclaw gateway run`:
|
|
|
44
44
|
|
|
45
45
|
No shared dev secret exists; each install owns its own credential and can be revoked independently.
|
|
46
46
|
|
|
47
|
+
### Move this runtime to another account
|
|
48
|
+
|
|
49
|
+
To remove this OpenClaw installation from its current ClawHive account, run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
openclaw clawhive unlink --yes
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This revokes the current plugin token and removes its cloud node from the old
|
|
56
|
+
account. Stop or restart the gateway, then run `openclaw clawhive authorize`
|
|
57
|
+
and approve the QR in the new account's signed-in App. The new account gets a
|
|
58
|
+
new node; the old account's agents, conversations, and history are not moved.
|
|
59
|
+
|
|
60
|
+
The phone device is shared by OpenClaw and Hermes. If OpenClaw is the last
|
|
61
|
+
runtime using it, unlink releases the phone so the original or a different
|
|
62
|
+
account can authorize next. If Hermes still uses the phone, OpenClaw can be
|
|
63
|
+
authorized again only by the same account; unlink Hermes as well before moving
|
|
64
|
+
the shared phone to another account.
|
|
65
|
+
|
|
47
66
|
## Configuration
|
|
48
67
|
|
|
49
68
|
Minimum managed-cloud install: **nothing**. The plugin falls back to the baked `DEFAULT_PROJECT_URL` in `src/defaults.ts`, silently bootstraps, and writes everything else into config.
|
|
@@ -126,7 +145,9 @@ The current OpenClaw SDK surface does not yet expose a stable local marketplace
|
|
|
126
145
|
## Manual Commands
|
|
127
146
|
|
|
128
147
|
- `openclaw clawhive authorize`
|
|
129
|
-
Prints the
|
|
148
|
+
Prints the app authorization QR and fallback device user code, then waits for authorization.
|
|
149
|
+
- `openclaw clawhive unlink`
|
|
150
|
+
Shows the account-transfer impact without changing anything. Add `--yes` to revoke this runtime authorization, remove its node from the current account, and clear the local identity.
|
|
130
151
|
- `openclaw clawhive pair`
|
|
131
152
|
Prints a fresh mobile pairing QR plus short code on demand so you can re-pair a phone after reinstall, add another phone, or recover if you missed the startup banner.
|
|
132
153
|
- `openclaw clawhive pair --ttl-seconds 600`
|
|
@@ -19,6 +19,9 @@ type AuthorizationDeps = {
|
|
|
19
19
|
printDeviceCodePrompt?: typeof printDeviceCodePrompt;
|
|
20
20
|
awaitDeviceCodeApproval?: typeof awaitDeviceCodeApproval;
|
|
21
21
|
};
|
|
22
|
+
type UnlinkDeps = {
|
|
23
|
+
createCloudApi?: typeof createClawHiveCloudApi;
|
|
24
|
+
};
|
|
22
25
|
/**
|
|
23
26
|
* Print a fresh mobile pairing QR and short code on demand.
|
|
24
27
|
*/
|
|
@@ -27,6 +30,13 @@ export declare function printManualPairingCode(api: OpenClawPluginApi, ttlSecond
|
|
|
27
30
|
* Run the explicit device-authorization command for the ClawHive plugin.
|
|
28
31
|
*/
|
|
29
32
|
export declare function authorizeClawHivePlugin(api: OpenClawPluginApi, deps?: AuthorizationDeps): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Revoke this runtime's current account authorization before it is approved
|
|
35
|
+
* from a different signed-in mobile App account.
|
|
36
|
+
*/
|
|
37
|
+
export declare function unlinkClawHivePlugin(api: OpenClawPluginApi, options: {
|
|
38
|
+
yes?: boolean;
|
|
39
|
+
}, deps?: UnlinkDeps): Promise<void>;
|
|
30
40
|
/**
|
|
31
41
|
* Register the top-level `openclaw clawhive` CLI group for manual plugin authorization.
|
|
32
42
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClawHiveCloudApi, ClawHiveApiError, DeviceCodeDeniedError, DeviceCodeExpiredError, } from "./client.js";
|
|
2
|
-
import { resolveResolvedAccount, writeRegistrationIdentity } from "./config.js";
|
|
2
|
+
import { clearAuthorizationIdentity, resolveResolvedAccount, writeRegistrationIdentity, } from "./config.js";
|
|
3
3
|
import { awaitDeviceCodeApproval, printDeviceCodePrompt, } from "./deviceCode.js";
|
|
4
4
|
import { getPairedDeviceCount, printPairingQr } from "./pairing.js";
|
|
5
5
|
/**
|
|
@@ -28,7 +28,7 @@ export function createClawHiveCloudApi(api) {
|
|
|
28
28
|
*/
|
|
29
29
|
export function printAuthorizationCommandHint(logger) {
|
|
30
30
|
logger.warn("ClawHive plugin is installed but not authorized yet.");
|
|
31
|
-
logger.info("Run `openclaw clawhive authorize` in your terminal to display
|
|
31
|
+
logger.info("Run `openclaw clawhive authorize` in your terminal to display an app authorization QR and fallback code.");
|
|
32
32
|
logger.info("If the command reports a missing anon key, set `channels.clawhive.anonKey` in config or export `CLAWHIVE_SUPABASE_ANON_KEY` first.");
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -145,6 +145,54 @@ export async function authorizeClawHivePlugin(api, deps = {}) {
|
|
|
145
145
|
throw error;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Revoke this runtime's current account authorization before it is approved
|
|
150
|
+
* from a different signed-in mobile App account.
|
|
151
|
+
*/
|
|
152
|
+
export async function unlinkClawHivePlugin(api, options, deps = {}) {
|
|
153
|
+
if (!options.yes) {
|
|
154
|
+
api.logger.warn("This removes the current OpenClaw runtime from its ClawHive account. No changes were made.");
|
|
155
|
+
api.logger.info("Run openclaw clawhive unlink --yes to confirm.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const { account, cloudApi } = (deps.createCloudApi ?? createClawHiveCloudApi)(api);
|
|
159
|
+
const clearLocalIdentity = async () => {
|
|
160
|
+
const latestCfg = api.runtime.config.loadConfig();
|
|
161
|
+
await api.runtime.config.writeConfigFile(clearAuthorizationIdentity(latestCfg));
|
|
162
|
+
};
|
|
163
|
+
if (!account.pluginToken || !account.pluginNodeId) {
|
|
164
|
+
await clearLocalIdentity();
|
|
165
|
+
api.logger.info("ClawHive local authorization was already incomplete and has been cleared. Run openclaw clawhive authorize to connect an account.");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const result = await cloudApi.unlinkPluginNode();
|
|
170
|
+
await clearLocalIdentity();
|
|
171
|
+
api.logger.info(result.node_removed
|
|
172
|
+
? "ClawHive runtime authorization revoked and node removed from the current account."
|
|
173
|
+
: "ClawHive runtime authorization was already removed from the current account.");
|
|
174
|
+
if (result.device_released) {
|
|
175
|
+
api.logger.info("ClawHive shared phone device was released. This runtime can now be authorized by the original or a different ClawHive account.");
|
|
176
|
+
}
|
|
177
|
+
else if (result.device_retained_reason === "other_runtime_linked") {
|
|
178
|
+
const runtimes = result.remaining_runtime_types
|
|
179
|
+
.map((runtime) => runtime === "hermes" ? "Hermes" : "OpenClaw")
|
|
180
|
+
.join(" and ");
|
|
181
|
+
api.logger.warn(`${runtimes || "Another runtime"} still uses this phone. Reauthorize OpenClaw with the same account, or unlink every remaining runtime before moving the phone to another account.`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (!isInvalidStoredPluginTokenError(error)) {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
await clearLocalIdentity();
|
|
189
|
+
api.logger.info("ClawHive stored authorization was already invalid and has been cleared locally.");
|
|
190
|
+
}
|
|
191
|
+
api.logger.info("Stop or restart the OpenClaw gateway, then run openclaw clawhive authorize and approve with the new account in the ClawHive app.");
|
|
192
|
+
if (process.env.CLAWHIVE_PLUGIN_TOKEN) {
|
|
193
|
+
api.logger.warn("CLAWHIVE_PLUGIN_TOKEN is set in this shell. Run unset CLAWHIVE_PLUGIN_TOKEN to avoid using the old credential.");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
148
196
|
function isInvalidStoredPluginTokenError(error) {
|
|
149
197
|
if (!(error instanceof ClawHiveApiError)) {
|
|
150
198
|
return false;
|
|
@@ -165,10 +213,17 @@ export function registerClawHiveCli(api) {
|
|
|
165
213
|
.description("Manage ClawHive plugin authorization and pairing");
|
|
166
214
|
clawhive
|
|
167
215
|
.command("authorize")
|
|
168
|
-
.description("Display
|
|
216
|
+
.description("Display an app authorization QR and fallback code, then wait for plugin authorization")
|
|
169
217
|
.action(async () => {
|
|
170
218
|
await authorizeClawHivePlugin(api);
|
|
171
219
|
});
|
|
220
|
+
clawhive
|
|
221
|
+
.command("unlink")
|
|
222
|
+
.description("Revoke this OpenClaw runtime authorization and remove it from the current ClawHive account")
|
|
223
|
+
.option("--yes", "Confirm unlinking this runtime")
|
|
224
|
+
.action(async (options) => {
|
|
225
|
+
await unlinkClawHivePlugin(api, options);
|
|
226
|
+
});
|
|
172
227
|
clawhive
|
|
173
228
|
.command("pair")
|
|
174
229
|
.description("Print a fresh mobile pairing QR code and short code")
|
package/dist/src/client.d.ts
CHANGED
|
@@ -58,6 +58,13 @@ export type PluginRegisterResult = {
|
|
|
58
58
|
heartbeat_interval: number;
|
|
59
59
|
user_session: UserSession;
|
|
60
60
|
};
|
|
61
|
+
export type PluginUnlinkResult = {
|
|
62
|
+
unlinked: true;
|
|
63
|
+
node_removed: boolean;
|
|
64
|
+
device_released: boolean;
|
|
65
|
+
device_retained_reason: "other_runtime_linked" | "device_not_found" | null;
|
|
66
|
+
remaining_runtime_types: Array<"openclaw" | "hermes">;
|
|
67
|
+
};
|
|
61
68
|
export type PairingCodeResult = {
|
|
62
69
|
pair_token: string;
|
|
63
70
|
expires_at: string;
|
|
@@ -195,6 +202,7 @@ export declare class ClawHiveCloudApi {
|
|
|
195
202
|
}): Promise<DeviceCodeStartResult>;
|
|
196
203
|
exchangeDeviceCode(deviceCode: string): Promise<DeviceCodeExchangeResult>;
|
|
197
204
|
registerPluginNode(): Promise<PluginRegisterResult>;
|
|
205
|
+
unlinkPluginNode(): Promise<PluginUnlinkResult>;
|
|
198
206
|
createPairingCode(pluginNodeId: string, ttlSeconds?: number): Promise<PairingCodeResult>;
|
|
199
207
|
heartbeat(params: {
|
|
200
208
|
pluginNodeId: string;
|
package/dist/src/client.js
CHANGED
|
@@ -148,6 +148,20 @@ export class ClawHiveCloudApi {
|
|
|
148
148
|
this.config.pluginNodeId = result.plugin_node.id;
|
|
149
149
|
return result;
|
|
150
150
|
}
|
|
151
|
+
async unlinkPluginNode() {
|
|
152
|
+
if (!this.config.pluginNodeId) {
|
|
153
|
+
throw new Error("ClawHive plugin node id is missing");
|
|
154
|
+
}
|
|
155
|
+
return this.request({
|
|
156
|
+
path: "plugin-unlink",
|
|
157
|
+
method: "POST",
|
|
158
|
+
auth: "plugin",
|
|
159
|
+
body: {
|
|
160
|
+
plugin_node_id: this.config.pluginNodeId,
|
|
161
|
+
runtime_type: "openclaw",
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
151
165
|
async createPairingCode(pluginNodeId, ttlSeconds) {
|
|
152
166
|
return this.request({
|
|
153
167
|
path: "pairing-code-create",
|
package/dist/src/config.d.ts
CHANGED
|
@@ -26,3 +26,8 @@ export declare function writeRegistrationIdentity(cfg: OpenClawConfig, patch: {
|
|
|
26
26
|
pluginToken?: string;
|
|
27
27
|
anonKey?: string;
|
|
28
28
|
}): OpenClawConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Remove only the locally persisted identity for this ClawHive runtime.
|
|
31
|
+
* Connection defaults and publishing preferences survive account transfer.
|
|
32
|
+
*/
|
|
33
|
+
export declare function clearAuthorizationIdentity(cfg: OpenClawConfig): OpenClawConfig;
|
package/dist/src/config.js
CHANGED
|
@@ -79,6 +79,21 @@ export function writeRegistrationIdentity(cfg, patch) {
|
|
|
79
79
|
next.anonKey = patch.anonKey;
|
|
80
80
|
return patchChannelSection(cfg, next);
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove only the locally persisted identity for this ClawHive runtime.
|
|
84
|
+
* Connection defaults and publishing preferences survive account transfer.
|
|
85
|
+
*/
|
|
86
|
+
export function clearAuthorizationIdentity(cfg) {
|
|
87
|
+
const { pluginToken: _pluginToken, userId: _userId, pluginNodeId: _pluginNodeId, ...remainingSection } = readRawChannelSection(cfg);
|
|
88
|
+
const channels = cfg.channels ?? {};
|
|
89
|
+
return {
|
|
90
|
+
...cfg,
|
|
91
|
+
channels: {
|
|
92
|
+
...channels,
|
|
93
|
+
[CLAWHIVE_CHANNEL_ID]: remainingSection,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
82
97
|
function patchChannelSection(cfg, patch) {
|
|
83
98
|
const nextChannels = {
|
|
84
99
|
...(cfg.channels ?? {}),
|
package/dist/src/deviceCode.js
CHANGED
|
@@ -13,19 +13,16 @@ export function normalizeDeviceCodeUrl(url) {
|
|
|
13
13
|
return parsed.toString();
|
|
14
14
|
}
|
|
15
15
|
export async function printDeviceCodePrompt(start, logger) {
|
|
16
|
-
const
|
|
17
|
-
const verificationUriComplete = normalizeDeviceCodeUrl(start.verification_uri_complete);
|
|
18
|
-
const qr = await renderQr(verificationUriComplete);
|
|
16
|
+
const qr = await renderQr(`clawhive-auth:v1:${start.device_code}`);
|
|
19
17
|
const expiresInMin = Math.floor(start.expires_in / 60);
|
|
20
18
|
const lines = [
|
|
21
19
|
"",
|
|
22
20
|
"════════════════════════════════════════════════════════════",
|
|
23
21
|
" ClawHive: authorize this OpenClaw install.",
|
|
24
22
|
"",
|
|
25
|
-
` Visit: ${verificationUri}`,
|
|
26
23
|
` Enter the code: ${start.user_code}`,
|
|
27
24
|
"",
|
|
28
|
-
"
|
|
25
|
+
" Scan this QR with the signed-in ClawHive mobile app:",
|
|
29
26
|
qr,
|
|
30
27
|
` (Expires in ${expiresInMin}m; polling for approval…)`,
|
|
31
28
|
"════════════════════════════════════════════════════════════",
|