@gholl-studio/pier-connector 0.3.17 → 0.3.19
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/package.json +1 -1
- package/src/inbound.ts +34 -53
- package/src/index.ts +159 -81
- package/src/robot.ts +5 -5
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gholl-studio/pier-connector",
|
|
3
3
|
"author": "gholl",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.19",
|
|
5
5
|
"description": "OpenClaw plugin that connects to the Pier job marketplace. Automatically fetches, executes, and reports distributed tasks for rewards.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "src/index.ts",
|
package/src/inbound.ts
CHANGED
|
@@ -3,35 +3,25 @@
|
|
|
3
3
|
* @description Manages inbound message processing, agent routing resolution, and session context propagation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { InboundMessage } from './types.js';
|
|
7
7
|
import { truncate } from './job-handler.js';
|
|
8
8
|
|
|
9
9
|
export async function handleInbound(
|
|
10
|
-
|
|
10
|
+
runtime: any, // Using context-aware runtime from gateway
|
|
11
11
|
inbound: InboundMessage,
|
|
12
12
|
jobId: string,
|
|
13
|
-
robot: any,
|
|
14
|
-
|
|
13
|
+
robot: any,
|
|
14
|
+
pierPlugin: any
|
|
15
15
|
) {
|
|
16
|
-
const logger =
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const logger = runtime.log || console;
|
|
17
|
+
|
|
18
|
+
if (!runtime.reply) {
|
|
19
|
+
console.error(`[pier-connector][${robot.accountId}] SDK Error: runtime.reply is not available.`);
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
const agentIds = Array.isArray(agentsList) ? agentsList.map((a: any) => a.id) : Object.keys(agentsList);
|
|
25
|
-
logger.info(`[pier-connector][${robot.accountId}] Available agents in OpenClaw: ${JSON.stringify(agentIds)}`);
|
|
26
|
-
|
|
27
|
-
// 1. Resolve Account-Scoped Configuration (Flat Structure for SDK)
|
|
28
|
-
/**
|
|
29
|
-
* ★ Multi-Account Configuration Isolation:
|
|
30
|
-
* SDK helpers like resolveAgentRoute expect the channel config at the top level
|
|
31
|
-
* of cfg.channels.<channelName>. Replacing it with the robot's merged config
|
|
32
|
-
* ensures the SDK sees the correct agentId/dmPolicy for this specific account.
|
|
33
|
-
*/
|
|
34
|
-
const rootConfig = api.config || {};
|
|
23
|
+
// 1. Resolve Account-Scoped Configuration
|
|
24
|
+
const rootConfig = runtime.cfg || {};
|
|
35
25
|
const accountScopedCfg = {
|
|
36
26
|
...rootConfig,
|
|
37
27
|
channels: {
|
|
@@ -40,8 +30,8 @@ export async function handleInbound(
|
|
|
40
30
|
}
|
|
41
31
|
};
|
|
42
32
|
|
|
43
|
-
// 2. Resolve Agent Route
|
|
44
|
-
const route =
|
|
33
|
+
// 2. Resolve Agent Route
|
|
34
|
+
const route = runtime.routing.resolveAgentRoute({
|
|
45
35
|
cfg: accountScopedCfg,
|
|
46
36
|
channel: 'pier',
|
|
47
37
|
accountId: robot.accountId,
|
|
@@ -53,14 +43,6 @@ export async function handleInbound(
|
|
|
53
43
|
|
|
54
44
|
logger.info(`[pier-connector] Routing account '${robot.accountId}' -> agent '${finalAgentId}' (Source: ${routingSource})`);
|
|
55
45
|
|
|
56
|
-
// Debug: Check Agent Identity (Correct Signature: resolveAgentIdentity(cfg, agentId))
|
|
57
|
-
try {
|
|
58
|
-
const identity = (api.runtime as any).agent.resolveAgentIdentity(accountScopedCfg, finalAgentId);
|
|
59
|
-
logger.info(`[pier-connector:debug] Identity resolution for ${finalAgentId}: ${JSON.stringify(identity)}`);
|
|
60
|
-
} catch (err: any) {
|
|
61
|
-
logger.warn(`[pier-connector:debug] Identity resolution failed for ${finalAgentId}: ${err.message}`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
46
|
const dynamicSessionKey = `pier-job-${jobId}`;
|
|
65
47
|
const metadata = robot.activeNodeJobs.get(jobId);
|
|
66
48
|
let injectedPrompt = "";
|
|
@@ -89,19 +71,18 @@ export async function handleInbound(
|
|
|
89
71
|
].join('\n');
|
|
90
72
|
}
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const ctxPayload = api.runtime.channel.reply.finalizeInboundContext({
|
|
74
|
+
// 3. Finalize Context
|
|
75
|
+
const ctxPayload = runtime.reply.finalizeInboundContext({
|
|
95
76
|
Body: inbound.body,
|
|
96
77
|
BodyForAgent: inbound.body,
|
|
97
78
|
RawBody: inbound.body,
|
|
98
|
-
From:
|
|
79
|
+
From: inbound.senderId,
|
|
99
80
|
To: `chat:${jobId}`,
|
|
100
81
|
SessionKey: dynamicSessionKey,
|
|
101
82
|
AccountId: robot.accountId,
|
|
102
83
|
ChatType: 'direct',
|
|
103
84
|
SenderId: inbound.senderId,
|
|
104
|
-
SenderName: inbound.senderId,
|
|
85
|
+
SenderName: inbound.senderId,
|
|
105
86
|
Provider: 'pier',
|
|
106
87
|
Surface: 'pier',
|
|
107
88
|
OriginatingChannel: 'pier',
|
|
@@ -114,41 +95,41 @@ export async function handleInbound(
|
|
|
114
95
|
Metadata: {
|
|
115
96
|
...metadata,
|
|
116
97
|
accountId: robot.accountId,
|
|
98
|
+
agentId: finalAgentId,
|
|
117
99
|
pierJobId: jobId,
|
|
118
100
|
routingSource: routingSource
|
|
119
101
|
}
|
|
120
102
|
});
|
|
121
103
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const { dispatcher, markDispatchIdle } = api.runtime.channel.reply.createReplyDispatcherWithTyping({
|
|
104
|
+
// 4. Create Dispatcher
|
|
105
|
+
const { dispatcher, markDispatchIdle } = runtime.reply.createReplyDispatcherWithTyping({
|
|
125
106
|
deliver: async (payload: any, info: any) => {
|
|
126
107
|
const currentMeta = robot.activeNodeJobs.get(jobId);
|
|
127
|
-
const
|
|
108
|
+
const resAgent = payload.agentId || finalAgentId;
|
|
128
109
|
|
|
129
|
-
logger.info(`[pier-connector:trace] Outbound delivery
|
|
130
|
-
logger.info(`[pier-connector:debug] FULL PAYLOAD: ${JSON.stringify(payload)}`);
|
|
110
|
+
logger.info(`[pier-connector:trace] Outbound delivery for ${jobId}. Responder: ${resAgent}.`);
|
|
131
111
|
|
|
132
112
|
if (payload.text && payload.text.length > 0) {
|
|
133
|
-
await
|
|
113
|
+
await pierPlugin.outbound.sendText({
|
|
134
114
|
text: payload.text,
|
|
135
115
|
to: `pier:${jobId}`,
|
|
116
|
+
accountId: robot.accountId,
|
|
136
117
|
metadata: {
|
|
137
118
|
...currentMeta,
|
|
138
119
|
accountId: robot.accountId,
|
|
139
120
|
pierJobId: jobId,
|
|
140
|
-
respondingAgentId:
|
|
121
|
+
respondingAgentId: resAgent
|
|
141
122
|
},
|
|
142
123
|
});
|
|
143
124
|
}
|
|
144
125
|
}
|
|
145
126
|
});
|
|
146
127
|
|
|
147
|
-
|
|
128
|
+
// 5. Session Recording
|
|
129
|
+
if (runtime.session?.recordSessionMetaFromInbound) {
|
|
148
130
|
try {
|
|
149
|
-
const storePath =
|
|
150
|
-
|
|
151
|
-
await api.runtime.channel.session.recordSessionMetaFromInbound({
|
|
131
|
+
const storePath = runtime.session.resolveStorePath(dynamicSessionKey);
|
|
132
|
+
await runtime.session.recordSessionMetaFromInbound({
|
|
152
133
|
storePath, sessionKey: dynamicSessionKey, ctx: ctxPayload
|
|
153
134
|
});
|
|
154
135
|
} catch (err: any) {
|
|
@@ -156,22 +137,22 @@ export async function handleInbound(
|
|
|
156
137
|
}
|
|
157
138
|
}
|
|
158
139
|
|
|
140
|
+
// 6. Dispatch
|
|
159
141
|
try {
|
|
160
|
-
|
|
161
|
-
await api.runtime.channel.reply.dispatchReplyFromConfig({
|
|
142
|
+
await runtime.reply.dispatchReplyFromConfig({
|
|
162
143
|
ctx: ctxPayload,
|
|
163
144
|
cfg: accountScopedCfg,
|
|
164
145
|
dispatcher,
|
|
165
146
|
replyOptions: {
|
|
166
147
|
onModelSelected: (mCtx: any) => {
|
|
167
|
-
logger.info(`[pier-connector:debug] Model selected for ${jobId}: ${mCtx.provider}/${mCtx.model}
|
|
148
|
+
logger.info(`[pier-connector:debug] Model selected for ${jobId}: ${mCtx.provider}/${mCtx.model}`);
|
|
168
149
|
}
|
|
169
150
|
}
|
|
170
|
-
}
|
|
171
|
-
logger.info(`[pier-connector:trace] dispatchReplyFromConfig completed for job ${jobId}`);
|
|
151
|
+
});
|
|
172
152
|
} catch (err: any) {
|
|
173
153
|
logger.error(`[pier-connector] ✖ Dispatch error for job ${jobId}: ${err.message}`);
|
|
174
154
|
} finally {
|
|
175
|
-
markDispatchIdle();
|
|
155
|
+
markDispatchIdle?.();
|
|
176
156
|
}
|
|
177
157
|
}
|
|
158
|
+
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file index.ts
|
|
3
|
-
* @description Main entry point for the Pier Connector plugin.
|
|
3
|
+
* @description Main entry point for the Pier Connector plugin.
|
|
4
|
+
* Implements the standard OpenClaw ChannelPlugin interface for native multi-account support.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry';
|
|
8
|
+
import type { ChannelPlugin, OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk';
|
|
7
9
|
import { protocol } from '@gholl-studio/pier-sdk';
|
|
8
10
|
const { createRequestPayload } = protocol;
|
|
9
11
|
import { DEFAULTS } from './config.js';
|
|
@@ -12,99 +14,174 @@ import { handleInbound } from './inbound.js';
|
|
|
12
14
|
import { registerCli } from './cli.js';
|
|
13
15
|
import type { PierAccountConfig, PierPluginApi } from './types.js';
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const instances = new Map<string, PierRobot>();
|
|
18
|
-
const globalStats = { received: 0, completed: 0, failed: 0 };
|
|
19
|
-
|
|
20
|
-
function mergedCfgFrom(legacy: any, account: any): PierAccountConfig {
|
|
21
|
-
const merged = { ...legacy, ...account };
|
|
22
|
-
return {
|
|
23
|
-
accountId: account.accountId || 'default',
|
|
24
|
-
pierApiUrl: merged.pierApiUrl || DEFAULTS.PIER_API_URL,
|
|
25
|
-
nodeId: merged.nodeId || DEFAULTS.NODE_ID,
|
|
26
|
-
secretKey: merged.secretKey || DEFAULTS.SECRET_KEY,
|
|
27
|
-
privateKey: merged.privateKey || process.env.PIER_PRIVATE_KEY || DEFAULTS.PRIVATE_KEY,
|
|
28
|
-
natsUrl: merged.natsUrl || DEFAULTS.NATS_URL,
|
|
29
|
-
subject: merged.subject || DEFAULTS.SUBJECT,
|
|
30
|
-
publishSubject: merged.publishSubject || DEFAULTS.PUBLISH_SUBJECT,
|
|
31
|
-
queueGroup: merged.queueGroup || DEFAULTS.QUEUE_GROUP,
|
|
32
|
-
agentId: merged.agentId || DEFAULTS.AGENT_ID,
|
|
33
|
-
walletAddress: merged.walletAddress || DEFAULTS.WALLET_ADDRESS,
|
|
34
|
-
capabilities: merged.capabilities || ['translation', 'code-execution', 'reasoning', 'vision'],
|
|
35
|
-
};
|
|
36
|
-
}
|
|
17
|
+
// Global instances map to track active robots by account ID
|
|
18
|
+
const instances = new Map<string, PierRobot>();
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Merges global/legacy config with account-specific overrides.
|
|
22
|
+
*/
|
|
23
|
+
function mergedCfgFrom(legacy: any, account: any): PierAccountConfig {
|
|
24
|
+
const merged = { ...legacy, ...account };
|
|
25
|
+
return {
|
|
26
|
+
accountId: account.accountId || 'default',
|
|
27
|
+
pierApiUrl: merged.pierApiUrl || DEFAULTS.PIER_API_URL,
|
|
28
|
+
nodeId: merged.nodeId || DEFAULTS.NODE_ID,
|
|
29
|
+
secretKey: merged.secretKey || DEFAULTS.SECRET_KEY,
|
|
30
|
+
privateKey: merged.privateKey || process.env.PIER_PRIVATE_KEY || DEFAULTS.PRIVATE_KEY,
|
|
31
|
+
natsUrl: merged.natsUrl || DEFAULTS.NATS_URL,
|
|
32
|
+
subject: merged.subject || DEFAULTS.SUBJECT,
|
|
33
|
+
publishSubject: merged.publishSubject || DEFAULTS.PUBLISH_SUBJECT,
|
|
34
|
+
queueGroup: merged.queueGroup || DEFAULTS.QUEUE_GROUP,
|
|
35
|
+
agentId: merged.agentId || DEFAULTS.AGENT_ID,
|
|
36
|
+
walletAddress: merged.walletAddress || DEFAULTS.WALLET_ADDRESS,
|
|
37
|
+
capabilities: merged.capabilities || ['translation', 'code-execution', 'reasoning', 'vision'],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Resolves all configured accounts for the Pier channel.
|
|
43
|
+
*/
|
|
44
|
+
function getAccountConfigs(cfg: OpenClawConfig, pluginConfig: any): PierAccountConfig[] {
|
|
45
|
+
const globalAccounts = (cfg as any)?.channels?.['pier']?.accounts || {};
|
|
46
|
+
const pluginAccounts = pluginConfig?.accounts || {};
|
|
47
|
+
const rawAccounts = { ...globalAccounts, ...pluginAccounts };
|
|
48
|
+
const legacyCfg = pluginConfig || {};
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
50
|
+
if (Object.keys(rawAccounts).length === 0) {
|
|
51
|
+
return [mergedCfgFrom(legacyCfg, { accountId: 'default' })];
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
return Object.entries(rawAccounts).map(([id, account]: [string, any]) =>
|
|
55
|
+
mergedCfgFrom(legacyCfg, { ...account, accountId: id })
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const pierPlugin: ChannelPlugin<PierAccountConfig> = {
|
|
60
|
+
id: 'pier',
|
|
61
|
+
|
|
62
|
+
meta: {
|
|
54
63
|
id: 'pier',
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
label: 'Pier',
|
|
65
|
+
selectionLabel: 'Pier (NATS Job Marketplace)',
|
|
66
|
+
docsPath: '/channels/pier',
|
|
67
|
+
blurb: 'Connect to the Pier distributed job marketplace.',
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
capabilities: {
|
|
71
|
+
chatTypes: ['direct'],
|
|
72
|
+
media: false,
|
|
73
|
+
reactions: false,
|
|
74
|
+
threads: false,
|
|
75
|
+
nativeCommands: true,
|
|
76
|
+
blockStreaming: true,
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
// Config Adapter
|
|
81
|
+
// -------------------------------------------------------------------------
|
|
82
|
+
config: {
|
|
83
|
+
listAccountIds: (cfg) => {
|
|
84
|
+
const accounts = (cfg as any)?.channels?.['pier']?.accounts || {};
|
|
85
|
+
const ids = Object.keys(accounts);
|
|
86
|
+
return ids.length > 0 ? ids : ['default'];
|
|
60
87
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const payload = {
|
|
70
|
-
id: crypto.randomUUID ? crypto.randomUUID() : (Math.random().toString(36).substring(2)),
|
|
71
|
-
job_id: jobId,
|
|
72
|
-
sender_id: robot.config.nodeId,
|
|
73
|
-
sender_name: robot.accountId,
|
|
74
|
-
sender_type: 'node',
|
|
75
|
-
content: params.text,
|
|
76
|
-
created_at: new Date().toISOString(),
|
|
77
|
-
auth_token: robot.config.secretKey
|
|
78
|
-
};
|
|
88
|
+
resolveAccount: (cfg, accountId) => {
|
|
89
|
+
const accounts = (cfg as any)?.channels?.['pier']?.accounts || {};
|
|
90
|
+
const account = accounts[accountId || 'default'] || {};
|
|
91
|
+
return mergedCfgFrom((cfg as any)?.channels?.['pier'] || {}, { ...account, accountId: accountId || 'default' });
|
|
92
|
+
},
|
|
93
|
+
defaultAccountId: () => 'default'
|
|
94
|
+
},
|
|
79
95
|
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
// -------------------------------------------------------------------------
|
|
97
|
+
// Pairing Adapter
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
pairing: {
|
|
100
|
+
idLabel: 'pierJobId',
|
|
101
|
+
normalizeAllowEntry: (entry) => entry.replace(/^(pier|job):/i, ''),
|
|
102
|
+
notifyApproval: async ({ cfg, id, accountId }) => {
|
|
103
|
+
// Signal received when 'openclaw pairing approve pier <id>' is run.
|
|
104
|
+
// Useful if we want to trigger specific onboarding or status updates.
|
|
105
|
+
console.log(`[pier-connector] Pairing approved for Job ${id} on account ${accountId}`);
|
|
82
106
|
}
|
|
83
|
-
}
|
|
107
|
+
},
|
|
84
108
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
globalStats.received++;
|
|
95
|
-
});
|
|
96
|
-
instances.set(config.accountId, robot);
|
|
97
|
-
await robot.start();
|
|
109
|
+
// -------------------------------------------------------------------------
|
|
110
|
+
// Outbound Adapter
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
outbound: {
|
|
113
|
+
deliveryMode: 'direct',
|
|
114
|
+
sendText: async (ctx) => {
|
|
115
|
+
const robot = instances.get(ctx.accountId || 'default') || instances.values().next().value;
|
|
116
|
+
if (!robot || !robot.js) {
|
|
117
|
+
throw new Error('Robot not connected');
|
|
98
118
|
}
|
|
119
|
+
|
|
120
|
+
const jobId = ctx.to.replace(/^pier:/, '');
|
|
121
|
+
const subject = `chat.${jobId}`;
|
|
122
|
+
|
|
123
|
+
const payload = {
|
|
124
|
+
id: (globalThis.crypto as any).randomUUID ? (globalThis.crypto as any).randomUUID() : (Math.random().toString(36).substring(2)),
|
|
125
|
+
job_id: jobId,
|
|
126
|
+
sender_id: robot.config.nodeId,
|
|
127
|
+
sender_name: robot.accountId,
|
|
128
|
+
sender_type: 'node',
|
|
129
|
+
content: ctx.text,
|
|
130
|
+
created_at: new Date().toISOString(),
|
|
131
|
+
auth_token: robot.config.secretKey
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
await robot.js.publish(subject, new TextEncoder().encode(JSON.stringify(payload)));
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
channel: 'pier',
|
|
138
|
+
messageId: payload.id,
|
|
139
|
+
conversationId: jobId
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// Gateway Adapter
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
gateway: {
|
|
148
|
+
startAccount: async (ctx) => {
|
|
149
|
+
const config = ctx.account;
|
|
150
|
+
const robot = new PierRobot(config, (ctx.runtime as any), async (inbound, jobId) => {
|
|
151
|
+
// Pass the context-aware runtime and the plugin instance
|
|
152
|
+
await handleInbound((ctx.runtime as any), inbound, jobId, robot, pierPlugin);
|
|
153
|
+
});
|
|
154
|
+
instances.set(ctx.accountId, robot);
|
|
155
|
+
await robot.start();
|
|
156
|
+
ctx.setStatus({
|
|
157
|
+
...ctx.getStatus(),
|
|
158
|
+
running: true,
|
|
159
|
+
lastStartAt: new Date().toISOString()
|
|
160
|
+
} as any);
|
|
99
161
|
},
|
|
100
|
-
|
|
101
|
-
|
|
162
|
+
stopAccount: async (ctx) => {
|
|
163
|
+
const robot = instances.get(ctx.accountId);
|
|
164
|
+
if (robot) {
|
|
102
165
|
await robot.stop();
|
|
166
|
+
instances.delete(ctx.accountId);
|
|
103
167
|
}
|
|
104
|
-
|
|
168
|
+
ctx.setStatus({
|
|
169
|
+
...ctx.getStatus(),
|
|
170
|
+
running: false,
|
|
171
|
+
lastStopAt: new Date().toISOString()
|
|
172
|
+
} as any);
|
|
105
173
|
}
|
|
106
|
-
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const register = (api: PierPluginApi) => {
|
|
178
|
+
const logger = api.logger;
|
|
179
|
+
const globalStats = { received: 0, completed: 0, failed: 0 };
|
|
180
|
+
|
|
181
|
+
// Register our new ChannelPlugin
|
|
182
|
+
api.registerChannel({ plugin: pierPlugin as any });
|
|
107
183
|
|
|
184
|
+
// Tools and CLI registration
|
|
108
185
|
api.registerTool({
|
|
109
186
|
name: 'pier_publish',
|
|
110
187
|
label: 'Publish to Pier',
|
|
@@ -193,6 +270,7 @@ const register = (api: PierPluginApi) => {
|
|
|
193
270
|
{ optional: true }
|
|
194
271
|
);
|
|
195
272
|
|
|
273
|
+
// Helper to register marketplace action tools
|
|
196
274
|
const registerSystemActionTool = (name: string, label: string, description: string, action: string, extraParams: any, userRole = 'node') => {
|
|
197
275
|
api.registerTool({
|
|
198
276
|
name,
|
|
@@ -215,7 +293,6 @@ const register = (api: PierPluginApi) => {
|
|
|
215
293
|
}
|
|
216
294
|
|
|
217
295
|
try {
|
|
218
|
-
const subject = `chat.${params.jobId}`;
|
|
219
296
|
const { jobId: j, accountId: _, ...p } = params;
|
|
220
297
|
|
|
221
298
|
if (!robot.js) {
|
|
@@ -234,7 +311,7 @@ const register = (api: PierPluginApi) => {
|
|
|
234
311
|
action: action
|
|
235
312
|
};
|
|
236
313
|
|
|
237
|
-
await robot.js.publish(
|
|
314
|
+
await robot.js.publish(`chat.${params.jobId}`, new TextEncoder().encode(JSON.stringify(msgData)));
|
|
238
315
|
return { content: [{ type: 'text', text: `${action} executed successfully` }], details: {} };
|
|
239
316
|
} catch (err: any) {
|
|
240
317
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], details: {} };
|
|
@@ -279,7 +356,7 @@ const register = (api: PierPluginApi) => {
|
|
|
279
356
|
}
|
|
280
357
|
}, { optional: true });
|
|
281
358
|
|
|
282
|
-
//
|
|
359
|
+
// Status Command
|
|
283
360
|
api.registerCommand({
|
|
284
361
|
name: 'pier',
|
|
285
362
|
description: 'Show Pier status',
|
|
@@ -302,3 +379,4 @@ export default definePluginEntry({
|
|
|
302
379
|
description: 'Connects OpenClaw to the Pier job marketplace.',
|
|
303
380
|
register
|
|
304
381
|
});
|
|
382
|
+
|
package/src/robot.ts
CHANGED
|
@@ -25,20 +25,20 @@ export class PierRobot {
|
|
|
25
25
|
public connectionStatus: 'disconnected' | 'connecting' | 'connected' | 'error' = 'disconnected';
|
|
26
26
|
public stats = { received: 0, completed: 0, failed: 0 };
|
|
27
27
|
|
|
28
|
-
private
|
|
28
|
+
private runtime: any;
|
|
29
29
|
private logger: any;
|
|
30
30
|
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
31
31
|
private onInbound: (inbound: InboundMessage, jobId: string) => Promise<void>;
|
|
32
32
|
|
|
33
33
|
constructor(
|
|
34
34
|
config: PierAccountConfig,
|
|
35
|
-
|
|
35
|
+
runtime: any,
|
|
36
36
|
onInbound: (inbound: InboundMessage, jobId: string) => Promise<void>
|
|
37
37
|
) {
|
|
38
38
|
this.config = config;
|
|
39
39
|
this.accountId = config.accountId;
|
|
40
|
-
this.
|
|
41
|
-
this.logger =
|
|
40
|
+
this.runtime = runtime;
|
|
41
|
+
this.logger = runtime.log || console;
|
|
42
42
|
this.onInbound = onInbound;
|
|
43
43
|
this.client = new PierClient({
|
|
44
44
|
apiUrl: config.pierApiUrl,
|
|
@@ -264,7 +264,7 @@ export class PierRobot {
|
|
|
264
264
|
|
|
265
265
|
async autoRegister() {
|
|
266
266
|
if (!this.config.privateKey) return;
|
|
267
|
-
const hostName = `${
|
|
267
|
+
const hostName = `${this.runtime?.hostname ?? 'Auto'}-${this.accountId}`;
|
|
268
268
|
const { nodeId, secretKey } = await this.client.autoRegister(this.config.privateKey, hostName);
|
|
269
269
|
this.config.nodeId = nodeId;
|
|
270
270
|
this.config.secretKey = secretKey;
|