@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gholl-studio/pier-connector",
3
3
  "author": "gholl",
4
- "version": "0.3.0",
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
- agentId: finalAgentId,
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
- await this.onInbound({
123
+ const inbound = {
124
124
  accountId: this.accountId,
125
125
  senderId: `pier:${msgPayload.sender_id}`,
126
126
  body: content,
127
127
  jobId: jobId
128
- }, jobId);
129
- } catch (err: any) { this.logger.error(`[pier-connector] Chat err: ${err.message}`); }
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) {