@clawhive/openclaw-plugin 0.2.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 +217 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.js +6 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +324 -0
- package/dist/runtime-api.d.ts +1 -0
- package/dist/runtime-api.js +1 -0
- package/dist/setup-entry.d.ts +4 -0
- package/dist/setup-entry.js +3 -0
- package/dist/src/agency-install/claimLoop.d.ts +41 -0
- package/dist/src/agency-install/claimLoop.js +188 -0
- package/dist/src/agent-panel/claimLoop.d.ts +32 -0
- package/dist/src/agent-panel/claimLoop.js +118 -0
- package/dist/src/agent-panel/executor.d.ts +8 -0
- package/dist/src/agent-panel/executor.js +368 -0
- package/dist/src/agent-panel/plan.d.ts +20 -0
- package/dist/src/agent-panel/plan.js +111 -0
- package/dist/src/agent-panel/prompts.d.ts +38 -0
- package/dist/src/agent-panel/prompts.js +87 -0
- package/dist/src/agent-panel/types.d.ts +45 -0
- package/dist/src/agent-panel/types.js +1 -0
- package/dist/src/agents.d.ts +44 -0
- package/dist/src/agents.js +195 -0
- package/dist/src/authorization.d.ts +34 -0
- package/dist/src/authorization.js +183 -0
- package/dist/src/channel.d.ts +5 -0
- package/dist/src/channel.js +93 -0
- package/dist/src/claimPolling.d.ts +9 -0
- package/dist/src/claimPolling.js +13 -0
- package/dist/src/client.d.ts +386 -0
- package/dist/src/client.js +595 -0
- package/dist/src/config.d.ts +28 -0
- package/dist/src/config.js +94 -0
- package/dist/src/defaults.d.ts +40 -0
- package/dist/src/defaults.js +52 -0
- package/dist/src/deviceCode.d.ts +16 -0
- package/dist/src/deviceCode.js +65 -0
- package/dist/src/heartbeat.d.ts +25 -0
- package/dist/src/heartbeat.js +65 -0
- package/dist/src/imageAttachments.d.ts +14 -0
- package/dist/src/imageAttachments.js +81 -0
- package/dist/src/inbound.d.ts +10 -0
- package/dist/src/inbound.js +140 -0
- package/dist/src/installMutex.d.ts +4 -0
- package/dist/src/installMutex.js +18 -0
- package/dist/src/market-install/claimLoop.d.ts +41 -0
- package/dist/src/market-install/claimLoop.js +183 -0
- package/dist/src/market-install/downloader.d.ts +36 -0
- package/dist/src/market-install/downloader.js +133 -0
- package/dist/src/market-install/executor.d.ts +28 -0
- package/dist/src/market-install/executor.js +352 -0
- package/dist/src/market-install/types.d.ts +62 -0
- package/dist/src/market-install/types.js +1 -0
- package/dist/src/market-install/verifier.d.ts +32 -0
- package/dist/src/market-install/verifier.js +60 -0
- package/dist/src/market-publish/packager.d.ts +34 -0
- package/dist/src/market-publish/packager.js +168 -0
- package/dist/src/market-publish/publishFlow.d.ts +70 -0
- package/dist/src/market-publish/publishFlow.js +107 -0
- package/dist/src/market-publish/uploader.d.ts +73 -0
- package/dist/src/market-publish/uploader.js +132 -0
- package/dist/src/openclawVersion.d.ts +4 -0
- package/dist/src/openclawVersion.js +13 -0
- package/dist/src/outbound.d.ts +13 -0
- package/dist/src/outbound.js +41 -0
- package/dist/src/pairing.d.ts +32 -0
- package/dist/src/pairing.js +64 -0
- package/dist/src/runtime.d.ts +52 -0
- package/dist/src/runtime.js +26 -0
- package/dist/src/telemetry.d.ts +34 -0
- package/dist/src/telemetry.js +89 -0
- package/dist/src/transport/realtime.d.ts +49 -0
- package/dist/src/transport/realtime.js +273 -0
- package/dist/src/transport.d.ts +38 -0
- package/dist/src/transport.js +15 -0
- package/dist/src/wake.d.ts +31 -0
- package/dist/src/wake.js +101 -0
- package/openclaw.config.example.yaml +10 -0
- package/openclaw.plugin.json +122 -0
- package/package.json +84 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
export class DeviceCodeExpiredError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super("Device code expired before the user approved it");
|
|
4
|
+
this.name = "DeviceCodeExpiredError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class DeviceCodeDeniedError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("The user denied the device authorization request");
|
|
10
|
+
this.name = "DeviceCodeDeniedError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Preserve low-level transport details so gateway logs show the real fetch cause.
|
|
15
|
+
*/
|
|
16
|
+
function formatTransportError(error) {
|
|
17
|
+
if (!(error instanceof Error)) {
|
|
18
|
+
return String(error);
|
|
19
|
+
}
|
|
20
|
+
const details = [error.message];
|
|
21
|
+
const cause = error.cause;
|
|
22
|
+
if (cause instanceof Error) {
|
|
23
|
+
details.push(`cause=${cause.name}: ${cause.message}`);
|
|
24
|
+
}
|
|
25
|
+
else if (cause !== undefined) {
|
|
26
|
+
details.push(`cause=${String(cause)}`);
|
|
27
|
+
}
|
|
28
|
+
const errorWithCode = error;
|
|
29
|
+
if (typeof errorWithCode.code === "string") {
|
|
30
|
+
details.push(`code=${errorWithCode.code}`);
|
|
31
|
+
}
|
|
32
|
+
return details.join("; ");
|
|
33
|
+
}
|
|
34
|
+
function mapPanelContribution(raw) {
|
|
35
|
+
return {
|
|
36
|
+
id: String(raw.id),
|
|
37
|
+
agentId: String(raw.agentId ?? raw.agent_id),
|
|
38
|
+
round: raw.round,
|
|
39
|
+
status: raw.status,
|
|
40
|
+
assignment: raw.assignment ?? null,
|
|
41
|
+
content: raw.content ?? null,
|
|
42
|
+
errorMessage: raw.errorMessage ?? raw.error_message ?? null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export class ClawHiveApiError extends Error {
|
|
46
|
+
status;
|
|
47
|
+
errorCode;
|
|
48
|
+
details;
|
|
49
|
+
constructor(payload) {
|
|
50
|
+
super(payload.error);
|
|
51
|
+
this.name = "ClawHiveApiError";
|
|
52
|
+
this.status = payload.status;
|
|
53
|
+
this.errorCode = payload.error;
|
|
54
|
+
this.details = payload.details;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class ClawHiveCloudApi {
|
|
58
|
+
config;
|
|
59
|
+
constructor(config) {
|
|
60
|
+
this.config = config;
|
|
61
|
+
}
|
|
62
|
+
get baseUrl() {
|
|
63
|
+
return this.config.projectUrl.replace(/\/$/, "");
|
|
64
|
+
}
|
|
65
|
+
get userId() {
|
|
66
|
+
return this.config.userId ?? null;
|
|
67
|
+
}
|
|
68
|
+
get pluginToken() {
|
|
69
|
+
return this.config.pluginToken ?? null;
|
|
70
|
+
}
|
|
71
|
+
requireUserId() {
|
|
72
|
+
if (!this.config.userId) {
|
|
73
|
+
throw new Error("ClawHiveCloudApi: userId is not set. Call registerPluginNode() before making user-scoped calls.");
|
|
74
|
+
}
|
|
75
|
+
return this.config.userId;
|
|
76
|
+
}
|
|
77
|
+
requirePluginToken() {
|
|
78
|
+
if (!this.config.pluginToken) {
|
|
79
|
+
throw new Error("ClawHiveCloudApi: pluginToken is not set. Call bootstrap() first.");
|
|
80
|
+
}
|
|
81
|
+
return this.config.pluginToken;
|
|
82
|
+
}
|
|
83
|
+
async startDeviceCode(opts) {
|
|
84
|
+
return await this.request({
|
|
85
|
+
path: "plugin-device-code-start",
|
|
86
|
+
method: "POST",
|
|
87
|
+
auth: "none",
|
|
88
|
+
body: {
|
|
89
|
+
node_name: opts?.nodeName ?? this.config.nodeName ?? "OpenClaw Desktop",
|
|
90
|
+
plugin_version: opts?.pluginVersion ?? this.config.pluginVersion ?? "0.2.0",
|
|
91
|
+
description: opts?.description,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async exchangeDeviceCode(deviceCode) {
|
|
96
|
+
try {
|
|
97
|
+
const result = await this.request({
|
|
98
|
+
path: "plugin-device-code-exchange",
|
|
99
|
+
method: "POST",
|
|
100
|
+
auth: "none",
|
|
101
|
+
body: { device_code: deviceCode },
|
|
102
|
+
});
|
|
103
|
+
if (result.status === "approved") {
|
|
104
|
+
this.config.userId = result.user_id;
|
|
105
|
+
this.config.pluginToken = result.plugin_token;
|
|
106
|
+
return {
|
|
107
|
+
status: "approved",
|
|
108
|
+
user_id: result.user_id,
|
|
109
|
+
plugin_token: result.plugin_token,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return { status: "pending", interval: result.interval };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof ClawHiveApiError) {
|
|
116
|
+
if (error.errorCode === "expired_token") {
|
|
117
|
+
throw new DeviceCodeExpiredError();
|
|
118
|
+
}
|
|
119
|
+
if (error.errorCode === "access_denied") {
|
|
120
|
+
throw new DeviceCodeDeniedError();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async registerPluginNode() {
|
|
127
|
+
const body = {
|
|
128
|
+
plugin_node_id: this.config.pluginNodeId ?? null,
|
|
129
|
+
node_name: this.config.nodeName ?? "OpenClaw Desktop",
|
|
130
|
+
plugin_version: this.config.pluginVersion ?? "0.2.0",
|
|
131
|
+
};
|
|
132
|
+
if (this.config.userId) {
|
|
133
|
+
body.user_id = this.config.userId;
|
|
134
|
+
}
|
|
135
|
+
const result = await this.request({
|
|
136
|
+
path: "plugin-register",
|
|
137
|
+
method: "POST",
|
|
138
|
+
auth: "plugin",
|
|
139
|
+
body,
|
|
140
|
+
});
|
|
141
|
+
this.config.userId = result.user_id;
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
async createPairingCode(pluginNodeId, ttlSeconds) {
|
|
145
|
+
return this.request({
|
|
146
|
+
path: "pairing-code-create",
|
|
147
|
+
method: "POST",
|
|
148
|
+
auth: "plugin",
|
|
149
|
+
body: {
|
|
150
|
+
plugin_node_id: pluginNodeId,
|
|
151
|
+
ttl_seconds: ttlSeconds,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async heartbeat(params) {
|
|
156
|
+
return this.request({
|
|
157
|
+
path: "plugin-heartbeat",
|
|
158
|
+
method: "POST",
|
|
159
|
+
auth: "plugin",
|
|
160
|
+
body: {
|
|
161
|
+
plugin_node_id: params.pluginNodeId,
|
|
162
|
+
user_id: this.requireUserId(),
|
|
163
|
+
status: params.status ?? "online",
|
|
164
|
+
plugin_version: this.config.pluginVersion ?? "0.2.0",
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async writeAgentMessage(params) {
|
|
169
|
+
return this.request({
|
|
170
|
+
path: "plugin-message-write",
|
|
171
|
+
method: "POST",
|
|
172
|
+
auth: "plugin",
|
|
173
|
+
body: {
|
|
174
|
+
plugin_node_id: params.pluginNodeId,
|
|
175
|
+
session_id: params.sessionId,
|
|
176
|
+
agent_id: params.agentId,
|
|
177
|
+
user_id: this.requireUserId(),
|
|
178
|
+
content: params.content,
|
|
179
|
+
type: params.type,
|
|
180
|
+
metadata: params.metadata,
|
|
181
|
+
client_message_id: params.clientMessageId,
|
|
182
|
+
role: params.role ?? "agent",
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async syncAgents(agents) {
|
|
187
|
+
return this.request({
|
|
188
|
+
path: "plugin-agents-sync",
|
|
189
|
+
method: "POST",
|
|
190
|
+
auth: "plugin",
|
|
191
|
+
body: {
|
|
192
|
+
user_id: this.requireUserId(),
|
|
193
|
+
agents: agents.map((agent) => ({
|
|
194
|
+
openclaw_agent_id: agent.openclawAgentId,
|
|
195
|
+
name: agent.name,
|
|
196
|
+
description: agent.description ?? null,
|
|
197
|
+
avatar_url: agent.avatarUrl ?? null,
|
|
198
|
+
config: agent.config ?? {},
|
|
199
|
+
})),
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
async refreshUserSession(refreshToken) {
|
|
204
|
+
return this.request({
|
|
205
|
+
path: "auth-refresh",
|
|
206
|
+
method: "POST",
|
|
207
|
+
auth: "none",
|
|
208
|
+
body: { refreshToken },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async syncMessages(params) {
|
|
212
|
+
return this.request({
|
|
213
|
+
path: "messages-sync",
|
|
214
|
+
method: "GET",
|
|
215
|
+
auth: "user",
|
|
216
|
+
accessToken: params.accessToken,
|
|
217
|
+
query: {
|
|
218
|
+
session_id: params.sessionId,
|
|
219
|
+
after_sequence: params.afterSequence ?? 0,
|
|
220
|
+
limit: params.limit ?? 100,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
async getMessageAttachmentDownloadUrl(params) {
|
|
225
|
+
return this.request({
|
|
226
|
+
path: "message-attachment-download-url",
|
|
227
|
+
method: "GET",
|
|
228
|
+
auth: "plugin",
|
|
229
|
+
query: {
|
|
230
|
+
message_id: params.messageId,
|
|
231
|
+
storage_path: params.storagePath,
|
|
232
|
+
public_origin: this.baseUrl,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Claim the oldest queued install job for the current plugin node.
|
|
238
|
+
*/
|
|
239
|
+
async claimInstallJob(params) {
|
|
240
|
+
return this.request({
|
|
241
|
+
path: "market-install-claim",
|
|
242
|
+
method: "POST",
|
|
243
|
+
auth: "plugin",
|
|
244
|
+
body: {
|
|
245
|
+
pluginNodeId: params.pluginNodeId,
|
|
246
|
+
userId: params.userId ?? this.requireUserId(),
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Persist node-side install execution state back to the durable market job.
|
|
252
|
+
*/
|
|
253
|
+
async updateInstallJob(params) {
|
|
254
|
+
return this.request({
|
|
255
|
+
path: "market-install-update",
|
|
256
|
+
method: "POST",
|
|
257
|
+
auth: "plugin",
|
|
258
|
+
body: {
|
|
259
|
+
pluginNodeId: params.pluginNodeId,
|
|
260
|
+
jobId: params.jobId,
|
|
261
|
+
status: params.status,
|
|
262
|
+
userId: params.userId ?? this.requireUserId(),
|
|
263
|
+
reasonKind: params.reasonKind,
|
|
264
|
+
errorMessage: params.errorMessage,
|
|
265
|
+
details: params.details ?? {},
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Fetch the approved manifest payload for one claimed install job.
|
|
271
|
+
*/
|
|
272
|
+
async fetchInstallManifest(params) {
|
|
273
|
+
return this.request({
|
|
274
|
+
path: "market-install-manifest",
|
|
275
|
+
method: "POST",
|
|
276
|
+
auth: "plugin",
|
|
277
|
+
body: {
|
|
278
|
+
pluginNodeId: params.pluginNodeId,
|
|
279
|
+
jobId: params.jobId,
|
|
280
|
+
userId: params.userId ?? this.requireUserId(),
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Claim the oldest queued Agency install job for the current plugin node.
|
|
286
|
+
*/
|
|
287
|
+
async claimAgencyInstallJob(params) {
|
|
288
|
+
return this.request({
|
|
289
|
+
path: "agency-install-claim",
|
|
290
|
+
method: "POST",
|
|
291
|
+
auth: "plugin",
|
|
292
|
+
body: {
|
|
293
|
+
pluginNodeId: params.pluginNodeId,
|
|
294
|
+
userId: params.userId ?? this.requireUserId(),
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Persist node-side Agency install execution state back to the durable job.
|
|
300
|
+
*/
|
|
301
|
+
async updateAgencyInstallJob(params) {
|
|
302
|
+
return this.request({
|
|
303
|
+
path: "agency-install-update",
|
|
304
|
+
method: "POST",
|
|
305
|
+
auth: "plugin",
|
|
306
|
+
body: {
|
|
307
|
+
pluginNodeId: params.pluginNodeId,
|
|
308
|
+
jobId: params.jobId,
|
|
309
|
+
status: params.status,
|
|
310
|
+
userId: params.userId ?? this.requireUserId(),
|
|
311
|
+
reasonKind: params.reasonKind,
|
|
312
|
+
errorMessage: params.errorMessage,
|
|
313
|
+
details: params.details ?? {},
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Fetch the approved Agency manifest payload for one claimed install job.
|
|
319
|
+
*/
|
|
320
|
+
async fetchAgencyInstallManifest(params) {
|
|
321
|
+
return this.request({
|
|
322
|
+
path: "agency-install-manifest",
|
|
323
|
+
method: "POST",
|
|
324
|
+
auth: "plugin",
|
|
325
|
+
body: {
|
|
326
|
+
pluginNodeId: params.pluginNodeId,
|
|
327
|
+
jobId: params.jobId,
|
|
328
|
+
userId: params.userId ?? this.requireUserId(),
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
async createMarketListing(params) {
|
|
333
|
+
const body = {
|
|
334
|
+
name: params.name,
|
|
335
|
+
slug: params.slug,
|
|
336
|
+
status: params.status ?? "draft",
|
|
337
|
+
};
|
|
338
|
+
if (params.listingId)
|
|
339
|
+
body.listing_id = params.listingId;
|
|
340
|
+
if (params.authorDisplayName)
|
|
341
|
+
body.author_display_name = params.authorDisplayName;
|
|
342
|
+
if (params.summary)
|
|
343
|
+
body.summary = params.summary;
|
|
344
|
+
if (params.description)
|
|
345
|
+
body.description = params.description;
|
|
346
|
+
if (params.category)
|
|
347
|
+
body.category = params.category;
|
|
348
|
+
if (params.license)
|
|
349
|
+
body.license = params.license;
|
|
350
|
+
if (params.avatarUrl)
|
|
351
|
+
body.avatar_url = params.avatarUrl;
|
|
352
|
+
if (params.tags)
|
|
353
|
+
body.tags = params.tags;
|
|
354
|
+
if (params.sourceNodeId)
|
|
355
|
+
body.source_node_id = params.sourceNodeId;
|
|
356
|
+
return this.request({
|
|
357
|
+
path: "market-listings",
|
|
358
|
+
method: "POST",
|
|
359
|
+
auth: "user",
|
|
360
|
+
accessToken: params.accessToken,
|
|
361
|
+
body,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
async createMarketRelease(params) {
|
|
365
|
+
const body = {
|
|
366
|
+
listing_id: params.listingId,
|
|
367
|
+
version: params.version,
|
|
368
|
+
storage_path: params.storagePath,
|
|
369
|
+
fingerprint: params.fingerprint,
|
|
370
|
+
source_type: params.sourceType,
|
|
371
|
+
source_ref: params.sourceRef,
|
|
372
|
+
publish: params.publish,
|
|
373
|
+
};
|
|
374
|
+
if (params.minPluginVersion)
|
|
375
|
+
body.min_plugin_version = params.minPluginVersion;
|
|
376
|
+
if (params.minOpenClawVersion)
|
|
377
|
+
body.min_openclaw_version = params.minOpenClawVersion;
|
|
378
|
+
if (params.compatibilityNotes)
|
|
379
|
+
body.compatibility_notes = params.compatibilityNotes;
|
|
380
|
+
return this.request({
|
|
381
|
+
path: "market-releases-create",
|
|
382
|
+
method: "POST",
|
|
383
|
+
auth: "user",
|
|
384
|
+
accessToken: params.accessToken,
|
|
385
|
+
body,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
async claimPanelTurn(params) {
|
|
389
|
+
return this.request({
|
|
390
|
+
path: "agent-panel-turn-claim",
|
|
391
|
+
method: "POST",
|
|
392
|
+
auth: "plugin",
|
|
393
|
+
body: {
|
|
394
|
+
pluginNodeId: params.pluginNodeId,
|
|
395
|
+
userId: params.userId ?? this.requireUserId(),
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
async updatePanelContribution(params) {
|
|
400
|
+
await this.request({
|
|
401
|
+
path: "agent-panel-contribution-update",
|
|
402
|
+
method: "POST",
|
|
403
|
+
auth: "plugin",
|
|
404
|
+
body: {
|
|
405
|
+
contributionId: params.contributionId,
|
|
406
|
+
status: params.status,
|
|
407
|
+
content: params.content,
|
|
408
|
+
errorMessage: params.errorMessage,
|
|
409
|
+
startedAt: params.startedAt,
|
|
410
|
+
completedAt: params.completedAt,
|
|
411
|
+
assignment: params.assignment,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async createPanelContribution(params) {
|
|
416
|
+
const result = await this.request({
|
|
417
|
+
path: "agent-panel-contribution-create",
|
|
418
|
+
method: "POST",
|
|
419
|
+
auth: "plugin",
|
|
420
|
+
body: {
|
|
421
|
+
turnId: params.turnId,
|
|
422
|
+
agentId: params.agentId,
|
|
423
|
+
round: params.round,
|
|
424
|
+
assignment: params.assignment,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
return { contribution: mapPanelContribution(result.contribution) };
|
|
428
|
+
}
|
|
429
|
+
async updatePanelTurn(params) {
|
|
430
|
+
await this.request({
|
|
431
|
+
path: "agent-panel-turn-update",
|
|
432
|
+
method: "POST",
|
|
433
|
+
auth: "plugin",
|
|
434
|
+
body: {
|
|
435
|
+
turnId: params.turnId,
|
|
436
|
+
status: params.status,
|
|
437
|
+
errorMessage: params.errorMessage,
|
|
438
|
+
startedAt: params.startedAt,
|
|
439
|
+
completedAt: params.completedAt,
|
|
440
|
+
plan: params.plan,
|
|
441
|
+
planStatus: params.planStatus,
|
|
442
|
+
planErrorMessage: params.planErrorMessage,
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Get signed download URL for a release package.
|
|
448
|
+
*
|
|
449
|
+
* This is used during install to obtain a time-limited signed URL for downloading
|
|
450
|
+
* the release package from Supabase Storage (D-12).
|
|
451
|
+
*
|
|
452
|
+
* The signed URL expires after 1 hour. If download fails with 403, the caller should
|
|
453
|
+
* retry by calling this method again to get a fresh signed URL.
|
|
454
|
+
*
|
|
455
|
+
* @param releaseId - Release UUID
|
|
456
|
+
* @param accessToken - User JWT access token
|
|
457
|
+
* @returns Signed URL, fingerprint, and expiry time
|
|
458
|
+
* @throws {ClawHiveApiError} If release is withdrawn (410), not found (404), or other errors
|
|
459
|
+
*/
|
|
460
|
+
async getSignedDownloadUrl(releaseId, accessToken) {
|
|
461
|
+
return this.request({
|
|
462
|
+
path: "market-release-download",
|
|
463
|
+
method: "GET",
|
|
464
|
+
auth: "user",
|
|
465
|
+
accessToken,
|
|
466
|
+
query: {
|
|
467
|
+
release_id: releaseId,
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Upload a release package to Supabase Storage.
|
|
473
|
+
*
|
|
474
|
+
* This is step 1 of the two-step publish flow (D-08):
|
|
475
|
+
* 1. Upload the tar.gz to Storage via this method → receive storage_path + fingerprint
|
|
476
|
+
* 2. Call market-releases-create with the storage_path + fingerprint + release metadata
|
|
477
|
+
*
|
|
478
|
+
* The upload is idempotent: uploading the same content to the same path is safe.
|
|
479
|
+
* Storage path format: releases/{listing_id}/{version}.tar.gz (D-09)
|
|
480
|
+
* Size limit: 10 MiB (D-10), enforced by the edge function
|
|
481
|
+
*
|
|
482
|
+
* Example usage:
|
|
483
|
+
* ```typescript
|
|
484
|
+
* import { packageAgentDirectory } from "./market-publish/packager.js";
|
|
485
|
+
*
|
|
486
|
+
* // Step 1: Package and upload
|
|
487
|
+
* const pkg = await packageAgentDirectory({ agentDir: "/path/to/agent" });
|
|
488
|
+
* const uploadResult = await client.uploadReleasePackage({
|
|
489
|
+
* listingId: "listing-uuid",
|
|
490
|
+
* version: "v1.0.0",
|
|
491
|
+
* buffer: pkg.buffer,
|
|
492
|
+
* fingerprint: pkg.fingerprint,
|
|
493
|
+
* accessToken: "user-jwt",
|
|
494
|
+
* });
|
|
495
|
+
*
|
|
496
|
+
* // Step 2: Create release record
|
|
497
|
+
* await client.createMarketRelease({
|
|
498
|
+
* listingId: uploadResult.listing_id,
|
|
499
|
+
* version: "v1.0.0",
|
|
500
|
+
* storagePath: uploadResult.storage_path,
|
|
501
|
+
* fingerprint: uploadResult.fingerprint,
|
|
502
|
+
* // ... other release metadata
|
|
503
|
+
* });
|
|
504
|
+
* ```
|
|
505
|
+
*
|
|
506
|
+
* @param params - Upload parameters
|
|
507
|
+
* @returns Upload result with storage_path and fingerprint
|
|
508
|
+
*/
|
|
509
|
+
async uploadReleasePackage(params) {
|
|
510
|
+
const url = new URL(`${this.baseUrl}/functions/v1/market-release-upload`);
|
|
511
|
+
const headers = {
|
|
512
|
+
"Content-Type": "application/json",
|
|
513
|
+
apikey: this.config.anonKey,
|
|
514
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
515
|
+
};
|
|
516
|
+
let response;
|
|
517
|
+
try {
|
|
518
|
+
response = await fetch(url.toString(), {
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers,
|
|
521
|
+
body: JSON.stringify({
|
|
522
|
+
listing_id: params.listingId,
|
|
523
|
+
version: params.version,
|
|
524
|
+
file_data: params.buffer.toString("base64"),
|
|
525
|
+
}),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
const reason = formatTransportError(error);
|
|
530
|
+
throw new Error(`ClawHive market-release-upload request failed (POST ${url.toString()}): ${reason}`);
|
|
531
|
+
}
|
|
532
|
+
const text = await response.text();
|
|
533
|
+
const payload = text ? JSON.parse(text) : {};
|
|
534
|
+
if (!response.ok) {
|
|
535
|
+
throw new ClawHiveApiError({
|
|
536
|
+
status: response.status,
|
|
537
|
+
error: typeof payload.error === "string"
|
|
538
|
+
? payload.error
|
|
539
|
+
: `ClawHive market-release-upload failed (${response.status})`,
|
|
540
|
+
details: payload.details,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
return payload;
|
|
544
|
+
}
|
|
545
|
+
async request(options) {
|
|
546
|
+
const url = new URL(`${this.baseUrl}/functions/v1/${options.path}`);
|
|
547
|
+
const method = options.method ?? "GET";
|
|
548
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
549
|
+
if (value !== undefined && value !== null) {
|
|
550
|
+
url.searchParams.set(key, String(value));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const headers = {
|
|
554
|
+
"Content-Type": "application/json",
|
|
555
|
+
Connection: "close",
|
|
556
|
+
apikey: this.config.anonKey,
|
|
557
|
+
};
|
|
558
|
+
if (options.auth === "plugin") {
|
|
559
|
+
headers.Authorization = `Bearer ${this.config.anonKey}`;
|
|
560
|
+
headers["x-plugin-token"] = this.requirePluginToken();
|
|
561
|
+
}
|
|
562
|
+
else if (options.auth === "user" && options.accessToken) {
|
|
563
|
+
headers.Authorization = `Bearer ${options.accessToken}`;
|
|
564
|
+
}
|
|
565
|
+
else if (options.auth === "none") {
|
|
566
|
+
headers.Authorization = `Bearer ${this.config.anonKey}`;
|
|
567
|
+
}
|
|
568
|
+
let response;
|
|
569
|
+
try {
|
|
570
|
+
response = await fetch(url.toString(), {
|
|
571
|
+
method,
|
|
572
|
+
headers,
|
|
573
|
+
body: options.body && method !== "GET"
|
|
574
|
+
? JSON.stringify(options.body)
|
|
575
|
+
: undefined,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
const reason = formatTransportError(error);
|
|
580
|
+
throw new Error(`ClawHive ${options.path} request failed (${method} ${url.toString()}): ${reason}`);
|
|
581
|
+
}
|
|
582
|
+
const text = await response.text();
|
|
583
|
+
const payload = text ? JSON.parse(text) : {};
|
|
584
|
+
if (!response.ok) {
|
|
585
|
+
throw new ClawHiveApiError({
|
|
586
|
+
status: response.status,
|
|
587
|
+
error: typeof payload.error === "string"
|
|
588
|
+
? payload.error
|
|
589
|
+
: `ClawHive ${options.path} failed (${response.status})`,
|
|
590
|
+
details: payload.details,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return payload;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ChannelSetupInput, type OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
+
export declare const CLAWHIVE_CHANNEL_ID = "clawhive";
|
|
3
|
+
export type ClawHiveResolvedAccount = {
|
|
4
|
+
accountId: string;
|
|
5
|
+
projectUrl: string;
|
|
6
|
+
anonKey: string;
|
|
7
|
+
pluginToken: string;
|
|
8
|
+
userId: string | null;
|
|
9
|
+
pluginNodeId: string | null;
|
|
10
|
+
nodeName: string;
|
|
11
|
+
pluginVersion: string;
|
|
12
|
+
publishSourceRef: string | null;
|
|
13
|
+
publishFingerprint: string | null;
|
|
14
|
+
minPluginVersion: string | null;
|
|
15
|
+
minOpenClawVersion: string | null;
|
|
16
|
+
dmPolicy: string | null;
|
|
17
|
+
allowFrom: Array<string | number>;
|
|
18
|
+
};
|
|
19
|
+
export declare function readRawChannelSection(cfg: OpenClawConfig): Record<string, unknown>;
|
|
20
|
+
export declare function resolveResolvedAccount(cfg: OpenClawConfig, accountId?: string | null): ClawHiveResolvedAccount;
|
|
21
|
+
export declare function writeChannelSection(cfg: OpenClawConfig, input: ChannelSetupInput): OpenClawConfig;
|
|
22
|
+
export declare function writePluginNodeId(cfg: OpenClawConfig, pluginNodeId: string): OpenClawConfig;
|
|
23
|
+
export declare function writeRegistrationIdentity(cfg: OpenClawConfig, patch: {
|
|
24
|
+
userId?: string;
|
|
25
|
+
pluginNodeId?: string;
|
|
26
|
+
pluginToken?: string;
|
|
27
|
+
anonKey?: string;
|
|
28
|
+
}): OpenClawConfig;
|