@gholl-studio/pier-connector 0.3.0 → 0.3.11
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 +12 -2
- package/src/index.ts +142 -0
- package/src/robot.ts +5 -3
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.11",
|
|
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
|
@@ -101,8 +101,10 @@ export async function handleInbound(
|
|
|
101
101
|
].join('\n');
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
logger.info(`[pier-connector:trace] Finalized inbound context for job ${jobId}. Target Agent: ${finalAgentId}, Session: ${dynamicSessionKey}`);
|
|
105
|
+
|
|
104
106
|
const ctxPayload = api.runtime.channel.reply.finalizeInboundContext({
|
|
105
|
-
|
|
107
|
+
AgentId: finalAgentId,
|
|
106
108
|
Body: inbound.body,
|
|
107
109
|
BodyForAgent: inbound.body,
|
|
108
110
|
RawBody: inbound.body,
|
|
@@ -131,6 +133,7 @@ export async function handleInbound(
|
|
|
131
133
|
const { dispatcher, markDispatchIdle } = api.runtime.channel.reply.createReplyDispatcherWithTyping({
|
|
132
134
|
deliver: async (payload: any) => {
|
|
133
135
|
const currentMeta = robot.activeNodeJobs.get(jobId);
|
|
136
|
+
logger.info(`[pier-connector:trace] Outbound delivery triggered for job ${jobId}. Text length: ${payload.text?.length || 0}`);
|
|
134
137
|
await pierChannel.outbound.sendText({
|
|
135
138
|
text: payload.text,
|
|
136
139
|
to: `pier:${jobId}`,
|
|
@@ -146,16 +149,23 @@ export async function handleInbound(
|
|
|
146
149
|
if (api.runtime.channel.session?.recordSessionMetaFromInbound) {
|
|
147
150
|
try {
|
|
148
151
|
const storePath = api.runtime.channel.session.resolveStorePath(dynamicSessionKey);
|
|
152
|
+
logger.info(`[pier-connector:trace] Recording session metadata at ${storePath}`);
|
|
149
153
|
await api.runtime.channel.session.recordSessionMetaFromInbound({
|
|
150
154
|
storePath, sessionKey: dynamicSessionKey, ctx: ctxPayload
|
|
151
155
|
});
|
|
152
|
-
} catch (err) {
|
|
156
|
+
} catch (err: any) {
|
|
157
|
+
logger.error(`[pier-connector] ✖ Failed to record session metadata: ${err.message}`);
|
|
158
|
+
}
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
try {
|
|
162
|
+
logger.info(`[pier-connector:trace] Dispatching reply to agent ${finalAgentId}...`);
|
|
156
163
|
await api.runtime.channel.reply.dispatchReplyFromConfig({
|
|
157
164
|
ctx: ctxPayload, cfg: api.config, dispatcher
|
|
158
165
|
});
|
|
166
|
+
logger.info(`[pier-connector:trace] dispatchReplyFromConfig completed for job ${jobId}`);
|
|
167
|
+
} catch (err: any) {
|
|
168
|
+
logger.error(`[pier-connector] ✖ Dispatch error for job ${jobId}: ${err.message}`);
|
|
159
169
|
} finally {
|
|
160
170
|
markDispatchIdle();
|
|
161
171
|
}
|
package/src/index.ts
CHANGED
|
@@ -130,6 +130,148 @@ const register = (api: PierPluginApi) => {
|
|
|
130
130
|
}
|
|
131
131
|
}, { optional: true });
|
|
132
132
|
|
|
133
|
+
api.registerTool(
|
|
134
|
+
{
|
|
135
|
+
name: 'pier_chat',
|
|
136
|
+
label: 'Pier Chat',
|
|
137
|
+
description: 'Send a message to the employer regarding a specific job.',
|
|
138
|
+
parameters: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
jobId: { type: 'string' },
|
|
142
|
+
text: { type: 'string' },
|
|
143
|
+
accountId: { type: 'string' }
|
|
144
|
+
},
|
|
145
|
+
required: ['jobId', 'text']
|
|
146
|
+
},
|
|
147
|
+
async execute(_id, params, ctx: any) {
|
|
148
|
+
const accountId = params.accountId || 'default';
|
|
149
|
+
const robot = instances.get(accountId) || instances.values().next().value;
|
|
150
|
+
if (!robot || robot.connectionStatus !== 'connected') {
|
|
151
|
+
return { content: [{ type: 'text', text: 'Error: Robot not connected' }], details: {} };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const subject = `chat.${params.jobId}`;
|
|
156
|
+
let metadata = robot.activeNodeJobs.get(params.jobId);
|
|
157
|
+
|
|
158
|
+
if (!metadata && ctx.to) {
|
|
159
|
+
const toId = ctx.to.replace(/^pier:/, '');
|
|
160
|
+
metadata = robot.activeNodeJobs.get(toId);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const jobId = metadata?.pierJobId || params.jobId;
|
|
164
|
+
|
|
165
|
+
if (!robot.js) {
|
|
166
|
+
return { content: [{ type: 'text', text: 'Error: JetStream not available' }], details: {} };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const payload = {
|
|
170
|
+
id: (globalThis.crypto as any).randomUUID ? (globalThis.crypto as any).randomUUID() : (Math.random().toString(36).substring(2)),
|
|
171
|
+
job_id: jobId,
|
|
172
|
+
sender_id: robot.config.nodeId,
|
|
173
|
+
sender_name: accountId,
|
|
174
|
+
sender_type: 'node',
|
|
175
|
+
content: params.text,
|
|
176
|
+
created_at: new Date().toISOString(),
|
|
177
|
+
auth_token: robot.config.secretKey
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
await robot.js.publish(subject, new TextEncoder().encode(JSON.stringify(payload)));
|
|
181
|
+
return { content: [{ type: 'text', text: 'Message sent' }], details: {} };
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], details: {} };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{ optional: true }
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const registerSystemActionTool = (name: string, label: string, description: string, action: string, extraParams: any, userRole = 'node') => {
|
|
191
|
+
api.registerTool({
|
|
192
|
+
name,
|
|
193
|
+
label,
|
|
194
|
+
description,
|
|
195
|
+
parameters: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
jobId: { type: 'string', description: 'The ID of the job' },
|
|
199
|
+
accountId: { type: 'string' },
|
|
200
|
+
...extraParams
|
|
201
|
+
},
|
|
202
|
+
required: ['jobId', ...Object.keys(extraParams)]
|
|
203
|
+
},
|
|
204
|
+
async execute(_id, params) {
|
|
205
|
+
const accountId = params.accountId || 'default';
|
|
206
|
+
const robot = instances.get(accountId) || instances.values().next().value;
|
|
207
|
+
if (!robot || robot.connectionStatus !== 'connected') {
|
|
208
|
+
return { content: [{ type: 'text', text: 'Error: Robot not connected' }], details: {} };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const subject = `chat.${params.jobId}`;
|
|
213
|
+
const { jobId: j, accountId: _, ...p } = params;
|
|
214
|
+
|
|
215
|
+
if (!robot.js) {
|
|
216
|
+
return { content: [{ type: 'text', text: 'Error: JetStream not available' }], details: {} };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const msgData = {
|
|
220
|
+
id: (globalThis.crypto as any).randomUUID ? (globalThis.crypto as any).randomUUID() : (Math.random().toString(36).substring(2)),
|
|
221
|
+
job_id: params.jobId,
|
|
222
|
+
sender_id: userRole === 'user' ? 'user_' + robot.config.nodeId : robot.config.nodeId,
|
|
223
|
+
sender_type: userRole,
|
|
224
|
+
content: JSON.stringify({ type: 'system_action', action, payload: p }),
|
|
225
|
+
created_at: new Date().toISOString(),
|
|
226
|
+
auth_token: robot.config.secretKey,
|
|
227
|
+
type: 'system_action',
|
|
228
|
+
action: action
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
await robot.js.publish(subject, new TextEncoder().encode(JSON.stringify(msgData)));
|
|
232
|
+
return { content: [{ type: 'text', text: `${action} executed successfully` }], details: {} };
|
|
233
|
+
} catch (err: any) {
|
|
234
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], details: {} };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, { optional: true });
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
registerSystemActionTool('pier_bid_task', 'Bid on task', 'Bid on an marketplace task', 'task_bid', { message: { type: 'string', description: 'Your pitch' } });
|
|
241
|
+
registerSystemActionTool('pier_accept_task', 'Accept task', 'Accept offered task', 'task_accept', {});
|
|
242
|
+
registerSystemActionTool('pier_finish_task', 'Finish task', 'Submit final result', 'task_submit', { result: { type: 'string', description: 'Final result' } });
|
|
243
|
+
registerSystemActionTool('pier_propose_task', 'Offer task', 'Offer task to a node', 'task_offer', {
|
|
244
|
+
price: { type: 'number' },
|
|
245
|
+
description: { type: 'string' }
|
|
246
|
+
}, 'user');
|
|
247
|
+
registerSystemActionTool('pier_rate_task', 'Rate task', 'Rate the node', 'task_rate', {
|
|
248
|
+
score: { type: 'number' },
|
|
249
|
+
comment: { type: 'string' }
|
|
250
|
+
}, 'user');
|
|
251
|
+
registerSystemActionTool('pier_reject_task', 'Reject task', 'Reject task', 'task_reject', { reason: { type: 'string' } });
|
|
252
|
+
registerSystemActionTool('pier_fail_task', 'Report error', 'Report that the task has failed', 'task_error', { error: { type: 'string' } });
|
|
253
|
+
registerSystemActionTool('pier_cancel_task', 'Cancel task', 'Cancel the task', 'task_cancel', { reason: { type: 'string' } }, 'user');
|
|
254
|
+
|
|
255
|
+
api.registerTool({
|
|
256
|
+
name: 'pier_get_profile',
|
|
257
|
+
label: 'Pier Profile',
|
|
258
|
+
description: 'Get current Pier profile and node stats.',
|
|
259
|
+
parameters: {
|
|
260
|
+
type: 'object',
|
|
261
|
+
properties: { accountId: { type: 'string' } }
|
|
262
|
+
},
|
|
263
|
+
async execute(_id, params) {
|
|
264
|
+
const robot = instances.get(params.accountId || 'default') || instances.values().next().value;
|
|
265
|
+
if (!robot) return { content: [{ type: 'text', text: 'Error: Robot not found' }], details: {} };
|
|
266
|
+
try {
|
|
267
|
+
const profile = await robot.client.getUserProfile(robot.config.secretKey);
|
|
268
|
+
return { content: [{ type: 'text', text: JSON.stringify(profile, null, 2) }], details: {} };
|
|
269
|
+
} catch (err: any) {
|
|
270
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], details: {} };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}, { optional: true });
|
|
274
|
+
|
|
133
275
|
// Register simple status command
|
|
134
276
|
api.registerCommand({
|
|
135
277
|
name: 'pier',
|
package/src/robot.ts
CHANGED
|
@@ -120,13 +120,15 @@ export class PierRobot {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
msg.ack();
|
|
123
|
-
|
|
123
|
+
const inbound = {
|
|
124
124
|
accountId: this.accountId,
|
|
125
125
|
senderId: `pier:${msgPayload.sender_id}`,
|
|
126
126
|
body: content,
|
|
127
127
|
jobId: jobId
|
|
128
|
-
}
|
|
129
|
-
|
|
128
|
+
};
|
|
129
|
+
this.logger.info(`[pier-connector:trace] NATS Chat Message received on PierRobot instance accountId='${this.accountId}'. Passing to handleInbound...`);
|
|
130
|
+
await this.onInbound(inbound, jobId);
|
|
131
|
+
} catch (err: any) { this.logger.error(`[pier-connector][${this.accountId}] ✖ Failed to process chat message: ${err.message}`); }
|
|
130
132
|
}
|
|
131
133
|
})();
|
|
132
134
|
} catch (err: any) {
|