@samp3ng/aster-plugin-opc-cloud 0.1.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/dist/index.js ADDED
@@ -0,0 +1,1223 @@
1
+ // src/client.ts
2
+ var OpcPlatformClient = class {
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ config;
7
+ async health() {
8
+ return this.request("/health");
9
+ }
10
+ async stats() {
11
+ return this.request("/stats");
12
+ }
13
+ async registerAccount(body) {
14
+ return this.request("/auth/register", { method: "POST", body });
15
+ }
16
+ async adminOverview() {
17
+ return this.request("/admin/overview");
18
+ }
19
+ async listAdminUsers() {
20
+ return this.request("/admin/users");
21
+ }
22
+ async listPolicies(query = "", category = "") {
23
+ return this.request(`/policies?${toQuery({ q: query, category })}`);
24
+ }
25
+ async createSupplier(body) {
26
+ return this.request("/suppliers", { method: "POST", body });
27
+ }
28
+ async searchSuppliers(query, skills) {
29
+ return this.request(`/suppliers?${toQuery({ q: query, skills: skills.join(",") })}`);
30
+ }
31
+ async getSupplier(id) {
32
+ return this.request(`/suppliers/${encodeURIComponent(String(id))}`);
33
+ }
34
+ async updateSupplier(id, body) {
35
+ return this.request(`/suppliers/${encodeURIComponent(String(id))}`, { method: "PUT", body });
36
+ }
37
+ async createDemand(body) {
38
+ return this.request("/demands", { method: "POST", body });
39
+ }
40
+ async searchDemands(query, skills, opts = {}) {
41
+ return this.request(`/demands?${toQuery({ q: query, skills: skills.join(","), status: opts.status || "", category: opts.category || "" })}`);
42
+ }
43
+ async getDemand(id) {
44
+ return this.request(`/demands/${encodeURIComponent(String(id))}`);
45
+ }
46
+ async updateDemandStatus(id, status) {
47
+ return this.request(`/demands/${encodeURIComponent(String(id))}/status`, {
48
+ method: "PUT",
49
+ body: { status }
50
+ });
51
+ }
52
+ async startDemandChat(demandId, body) {
53
+ return this.request(`/demands/${encodeURIComponent(String(demandId))}/chat`, {
54
+ method: "POST",
55
+ body
56
+ });
57
+ }
58
+ async applyDemand(demandId, body) {
59
+ return this.request(`/demands/${encodeURIComponent(String(demandId))}/applications`, {
60
+ method: "POST",
61
+ body
62
+ });
63
+ }
64
+ async listApplications(demandId) {
65
+ return this.request(`/demands/${encodeURIComponent(String(demandId))}/applications`);
66
+ }
67
+ async reviewApplication(demandId, applicationId, action) {
68
+ return this.request(`/demands/${encodeURIComponent(String(demandId))}/applications/${encodeURIComponent(String(applicationId))}`, {
69
+ method: "PUT",
70
+ body: { action }
71
+ });
72
+ }
73
+ async listConversations(userId = "") {
74
+ return this.request(`/conversations?${toQuery({ userId })}`);
75
+ }
76
+ async sendMessage(conversationId, body) {
77
+ return this.request(`/conversations/${encodeURIComponent(String(conversationId))}/messages`, {
78
+ method: "POST",
79
+ body
80
+ });
81
+ }
82
+ async listMessages(conversationId) {
83
+ return this.request(`/conversations/${encodeURIComponent(String(conversationId))}/messages`);
84
+ }
85
+ async listOrders() {
86
+ return this.request("/orders");
87
+ }
88
+ async updateOrderStatus(id, status) {
89
+ return this.request(`/orders/${encodeURIComponent(String(id))}/status`, {
90
+ method: "PUT",
91
+ body: { status }
92
+ });
93
+ }
94
+ async request(path2, opts = {}) {
95
+ const base = this.config.baseUrl.replace(/\/+$/, "");
96
+ const res = await fetch(`${base}${path2}`, {
97
+ method: opts.method || "GET",
98
+ headers: opts.body === void 0 ? void 0 : { "content-type": "application/json" },
99
+ body: opts.body === void 0 ? void 0 : JSON.stringify(opts.body)
100
+ });
101
+ const text = await res.text();
102
+ let data = text;
103
+ try {
104
+ data = text ? JSON.parse(text) : null;
105
+ } catch {
106
+ }
107
+ if (!res.ok) {
108
+ const message = typeof data === "object" && data && "message" in data ? String(data.message) : text;
109
+ throw new Error(`OPC platform API ${res.status}: ${message}`);
110
+ }
111
+ return data;
112
+ }
113
+ };
114
+ function toQuery(params) {
115
+ const q = new URLSearchParams();
116
+ for (const [key, value] of Object.entries(params)) {
117
+ if (value) q.set(key, value);
118
+ }
119
+ return q.toString();
120
+ }
121
+
122
+ // src/prompts.ts
123
+ var OPC_CLOUD_SYSTEM_PROMPT = [
124
+ "[\u661F\u73AF OPC \u4F9B\u9700\u805A\u5408\u63D2\u4EF6]",
125
+ "\u4F60\u53EF\u4EE5\u901A\u8FC7 opc_platform_* \u5DE5\u5177\u628A\u672C\u5730 Aster \u63A5\u5165\u661F\u73AF OPC \u7EBF\u4E0A\u4F9B\u9700\u5E73\u53F0\u3002\u5E73\u53F0\u5B9A\u4F4D\u662F OPC \u4EEC\u53D1\u5355\u3001\u627E\u5355\u3001\u63A5\u5355\u3001\u79C1\u804A\u548C\u540C\u6B65\u8BA2\u5355,\u4E0D\u662F\u666E\u901A\u540E\u53F0\u7BA1\u7406\u3002",
126
+ "",
127
+ "\u4F18\u5148\u4F7F\u7528\u9AD8\u5C42\u5DE5\u4F5C\u6D41\u5DE5\u5177:",
128
+ "- \u7528\u6237\u8981\u5165\u9A7B/\u4E0A\u67B6/\u628A\u516C\u53F8\u653E\u5230\u5E73\u53F0:\u4F18\u5148\u8C03\u7528 opc_platform_onboard\u3002\u5B83\u4F1A\u8BFB\u53D6\u672C\u5730 OPC \u516C\u53F8\u8D44\u6599,\u751F\u6210\u6CE8\u518C\u8D26\u6237 + \u4E0A\u67B6\u670D\u52A1\u5546\u4E3B\u9875\u8BA1\u5212\u3002",
129
+ "- \u7528\u6237\u8981\u627E\u9002\u5408\u81EA\u5DF1\u63A5\u7684\u5355:\u4F18\u5148\u8C03\u7528 opc_platform_find_work\u3002\u5B83\u4F1A\u8BFB\u53D6\u672C\u5730 OPC \u6280\u80FD\u5E76\u63A8\u8350\u9700\u6C42\u3002",
130
+ "- \u7528\u6237\u8BF4\u201C\u8FD9\u4E2A\u5355\u6211\u63A5\u4E86/\u5E2E\u6211\u6295\u9012\u201D:\u4F18\u5148\u8C03\u7528 opc_platform_apply_to_work\u3002\u5B83\u4F1A\u5339\u914D\u672C\u5730\u670D\u52A1\u5546\u4E3B\u9875\u5E76\u751F\u6210\u7533\u8BF7\u6587\u6848\u3002",
131
+ "",
132
+ "\u5E95\u5C42\u5DE5\u5177\u7528\u4E8E\u7CBE\u786E\u64CD\u4F5C:",
133
+ "- \u627E\u670D\u52A1\u5546:opc_platform_search_supplier / opc_platform_get_supplier\u3002",
134
+ "- \u53D1\u9700\u6C42:opc_platform_post_demand,\u5148\u8865\u9F50\u6807\u9898\u3001\u8BE6\u60C5\u3001\u6280\u80FD\u3001\u9884\u7B97\u3001\u622A\u6B62\u65E5\u671F\u3002",
135
+ "- \u67E5\u9700\u6C42:opc_platform_search_demand / opc_platform_get_demand / opc_platform_list_applications\u3002",
136
+ "- \u6C9F\u901A:opc_platform_start_demand_chat / opc_platform_list_conversations / opc_platform_list_messages / opc_platform_send_chat_message\u3002",
137
+ "- \u8BA2\u5355:opc_platform_list_orders / opc_platform_update_order_status / opc_platform_pull_orders\u3002",
138
+ "- \u653F\u7B56\u8D44\u6E90:opc_platform_list_policies\u3002",
139
+ "",
140
+ "\u9AD8\u5F71\u54CD\u52A8\u4F5C\u5FC5\u987B dry-run-then-confirm:\u6CE8\u518C/\u4E0A\u67B6\u3001\u53D1\u5E03\u9700\u6C42\u3001\u63A5\u5355\u7533\u8BF7\u3001\u5BA1\u6838\u7533\u8BF7\u3001\u66F4\u65B0\u8BA2\u5355\u3001\u672C\u5730\u56DE\u6D41\u90FD\u4E0D\u80FD\u76F4\u63A5\u771F\u53D1\u3002\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u518D\u4F20 confirm:true\u3002",
141
+ "\u56DE\u7B54\u65F6\u628A\u7ED3\u679C\u7FFB\u8BD1\u6210\u7528\u6237\u80FD\u51B3\u7B56\u7684\u4FE1\u606F:\u63A8\u8350\u539F\u56E0\u3001\u9884\u7B97\u3001\u5339\u914D\u6280\u80FD\u3001\u4E0B\u4E00\u6B65\u52A8\u4F5C,\u4E0D\u8981\u53EA\u8D34 JSON\u3002"
142
+ ].join("\n");
143
+
144
+ // src/local-opc.ts
145
+ import fs from "fs";
146
+ import os from "os";
147
+ import path from "path";
148
+ import Database from "better-sqlite3";
149
+ function readLocalOpcProfile(homeDir, configuredDbPath) {
150
+ const dbPath = expandHome(configuredDbPath || path.join(homeDir, "plugins", "opc", "opc.db"));
151
+ if (!fs.existsSync(dbPath)) return {};
152
+ const db = new Database(dbPath, { readonly: true, fileMustExist: true });
153
+ try {
154
+ const company = db.prepare(`
155
+ SELECT name, business_type, notes
156
+ FROM opc_companies
157
+ ORDER BY id DESC
158
+ LIMIT 1
159
+ `).get();
160
+ const projects = db.prepare(`
161
+ SELECT name, description, status
162
+ FROM opc_projects
163
+ ORDER BY id DESC
164
+ LIMIT 5
165
+ `).all();
166
+ const contracts = db.prepare(`
167
+ SELECT title, party
168
+ FROM opc_contracts
169
+ ORDER BY id DESC
170
+ LIMIT 5
171
+ `).all();
172
+ if (!company?.name) return {};
173
+ const skills = inferSkills([company.business_type, company.notes, ...projects.map((p) => `${p.name} ${p.description}`)]);
174
+ const services = [company.business_type || "OPC \u670D\u52A1"].filter(Boolean);
175
+ return {
176
+ companyName: company.name,
177
+ headline: `${company.name} \xB7 ${company.business_type || "\u4E00\u4EBA\u516C\u53F8\u670D\u52A1\u5546"}`,
178
+ summary: company.notes || `\u4E3B\u8425 ${company.business_type || "\u9879\u76EE\u4EA4\u4ED8\u3001\u4E1A\u52A1\u534F\u4F5C\u548C\u54A8\u8BE2\u670D\u52A1"}\u3002`,
179
+ skills,
180
+ services,
181
+ portfolio: [
182
+ ...projects.map((item) => ({ title: item.name || "\u9879\u76EE", description: item.description || item.status || "" })),
183
+ ...contracts.map((item) => ({ title: item.title || "\u5408\u540C", description: item.party || "" }))
184
+ ].filter((item) => item.title)
185
+ };
186
+ } finally {
187
+ db.close();
188
+ }
189
+ }
190
+ function syncOrdersToLocalOpc(homeDir, orders, configuredDbPath) {
191
+ const dbPath = expandHome(configuredDbPath || path.join(homeDir, "plugins", "opc", "opc.db"));
192
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
193
+ const db = new Database(dbPath);
194
+ try {
195
+ ensureMinimalOpcSchema(db);
196
+ let created = 0;
197
+ let skipped = 0;
198
+ const now = Date.now();
199
+ const findCompany = db.prepare("SELECT id FROM opc_companies ORDER BY id DESC LIMIT 1");
200
+ const insertCompany = db.prepare(`
201
+ INSERT INTO opc_companies (name, address, business_type, founded_on, notes, created_at)
202
+ VALUES (?, '', 'OPC \u5728\u7EBF\u5E73\u53F0', '', '\u7531 opc_platform_pull_orders \u81EA\u52A8\u521B\u5EFA', ?)
203
+ `);
204
+ let company = findCompany.get();
205
+ if (!company) {
206
+ const result = insertCompany.run("OPC \u5728\u7EBF\u5E73\u53F0\u8BA2\u5355", now);
207
+ company = { id: Number(result.lastInsertRowid) };
208
+ }
209
+ const existsContract = db.prepare("SELECT id FROM opc_contracts WHERE title = ? LIMIT 1");
210
+ const insertContract = db.prepare(`
211
+ INSERT INTO opc_contracts (company_id, title, party, amount, start_on, end_on, status, content, created_at)
212
+ VALUES (?, ?, ?, ?, ?, ?, 'active', ?, ?)
213
+ `);
214
+ const insertProject = db.prepare(`
215
+ INSERT INTO opc_projects (company_id, name, description, status, created_at)
216
+ VALUES (?, ?, ?, 'active', ?)
217
+ `);
218
+ for (const order of orders) {
219
+ const title = `[OPC#${order.id}] ${order.demand?.title || "\u5E73\u53F0\u8BA2\u5355"}`;
220
+ if (existsContract.get(title)) {
221
+ skipped += 1;
222
+ continue;
223
+ }
224
+ const amount = order.demand?.budgetMax ?? order.demand?.budgetMin ?? null;
225
+ const content = [
226
+ `\u5E73\u53F0\u8BA2\u5355 ID: ${order.id}`,
227
+ `\u72B6\u6001: ${order.status || "pending"}`,
228
+ `\u9700\u6C42: ${order.demand?.title || ""}`,
229
+ `\u63CF\u8FF0: ${order.demand?.description || ""}`,
230
+ `\u4F9B\u5E94\u65B9: ${order.supplier?.companyName || ""}`
231
+ ].filter(Boolean).join("\n");
232
+ insertContract.run(
233
+ company.id,
234
+ title,
235
+ order.demand?.ownerId || "OPC \u5E73\u53F0\u9700\u6C42\u65B9",
236
+ amount,
237
+ (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
238
+ order.demand?.deadline || "",
239
+ content,
240
+ now
241
+ );
242
+ insertProject.run(company.id, title, order.demand?.description || content, now);
243
+ created += 1;
244
+ }
245
+ return { created, skipped, dbPath };
246
+ } finally {
247
+ db.close();
248
+ }
249
+ }
250
+ function expandHome(value) {
251
+ if (value === "~") return os.homedir();
252
+ if (value.startsWith("~/") || value.startsWith("~\\")) return path.join(os.homedir(), value.slice(2));
253
+ return value;
254
+ }
255
+ function inferSkills(values) {
256
+ const text = values.filter(Boolean).join(" ").toLowerCase();
257
+ const candidates = ["vue", "react", "node", "python", "java", "ai", "llm", "crm", "\u8D22\u52A1", "\u6CD5\u52A1", "\u540E\u7AEF", "\u524D\u7AEF"];
258
+ const matched = candidates.filter((item) => text.includes(item.toLowerCase()));
259
+ return matched.length ? matched : ["\u9879\u76EE\u4EA4\u4ED8", "OPC"];
260
+ }
261
+ function ensureMinimalOpcSchema(db) {
262
+ db.exec(`
263
+ CREATE TABLE IF NOT EXISTS opc_companies (
264
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
265
+ name TEXT NOT NULL UNIQUE,
266
+ address TEXT,
267
+ business_type TEXT,
268
+ founded_on TEXT,
269
+ notes TEXT,
270
+ created_at INTEGER NOT NULL
271
+ );
272
+
273
+ CREATE TABLE IF NOT EXISTS opc_contracts (
274
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
275
+ company_id INTEGER,
276
+ title TEXT NOT NULL,
277
+ party TEXT,
278
+ amount REAL,
279
+ start_on TEXT,
280
+ end_on TEXT,
281
+ status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','expired','terminated')),
282
+ content TEXT,
283
+ created_at INTEGER NOT NULL,
284
+ FOREIGN KEY (company_id) REFERENCES opc_companies(id) ON DELETE SET NULL
285
+ );
286
+
287
+ CREATE TABLE IF NOT EXISTS opc_projects (
288
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
289
+ company_id INTEGER,
290
+ name TEXT NOT NULL,
291
+ description TEXT,
292
+ status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','paused','done','archived')),
293
+ created_at INTEGER NOT NULL,
294
+ FOREIGN KEY (company_id) REFERENCES opc_companies(id) ON DELETE SET NULL
295
+ );
296
+ `);
297
+ }
298
+
299
+ // src/tools.ts
300
+ function createOpcCloudTools(opts) {
301
+ return [
302
+ onboardTool(opts),
303
+ findWorkTool(opts),
304
+ applyToWorkTool(opts),
305
+ registerAccountTool(opts),
306
+ statsTool(opts),
307
+ adminOverviewTool(opts),
308
+ adminUsersTool(opts),
309
+ listPoliciesTool(opts),
310
+ publishTool(opts),
311
+ updateSupplierTool(opts),
312
+ searchSupplierTool(opts),
313
+ getSupplierTool(opts),
314
+ postDemandTool(opts),
315
+ searchDemandTool(opts),
316
+ getDemandTool(opts),
317
+ startDemandChatTool(opts),
318
+ applyTool(opts),
319
+ listApplicationsTool(opts),
320
+ reviewApplicationTool(opts),
321
+ updateDemandStatusTool(opts),
322
+ listOrdersTool(opts),
323
+ updateOrderStatusTool(opts),
324
+ listConversationsTool(opts),
325
+ listMessagesTool(opts),
326
+ sendChatMessageTool(opts),
327
+ pullOrdersTool(opts)
328
+ ];
329
+ }
330
+ function onboardTool(opts) {
331
+ return {
332
+ name: "opc_platform_onboard",
333
+ toolset: "opc-cloud",
334
+ description: "Aster \u4E00\u952E\u5165\u9A7B\u661F\u73AF OPC:\u8BFB\u53D6\u672C\u5730 OPC \u516C\u53F8\u8D44\u6599,\u751F\u6210\u6CE8\u518C\u8D26\u6237\u548C\u4E0A\u67B6\u670D\u52A1\u5546\u4E3B\u9875\u8BA1\u5212;confirm:true \u540E\u6267\u884C\u3002\u9002\u5408\u201C\u5E2E\u6211\u5165\u9A7B/\u4E0A\u67B6\u5230\u661F\u73AF OPC\u201D\u3002",
335
+ parameters: {
336
+ type: "object",
337
+ properties: {
338
+ name: { type: "string", description: "\u5E73\u53F0\u8D26\u6237\u540D\u79F0,\u9ED8\u8BA4\u4F7F\u7528\u672C\u5730\u516C\u53F8\u540D\u6216\u5F53\u524D\u7528\u6237" },
339
+ email: { type: "string", description: "\u90AE\u7BB1,\u53EF\u9009" },
340
+ role: { type: "string", enum: ["user", "admin"], description: "\u89D2\u8272,\u9ED8\u8BA4 user" },
341
+ companyName: { type: "string", description: "\u516C\u53F8\u540D;\u4E0D\u586B\u5219\u4ECE\u672C\u5730 OPC \u8BFB\u53D6" },
342
+ headline: { type: "string", description: "\u4E00\u53E5\u8BDD\u5356\u70B9" },
343
+ summary: { type: "string", description: "\u670D\u52A1\u5546\u7B80\u4ECB" },
344
+ skills: { type: "array", items: { type: "string" } },
345
+ services: { type: "array", items: { type: "string" } },
346
+ priceMin: { type: "number" },
347
+ priceMax: { type: "number" },
348
+ region: { type: "string" },
349
+ confirm: { type: "boolean", default: false }
350
+ },
351
+ additionalProperties: true
352
+ },
353
+ risk: "medium",
354
+ parallelSafe: false,
355
+ async handler(args, ctx) {
356
+ const local = readLocalOpcProfile(ctx.homeDir, opts.getOpcDbPath());
357
+ const profile = compact({
358
+ ownerId: ctx.userId || str(args.name) || "local",
359
+ ...local,
360
+ ...args,
361
+ visibility: "published"
362
+ });
363
+ delete profile.confirm;
364
+ delete profile.name;
365
+ delete profile.email;
366
+ delete profile.role;
367
+ const account = compact({
368
+ name: args.name || profile.companyName || ctx.userId || "local",
369
+ email: args.email,
370
+ role: args.role
371
+ });
372
+ if (!profile.companyName) {
373
+ return "\u672A\u627E\u5230\u53EF\u4E0A\u67B6\u7684 OPC \u516C\u53F8\u8D44\u6599\u3002\u8BF7\u5148\u5728\u672C\u5730 OPC \u6CE8\u518C\u516C\u53F8,\u6216\u4F20 companyName/summary/skills/services\u3002";
374
+ }
375
+ if (!bool(args.confirm)) {
376
+ return [
377
+ "[dry-run] \u5C06\u6267\u884C\u661F\u73AF OPC \u4E00\u952E\u5165\u9A7B:",
378
+ "",
379
+ "1. \u6CE8\u518C/\u590D\u7528\u5E73\u53F0\u8D26\u6237:",
380
+ JSON.stringify(account, null, 2),
381
+ "",
382
+ "2. \u4E0A\u67B6 OPC \u670D\u52A1\u5546\u4E3B\u9875:",
383
+ JSON.stringify(profile, null, 2),
384
+ "",
385
+ "\u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_onboard \u5E76\u4F20 confirm:true\u3002"
386
+ ].join("\n");
387
+ }
388
+ const registered = await opts.getClient().registerAccount(account);
389
+ const supplier = await opts.getClient().createSupplier(profile);
390
+ return `\u661F\u73AF OPC \u5165\u9A7B\u5B8C\u6210:
391
+ \u8D26\u6237:
392
+ ${JSON.stringify(registered, null, 2)}
393
+
394
+ \u670D\u52A1\u5546\u4E3B\u9875:
395
+ ${JSON.stringify(supplier, null, 2)}`;
396
+ }
397
+ };
398
+ }
399
+ function findWorkTool(opts) {
400
+ return {
401
+ name: "opc_platform_find_work",
402
+ toolset: "opc-cloud",
403
+ description: "\u57FA\u4E8E\u672C\u5730 OPC \u516C\u53F8\u80FD\u529B\u5728\u661F\u73AF OPC \u9700\u6C42\u5927\u5385\u627E\u9002\u5408\u63A5\u7684\u5355,\u8FD4\u56DE\u6392\u5E8F\u540E\u7684\u63A8\u8350\u539F\u56E0\u3002\u9002\u5408\u201C\u5E2E\u6211\u770B\u770B\u6709\u4EC0\u4E48\u5355\u9002\u5408\u6211\u201D\u3002",
404
+ parameters: {
405
+ type: "object",
406
+ properties: {
407
+ query: { type: "string", description: "\u989D\u5916\u5173\u952E\u8BCD" },
408
+ skills: { type: "array", items: { type: "string" }, description: "\u989D\u5916\u6280\u80FD\u6807\u7B7E;\u4E0D\u586B\u5219\u4F7F\u7528\u672C\u5730 OPC \u6280\u80FD" },
409
+ category: { type: "string", description: "\u9700\u6C42\u7C7B\u578B:model/software/policy/resource/other" },
410
+ limit: { type: "number", description: "\u8FD4\u56DE\u6570\u91CF,\u9ED8\u8BA4 5" }
411
+ }
412
+ },
413
+ risk: "low",
414
+ parallelSafe: true,
415
+ async handler(args, ctx) {
416
+ const local = readLocalOpcProfile(ctx.homeDir, opts.getOpcDbPath());
417
+ const skills = unique([...list(local.skills), ...list(args.skills)]);
418
+ const payload = await opts.getClient().searchDemands(str(args.query), skills, {
419
+ status: "open",
420
+ category: str(args.category)
421
+ });
422
+ const demands = extractItems(payload);
423
+ const ranked = demands.map((item) => scoreDemand(item, skills, str(args.query))).sort((a, b) => b.score - a.score).slice(0, Number(args.limit) > 0 ? Number(args.limit) : 5);
424
+ return [
425
+ `\u57FA\u4E8E\u672C\u5730 OPC \u80FD\u529B\u63A8\u8350\u9700\u6C42\u3002\u8BC6\u522B\u5230\u6280\u80FD:${skills.length ? skills.join(", ") : "\u672A\u8BC6\u522B,\u6309\u5F00\u653E\u9700\u6C42\u6392\u5E8F"}`,
426
+ "",
427
+ JSON.stringify(ranked, null, 2)
428
+ ].join("\n");
429
+ }
430
+ };
431
+ }
432
+ function applyToWorkTool(opts) {
433
+ return {
434
+ name: "opc_platform_apply_to_work",
435
+ toolset: "opc-cloud",
436
+ description: "Aster \u63A5\u5355\u5DE5\u4F5C\u6D41:\u8BFB\u53D6\u672C\u5730 OPC \u4E3B\u9875/\u6309\u516C\u53F8\u540D\u5339\u914D\u4F9B\u5E94\u65B9,\u751F\u6210\u63A5\u5355\u7533\u8BF7\u6587\u6848,confirm:true \u540E\u6295\u9012\u3002\u9002\u5408\u201C\u8FD9\u4E2A\u5355\u6211\u63A5\u4E86\u201D\u3002",
437
+ parameters: {
438
+ type: "object",
439
+ required: ["demandId"],
440
+ properties: {
441
+ demandId: { type: "number", description: "\u9700\u6C42 ID" },
442
+ supplierId: { type: "number", description: "\u4F9B\u5E94\u65B9\u4E3B\u9875 ID;\u4E0D\u586B\u5219\u5C1D\u8BD5\u6309\u672C\u5730\u516C\u53F8\u540D\u5339\u914D" },
443
+ message: { type: "string", description: "\u63A5\u5355\u7533\u8BF7\u8BF4\u660E;\u4E0D\u586B\u5219\u81EA\u52A8\u751F\u6210" },
444
+ contact: { type: "string", description: "\u8054\u7CFB\u65B9\u5F0F" },
445
+ confirm: { type: "boolean", default: false }
446
+ }
447
+ },
448
+ risk: "medium",
449
+ parallelSafe: false,
450
+ async handler(args, ctx) {
451
+ const demandId = Number(args.demandId);
452
+ if (!Number.isFinite(demandId)) return "demandId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
453
+ const demand = await opts.getClient().getDemand(demandId);
454
+ const local = readLocalOpcProfile(ctx.homeDir, opts.getOpcDbPath());
455
+ const supplierId = Number.isFinite(Number(args.supplierId)) ? Number(args.supplierId) : await findSupplierIdByLocalProfile(opts.getClient(), local);
456
+ if (!Number.isFinite(supplierId)) {
457
+ return "\u6CA1\u6709\u627E\u5230\u53EF\u7528\u4E8E\u63A5\u5355\u7684\u4F9B\u5E94\u65B9\u4E3B\u9875\u3002\u8BF7\u5148\u8C03\u7528 opc_platform_onboard/opc_platform_publish \u4E0A\u67B6,\u6216\u4F20 supplierId\u3002";
458
+ }
459
+ const message = str(args.message) || buildApplyMessage(local, demand);
460
+ const body = compact({ supplierId, message, contact: args.contact });
461
+ if (!bool(args.confirm)) {
462
+ return [
463
+ `[dry-run] \u5C06\u5BF9\u9700\u6C42 #${demandId} \u53D1\u8D77\u63A5\u5355\u7533\u8BF7:`,
464
+ JSON.stringify(body, null, 2),
465
+ "",
466
+ "\u9700\u6C42\u8BE6\u60C5:",
467
+ JSON.stringify(demand, null, 2),
468
+ "",
469
+ "\u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_apply_to_work \u5E76\u4F20 confirm:true\u3002"
470
+ ].join("\n");
471
+ }
472
+ const result = await opts.getClient().applyDemand(demandId, body);
473
+ return `\u5DF2\u63D0\u4EA4\u63A5\u5355\u7533\u8BF7:
474
+ ${JSON.stringify(result, null, 2)}`;
475
+ }
476
+ };
477
+ }
478
+ function registerAccountTool(opts) {
479
+ return {
480
+ name: "opc_platform_register_account",
481
+ toolset: "opc-cloud",
482
+ description: "\u5728\u661F\u73AF OPC \u5728\u7EBF\u5E73\u53F0\u6CE8\u518C\u8D26\u6237\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u6CE8\u518C\u3002\u9002\u5408\u7528\u6237\u8BF4\u201C\u5E2E\u6211\u5728\u661F\u73AFOPC\u6CE8\u518C\u8D26\u6237\u201D\u3002",
483
+ parameters: {
484
+ type: "object",
485
+ required: ["name"],
486
+ properties: {
487
+ name: { type: "string", description: "\u7528\u6237\u540D\u79F0" },
488
+ email: { type: "string", description: "\u90AE\u7BB1,\u53EF\u9009" },
489
+ role: { type: "string", enum: ["user", "admin"], description: "\u89D2\u8272,\u9ED8\u8BA4 user" },
490
+ confirm: { type: "boolean", default: false }
491
+ }
492
+ },
493
+ risk: "medium",
494
+ parallelSafe: false,
495
+ async handler(args) {
496
+ const body = compact({ name: args.name, email: args.email, role: args.role });
497
+ if (!body.name) return "\u6CE8\u518C\u661F\u73AF OPC \u8D26\u6237\u81F3\u5C11\u9700\u8981 name\u3002";
498
+ if (!bool(args.confirm)) {
499
+ return `[dry-run] \u5C06\u6CE8\u518C\u661F\u73AF OPC \u8D26\u6237:
500
+ ${JSON.stringify(body, null, 2)}
501
+
502
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_register_account \u5E76\u4F20 confirm:true\u3002`;
503
+ }
504
+ const result = await opts.getClient().registerAccount(body);
505
+ return `\u5DF2\u6CE8\u518C\u661F\u73AF OPC \u8D26\u6237:
506
+ ${JSON.stringify(result, null, 2)}`;
507
+ }
508
+ };
509
+ }
510
+ function statsTool(opts) {
511
+ return {
512
+ name: "opc_platform_stats",
513
+ toolset: "opc-cloud",
514
+ description: "\u67E5\u770B OPC \u5728\u7EBF\u5E73\u53F0\u7EDF\u8BA1:\u4F01\u4E1A\u3001\u9700\u6C42\u3001\u7533\u8BF7\u3001\u6D3B\u8DC3\u8BA2\u5355\u6570\u91CF\u3002",
515
+ parameters: { type: "object", properties: {} },
516
+ risk: "low",
517
+ parallelSafe: true,
518
+ async handler() {
519
+ const result = await opts.getClient().stats();
520
+ return `OPC \u5E73\u53F0\u7EDF\u8BA1:
521
+ ${JSON.stringify(result, null, 2)}`;
522
+ }
523
+ };
524
+ }
525
+ function adminOverviewTool(opts) {
526
+ return {
527
+ name: "opc_platform_admin_overview",
528
+ toolset: "opc-cloud",
529
+ description: "\u67E5\u770B\u661F\u73AF OPC \u7BA1\u7406\u5458\u6982\u89C8:\u7528\u6237\u3001\u4F01\u4E1A\u3001\u9700\u6C42\u3001\u7533\u8BF7\u3001\u8BA2\u5355\u3001\u4F1A\u8BDD\u548C\u653F\u7B56\u6570\u91CF\u3002",
530
+ parameters: { type: "object", properties: {} },
531
+ risk: "low",
532
+ parallelSafe: true,
533
+ async handler() {
534
+ const result = await opts.getClient().adminOverview();
535
+ return `\u661F\u73AF OPC \u7BA1\u7406\u5458\u6982\u89C8:
536
+ ${JSON.stringify(result, null, 2)}`;
537
+ }
538
+ };
539
+ }
540
+ function adminUsersTool(opts) {
541
+ return {
542
+ name: "opc_platform_admin_users",
543
+ toolset: "opc-cloud",
544
+ description: "\u5217\u51FA\u661F\u73AF OPC \u5E73\u53F0\u7528\u6237\u3002",
545
+ parameters: { type: "object", properties: {} },
546
+ risk: "low",
547
+ parallelSafe: true,
548
+ async handler() {
549
+ const result = await opts.getClient().listAdminUsers();
550
+ return `\u661F\u73AF OPC \u7528\u6237\u5217\u8868:
551
+ ${JSON.stringify(result, null, 2)}`;
552
+ }
553
+ };
554
+ }
555
+ function listPoliciesTool(opts) {
556
+ return {
557
+ name: "opc_platform_list_policies",
558
+ toolset: "opc-cloud",
559
+ description: "\u67E5\u8BE2\u661F\u73AF OPC \u653F\u7B56\u5E93,\u652F\u6301\u5173\u952E\u8BCD\u548C\u5206\u7C7B\u7B5B\u9009\u3002",
560
+ parameters: {
561
+ type: "object",
562
+ properties: {
563
+ query: { type: "string", description: "\u5173\u952E\u8BCD" },
564
+ category: { type: "string", description: "\u5206\u7C7B,\u5982 OPC \u4E13\u9879/\u843D\u5730\u53C2\u8003" }
565
+ }
566
+ },
567
+ risk: "low",
568
+ parallelSafe: true,
569
+ async handler(args) {
570
+ const result = await opts.getClient().listPolicies(str(args.query), str(args.category));
571
+ return `\u661F\u73AF OPC \u653F\u7B56\u5E93:
572
+ ${JSON.stringify(result, null, 2)}`;
573
+ }
574
+ };
575
+ }
576
+ function publishTool(opts) {
577
+ return {
578
+ name: "opc_platform_publish",
579
+ toolset: "opc-cloud",
580
+ description: [
581
+ "\u628A\u672C\u5730 OPC \u516C\u53F8\u8D44\u6599\u53D1\u5E03\u5230 OPC \u5E7F\u573A\u3002\u9ED8\u8BA4 confirm:false \u53EA\u751F\u6210 dry-run \u8BA1\u5212;",
582
+ "\u7528\u6237\u786E\u8BA4\u540E\u4F20 confirm:true \u624D\u771F\u6B63\u521B\u5EFA\u7EBF\u4E0A\u4F9B\u5E94\u65B9\u4E3B\u9875\u3002"
583
+ ].join(" "),
584
+ parameters: {
585
+ type: "object",
586
+ properties: {
587
+ companyName: { type: "string", description: "\u516C\u53F8\u540D;\u4E0D\u586B\u5219\u5C1D\u8BD5\u4ECE\u672C\u5730 plugin-opc \u8BFB\u53D6" },
588
+ headline: { type: "string", description: "\u4E00\u53E5\u8BDD\u5356\u70B9" },
589
+ summary: { type: "string", description: "\u516C\u53F8\u7B80\u4ECB" },
590
+ skills: { type: "array", items: { type: "string" }, description: "\u6280\u80FD\u6807\u7B7E" },
591
+ services: { type: "array", items: { type: "string" }, description: "\u53EF\u63D0\u4F9B\u670D\u52A1" },
592
+ priceMin: { type: "number", description: "\u62A5\u4EF7\u4E0B\u9650" },
593
+ priceMax: { type: "number", description: "\u62A5\u4EF7\u4E0A\u9650" },
594
+ region: { type: "string", description: "\u5730\u533A" },
595
+ confirm: { type: "boolean", default: false, description: "true \u624D\u771F\u53D1\u5E03" }
596
+ },
597
+ additionalProperties: true
598
+ },
599
+ risk: "medium",
600
+ parallelSafe: false,
601
+ async handler(args, ctx) {
602
+ const local = readLocalOpcProfile(ctx.homeDir, opts.getOpcDbPath());
603
+ const body = compact({
604
+ ownerId: ctx.userId || "local",
605
+ ...local,
606
+ ...args,
607
+ visibility: "published"
608
+ });
609
+ delete body.confirm;
610
+ if (!body.companyName) {
611
+ return "\u672A\u627E\u5230\u53EF\u53D1\u5E03\u7684\u516C\u53F8\u8D44\u6599\u3002\u8BF7\u5148\u7528 opc_manage register_company \u6CE8\u518C\u516C\u53F8,\u6216\u5728\u672C\u5DE5\u5177\u53C2\u6570\u91CC\u63D0\u4F9B companyName / summary\u3002";
612
+ }
613
+ if (!bool(args.confirm)) {
614
+ return `[dry-run] \u5C06\u53D1\u5E03 OPC \u5E7F\u573A\u4E3B\u9875:
615
+ ${JSON.stringify(body, null, 2)}
616
+
617
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_publish \u5E76\u4F20 confirm:true\u3002`;
618
+ }
619
+ const result = await opts.getClient().createSupplier(body);
620
+ return `\u5DF2\u53D1\u5E03 OPC \u5E7F\u573A\u4E3B\u9875:
621
+ ${JSON.stringify(result, null, 2)}`;
622
+ }
623
+ };
624
+ }
625
+ function updateSupplierTool(opts) {
626
+ return {
627
+ name: "opc_platform_update_supplier",
628
+ toolset: "opc-cloud",
629
+ description: "\u66F4\u65B0 OPC \u5E7F\u573A\u4F9B\u5E94\u65B9\u4E3B\u9875\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u66F4\u65B0\u3002",
630
+ parameters: {
631
+ type: "object",
632
+ required: ["supplierId"],
633
+ properties: {
634
+ supplierId: { type: "number", description: "\u4F9B\u5E94\u65B9\u4E3B\u9875 ID" },
635
+ companyName: { type: "string" },
636
+ headline: { type: "string" },
637
+ summary: { type: "string" },
638
+ skills: { type: "array", items: { type: "string" } },
639
+ services: { type: "array", items: { type: "string" } },
640
+ priceMin: { type: "number" },
641
+ priceMax: { type: "number" },
642
+ region: { type: "string" },
643
+ visibility: { type: "string", enum: ["draft", "published"] },
644
+ confirm: { type: "boolean", default: false }
645
+ },
646
+ additionalProperties: true
647
+ },
648
+ risk: "medium",
649
+ parallelSafe: false,
650
+ async handler(args) {
651
+ const supplierId = Number(args.supplierId);
652
+ if (!Number.isFinite(supplierId)) return "supplierId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
653
+ const body = compact({ ...args });
654
+ delete body.supplierId;
655
+ delete body.confirm;
656
+ if (!bool(args.confirm)) {
657
+ return `[dry-run] \u5C06\u66F4\u65B0 OPC \u4F9B\u5E94\u65B9 #${supplierId}:
658
+ ${JSON.stringify(body, null, 2)}
659
+
660
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_update_supplier \u5E76\u4F20 confirm:true\u3002`;
661
+ }
662
+ const result = await opts.getClient().updateSupplier(supplierId, body);
663
+ return `\u5DF2\u66F4\u65B0 OPC \u4F9B\u5E94\u65B9\u4E3B\u9875:
664
+ ${JSON.stringify(result, null, 2)}`;
665
+ }
666
+ };
667
+ }
668
+ function searchSupplierTool(opts) {
669
+ return {
670
+ name: "opc_platform_search_supplier",
671
+ toolset: "opc-cloud",
672
+ description: "\u5728 OPC \u5E7F\u573A\u6309\u5173\u952E\u8BCD\u548C\u6280\u80FD\u641C\u7D22\u4F9B\u5E94\u65B9\u516C\u53F8\u3002",
673
+ parameters: {
674
+ type: "object",
675
+ properties: {
676
+ query: { type: "string", description: "\u5173\u952E\u8BCD" },
677
+ skills: { type: "array", items: { type: "string" }, description: "\u6280\u80FD\u6807\u7B7E" }
678
+ }
679
+ },
680
+ risk: "low",
681
+ parallelSafe: true,
682
+ async handler(args) {
683
+ const result = await opts.getClient().searchSuppliers(str(args.query), list(args.skills));
684
+ return `OPC \u4F9B\u5E94\u65B9\u641C\u7D22\u7ED3\u679C:
685
+ ${JSON.stringify(result, null, 2)}`;
686
+ }
687
+ };
688
+ }
689
+ function getSupplierTool(opts) {
690
+ return {
691
+ name: "opc_platform_get_supplier",
692
+ toolset: "opc-cloud",
693
+ description: "\u67E5\u770B\u67D0\u4E2A OPC \u5E7F\u573A\u4F9B\u5E94\u65B9\u4E3B\u9875\u8BE6\u60C5\u3002",
694
+ parameters: {
695
+ type: "object",
696
+ required: ["supplierId"],
697
+ properties: {
698
+ supplierId: { type: "number", description: "\u4F9B\u5E94\u65B9\u4E3B\u9875 ID" }
699
+ }
700
+ },
701
+ risk: "low",
702
+ parallelSafe: true,
703
+ async handler(args) {
704
+ const supplierId = Number(args.supplierId);
705
+ if (!Number.isFinite(supplierId)) return "supplierId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
706
+ const result = await opts.getClient().getSupplier(supplierId);
707
+ return `OPC \u4F9B\u5E94\u65B9\u8BE6\u60C5:
708
+ ${JSON.stringify(result, null, 2)}`;
709
+ }
710
+ };
711
+ }
712
+ function postDemandTool(opts) {
713
+ return {
714
+ name: "opc_platform_post_demand",
715
+ toolset: "opc-cloud",
716
+ description: "\u53D1\u5E03 OPC \u9700\u6C42\u6C60\u9700\u6C42\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u53D1\u5E03\u3002",
717
+ parameters: {
718
+ type: "object",
719
+ required: ["title"],
720
+ properties: {
721
+ title: { type: "string", description: "\u9700\u6C42\u6807\u9898" },
722
+ description: { type: "string", description: "\u9700\u6C42\u8BE6\u60C5" },
723
+ category: { type: "string", description: "\u9700\u6C42\u7C7B\u578B:model/software/policy/resource/other" },
724
+ skills: { type: "array", items: { type: "string" }, description: "\u6280\u80FD\u8981\u6C42" },
725
+ budgetMin: { type: "number", description: "\u9884\u7B97\u4E0B\u9650" },
726
+ budgetMax: { type: "number", description: "\u9884\u7B97\u4E0A\u9650" },
727
+ deadline: { type: "string", description: "\u622A\u6B62\u65E5\u671F" },
728
+ contact: { type: "string", description: "\u8054\u7CFB\u65B9\u5F0F" },
729
+ confirm: { type: "boolean", default: false }
730
+ },
731
+ additionalProperties: true
732
+ },
733
+ risk: "medium",
734
+ parallelSafe: false,
735
+ async handler(args, ctx) {
736
+ const body = compact({ ownerId: ctx.userId || "local", ...args });
737
+ delete body.confirm;
738
+ if (!body.title) return "\u53D1\u5E03\u9700\u6C42\u81F3\u5C11\u9700\u8981 title\u3002";
739
+ if (!bool(args.confirm)) {
740
+ return `[dry-run] \u5C06\u53D1\u5E03 OPC \u9700\u6C42:
741
+ ${JSON.stringify(body, null, 2)}
742
+
743
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_post_demand \u5E76\u4F20 confirm:true\u3002`;
744
+ }
745
+ const result = await opts.getClient().createDemand(body);
746
+ return `\u5DF2\u53D1\u5E03 OPC \u9700\u6C42:
747
+ ${JSON.stringify(result, null, 2)}`;
748
+ }
749
+ };
750
+ }
751
+ function searchDemandTool(opts) {
752
+ return {
753
+ name: "opc_platform_search_demand",
754
+ toolset: "opc-cloud",
755
+ description: "\u6309\u5173\u952E\u8BCD\u548C\u6280\u80FD\u641C\u7D22 OPC \u9700\u6C42\u6C60\u4E2D\u7684\u5F00\u653E\u9700\u6C42\u3002",
756
+ parameters: {
757
+ type: "object",
758
+ properties: {
759
+ query: { type: "string", description: "\u5173\u952E\u8BCD" },
760
+ skills: { type: "array", items: { type: "string" }, description: "\u6280\u80FD\u6807\u7B7E" },
761
+ status: { type: "string", description: "\u72B6\u6001\u7B5B\u9009:open/chatting/matched/closed/cancelled/accepted" },
762
+ category: { type: "string", description: "\u7C7B\u578B\u7B5B\u9009:model/software/policy/resource/other" }
763
+ }
764
+ },
765
+ risk: "low",
766
+ parallelSafe: true,
767
+ async handler(args) {
768
+ const result = await opts.getClient().searchDemands(str(args.query), list(args.skills), {
769
+ status: str(args.status),
770
+ category: str(args.category)
771
+ });
772
+ return `OPC \u9700\u6C42\u641C\u7D22\u7ED3\u679C:
773
+ ${JSON.stringify(result, null, 2)}`;
774
+ }
775
+ };
776
+ }
777
+ function getDemandTool(opts) {
778
+ return {
779
+ name: "opc_platform_get_demand",
780
+ toolset: "opc-cloud",
781
+ description: "\u67E5\u770B\u67D0\u4E2A OPC \u9700\u6C42\u8BE6\u60C5,\u5305\u542B\u7533\u8BF7\u6570\u91CF\u548C\u5DF2\u63A5\u53D7\u6570\u91CF\u3002",
782
+ parameters: {
783
+ type: "object",
784
+ required: ["demandId"],
785
+ properties: {
786
+ demandId: { type: "number", description: "\u9700\u6C42 ID" }
787
+ }
788
+ },
789
+ risk: "low",
790
+ parallelSafe: true,
791
+ async handler(args) {
792
+ const demandId = Number(args.demandId);
793
+ if (!Number.isFinite(demandId)) return "demandId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
794
+ const result = await opts.getClient().getDemand(demandId);
795
+ return `OPC \u9700\u6C42\u8BE6\u60C5:
796
+ ${JSON.stringify(result, null, 2)}`;
797
+ }
798
+ };
799
+ }
800
+ function startDemandChatTool(opts) {
801
+ return {
802
+ name: "opc_platform_start_demand_chat",
803
+ toolset: "opc-cloud",
804
+ description: "\u56F4\u7ED5\u67D0\u4E2A OPC \u9700\u6C42\u53D1\u8D77\u79C1\u804A\u4F1A\u8BDD\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u521B\u5EFA\u4F1A\u8BDD\u3002",
805
+ parameters: {
806
+ type: "object",
807
+ required: ["demandId", "userId"],
808
+ properties: {
809
+ demandId: { type: "number", description: "\u9700\u6C42 ID" },
810
+ userId: { type: "string", description: "\u53D1\u8D77\u4EBA ID/\u540D\u79F0" },
811
+ supplierId: { type: "number", description: "\u53EF\u9009\u4F9B\u5E94\u65B9\u4E3B\u9875 ID" },
812
+ confirm: { type: "boolean", default: false }
813
+ }
814
+ },
815
+ risk: "medium",
816
+ parallelSafe: false,
817
+ async handler(args) {
818
+ const demandId = Number(args.demandId);
819
+ if (!Number.isFinite(demandId)) return "demandId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
820
+ const body = compact({ userId: args.userId, supplierId: args.supplierId });
821
+ if (!body.userId) return "\u53D1\u8D77\u79C1\u804A\u9700\u8981 userId\u3002";
822
+ if (!bool(args.confirm)) {
823
+ return `[dry-run] \u5C06\u56F4\u7ED5\u9700\u6C42 #${demandId} \u521B\u5EFA\u79C1\u804A:
824
+ ${JSON.stringify(body, null, 2)}
825
+
826
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_start_demand_chat \u5E76\u4F20 confirm:true\u3002`;
827
+ }
828
+ const result = await opts.getClient().startDemandChat(demandId, body);
829
+ return `\u5DF2\u521B\u5EFA OPC \u79C1\u804A\u4F1A\u8BDD:
830
+ ${JSON.stringify(result, null, 2)}`;
831
+ }
832
+ };
833
+ }
834
+ function applyTool(opts) {
835
+ return {
836
+ name: "opc_platform_apply",
837
+ toolset: "opc-cloud",
838
+ description: "\u5BF9\u67D0\u4E2A OPC \u9700\u6C42\u53D1\u8D77\u63A5\u5355\u7533\u8BF7\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u6295\u9012\u3002",
839
+ parameters: {
840
+ type: "object",
841
+ required: ["demandId", "supplierId"],
842
+ properties: {
843
+ demandId: { type: "number", description: "\u9700\u6C42 ID" },
844
+ supplierId: { type: "number", description: "\u4F9B\u5E94\u65B9\u4E3B\u9875 ID" },
845
+ message: { type: "string", description: "\u7533\u8BF7\u8BF4\u660E" },
846
+ contact: { type: "string", description: "\u8054\u7CFB\u65B9\u5F0F" },
847
+ confirm: { type: "boolean", default: false }
848
+ }
849
+ },
850
+ risk: "medium",
851
+ parallelSafe: false,
852
+ async handler(args) {
853
+ const demandId = Number(args.demandId);
854
+ const supplierId = Number(args.supplierId);
855
+ const body = { supplierId, message: str(args.message), contact: str(args.contact) };
856
+ if (!Number.isFinite(demandId) || !Number.isFinite(supplierId)) return "demandId \u548C supplierId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
857
+ if (!bool(args.confirm)) {
858
+ return `[dry-run] \u5C06\u5BF9\u9700\u6C42 #${demandId} \u4F7F\u7528\u4F9B\u5E94\u65B9 #${supplierId} \u53D1\u8D77\u63A5\u5355\u7533\u8BF7:
859
+ ${JSON.stringify(body, null, 2)}
860
+
861
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_apply \u5E76\u4F20 confirm:true\u3002`;
862
+ }
863
+ const result = await opts.getClient().applyDemand(demandId, body);
864
+ return `\u5DF2\u63D0\u4EA4\u63A5\u5355\u7533\u8BF7:
865
+ ${JSON.stringify(result, null, 2)}`;
866
+ }
867
+ };
868
+ }
869
+ function listApplicationsTool(opts) {
870
+ return {
871
+ name: "opc_platform_list_applications",
872
+ toolset: "opc-cloud",
873
+ description: "\u5217\u51FA\u67D0\u4E2A OPC \u9700\u6C42\u7684\u63A5\u5355\u7533\u8BF7,\u5305\u542B\u4F9B\u5E94\u65B9\u4E3B\u9875\u4FE1\u606F\u3002",
874
+ parameters: {
875
+ type: "object",
876
+ required: ["demandId"],
877
+ properties: {
878
+ demandId: { type: "number", description: "\u9700\u6C42 ID" }
879
+ }
880
+ },
881
+ risk: "low",
882
+ parallelSafe: true,
883
+ async handler(args) {
884
+ const demandId = Number(args.demandId);
885
+ if (!Number.isFinite(demandId)) return "demandId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
886
+ const result = await opts.getClient().listApplications(demandId);
887
+ return `OPC \u9700\u6C42 #${demandId} \u7684\u63A5\u5355\u7533\u8BF7:
888
+ ${JSON.stringify(result, null, 2)}`;
889
+ }
890
+ };
891
+ }
892
+ function reviewApplicationTool(opts) {
893
+ return {
894
+ name: "opc_platform_review_application",
895
+ toolset: "opc-cloud",
896
+ description: "\u63A5\u53D7\u6216\u62D2\u7EDD\u67D0\u4E2A OPC \u9700\u6C42\u7684\u63A5\u5355\u7533\u8BF7\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u64CD\u4F5C\u3002",
897
+ parameters: {
898
+ type: "object",
899
+ required: ["demandId", "applicationId", "action"],
900
+ properties: {
901
+ demandId: { type: "number", description: "\u9700\u6C42 ID" },
902
+ applicationId: { type: "number", description: "\u7533\u8BF7 ID" },
903
+ action: { type: "string", enum: ["accept", "reject"], description: "accept \u63A5\u53D7 / reject \u62D2\u7EDD" },
904
+ confirm: { type: "boolean", default: false }
905
+ }
906
+ },
907
+ risk: "medium",
908
+ parallelSafe: false,
909
+ async handler(args) {
910
+ const demandId = Number(args.demandId);
911
+ const applicationId = Number(args.applicationId);
912
+ const action = str(args.action);
913
+ if (!Number.isFinite(demandId) || !Number.isFinite(applicationId)) return "demandId \u548C applicationId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
914
+ if (action !== "accept" && action !== "reject") return "action \u5FC5\u987B\u662F accept \u6216 reject\u3002";
915
+ if (!bool(args.confirm)) {
916
+ return `[dry-run] \u5C06\u5BF9\u9700\u6C42 #${demandId} \u7684\u7533\u8BF7 #${applicationId} \u6267\u884C ${action}\u3002
917
+
918
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_review_application \u5E76\u4F20 confirm:true\u3002`;
919
+ }
920
+ const result = await opts.getClient().reviewApplication(demandId, applicationId, action);
921
+ return `\u5DF2\u5904\u7406 OPC \u63A5\u5355\u7533\u8BF7:
922
+ ${JSON.stringify(result, null, 2)}`;
923
+ }
924
+ };
925
+ }
926
+ function updateDemandStatusTool(opts) {
927
+ return {
928
+ name: "opc_platform_update_demand_status",
929
+ toolset: "opc-cloud",
930
+ description: "\u66F4\u65B0 OPC \u9700\u6C42\u72B6\u6001(open/chatting/matched/closed/cancelled)\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u64CD\u4F5C\u3002",
931
+ parameters: {
932
+ type: "object",
933
+ required: ["demandId", "status"],
934
+ properties: {
935
+ demandId: { type: "number", description: "\u9700\u6C42 ID" },
936
+ status: { type: "string", enum: ["open", "chatting", "matched", "closed", "cancelled"] },
937
+ confirm: { type: "boolean", default: false }
938
+ }
939
+ },
940
+ risk: "medium",
941
+ parallelSafe: false,
942
+ async handler(args) {
943
+ const demandId = Number(args.demandId);
944
+ const status = str(args.status);
945
+ if (!Number.isFinite(demandId)) return "demandId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
946
+ if (!["open", "chatting", "matched", "closed", "cancelled"].includes(status)) return "status \u5FC5\u987B\u662F open/chatting/matched/closed/cancelled\u3002";
947
+ if (!bool(args.confirm)) {
948
+ return `[dry-run] \u5C06\u628A OPC \u9700\u6C42 #${demandId} \u72B6\u6001\u6539\u4E3A ${status}\u3002
949
+
950
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_update_demand_status \u5E76\u4F20 confirm:true\u3002`;
951
+ }
952
+ const result = await opts.getClient().updateDemandStatus(demandId, status);
953
+ return `\u5DF2\u66F4\u65B0 OPC \u9700\u6C42\u72B6\u6001:
954
+ ${JSON.stringify(result, null, 2)}`;
955
+ }
956
+ };
957
+ }
958
+ function listOrdersTool(opts) {
959
+ return {
960
+ name: "opc_platform_list_orders",
961
+ toolset: "opc-cloud",
962
+ description: "\u5217\u51FA OPC \u5728\u7EBF\u5E73\u53F0\u8BA2\u5355\u3002",
963
+ parameters: { type: "object", properties: {} },
964
+ risk: "low",
965
+ parallelSafe: true,
966
+ async handler() {
967
+ const result = await opts.getClient().listOrders();
968
+ return `OPC \u5E73\u53F0\u8BA2\u5355:
969
+ ${JSON.stringify(result, null, 2)}`;
970
+ }
971
+ };
972
+ }
973
+ function updateOrderStatusTool(opts) {
974
+ return {
975
+ name: "opc_platform_update_order_status",
976
+ toolset: "opc-cloud",
977
+ description: "\u66F4\u65B0 OPC \u5728\u7EBF\u5E73\u53F0\u8BA2\u5355\u72B6\u6001(pending/active/completed/cancelled)\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u64CD\u4F5C\u3002",
978
+ parameters: {
979
+ type: "object",
980
+ required: ["orderId", "status"],
981
+ properties: {
982
+ orderId: { type: "number", description: "\u8BA2\u5355 ID" },
983
+ status: { type: "string", enum: ["pending", "active", "completed", "cancelled"] },
984
+ confirm: { type: "boolean", default: false }
985
+ }
986
+ },
987
+ risk: "medium",
988
+ parallelSafe: false,
989
+ async handler(args) {
990
+ const orderId = Number(args.orderId);
991
+ const status = str(args.status);
992
+ if (!Number.isFinite(orderId)) return "orderId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
993
+ if (!["pending", "active", "completed", "cancelled"].includes(status)) return "status \u5FC5\u987B\u662F pending/active/completed/cancelled\u3002";
994
+ if (!bool(args.confirm)) {
995
+ return `[dry-run] \u5C06\u628A OPC \u8BA2\u5355 #${orderId} \u72B6\u6001\u6539\u4E3A ${status}\u3002
996
+
997
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_update_order_status \u5E76\u4F20 confirm:true\u3002`;
998
+ }
999
+ const result = await opts.getClient().updateOrderStatus(orderId, status);
1000
+ return `\u5DF2\u66F4\u65B0 OPC \u8BA2\u5355\u72B6\u6001:
1001
+ ${JSON.stringify(result, null, 2)}`;
1002
+ }
1003
+ };
1004
+ }
1005
+ function listConversationsTool(opts) {
1006
+ return {
1007
+ name: "opc_platform_list_conversations",
1008
+ toolset: "opc-cloud",
1009
+ description: "\u5217\u51FA\u661F\u73AF OPC \u79C1\u804A\u4F1A\u8BDD,\u53EF\u6309 userId \u7B5B\u9009\u3002",
1010
+ parameters: {
1011
+ type: "object",
1012
+ properties: {
1013
+ userId: { type: "string", description: "\u7528\u6237 ID/\u540D\u79F0,\u53EF\u9009" }
1014
+ }
1015
+ },
1016
+ risk: "low",
1017
+ parallelSafe: true,
1018
+ async handler(args) {
1019
+ const result = await opts.getClient().listConversations(str(args.userId));
1020
+ return `\u661F\u73AF OPC \u79C1\u804A\u4F1A\u8BDD:
1021
+ ${JSON.stringify(result, null, 2)}`;
1022
+ }
1023
+ };
1024
+ }
1025
+ function listMessagesTool(opts) {
1026
+ return {
1027
+ name: "opc_platform_list_messages",
1028
+ toolset: "opc-cloud",
1029
+ description: "\u67E5\u770B\u661F\u73AF OPC \u79C1\u804A\u4F1A\u8BDD\u6D88\u606F\u3002\u9002\u5408\u7528\u6237\u8BF4\u201C\u770B\u770B\u8FD9\u4E2A OPC \u4F1A\u8BDD\u804A\u4E86\u4EC0\u4E48\u201D\u3002",
1030
+ parameters: {
1031
+ type: "object",
1032
+ required: ["conversationId"],
1033
+ properties: {
1034
+ conversationId: { type: "number", description: "\u4F1A\u8BDD ID" }
1035
+ }
1036
+ },
1037
+ risk: "low",
1038
+ parallelSafe: true,
1039
+ async handler(args) {
1040
+ const conversationId = Number(args.conversationId);
1041
+ if (!Number.isFinite(conversationId)) return "conversationId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
1042
+ const result = await opts.getClient().listMessages(conversationId);
1043
+ return `\u661F\u73AF OPC \u79C1\u804A #${conversationId} \u6D88\u606F:
1044
+ ${JSON.stringify(result, null, 2)}`;
1045
+ }
1046
+ };
1047
+ }
1048
+ function sendChatMessageTool(opts) {
1049
+ return {
1050
+ name: "opc_platform_send_chat_message",
1051
+ toolset: "opc-cloud",
1052
+ description: "\u5411\u661F\u73AF OPC \u79C1\u804A\u4F1A\u8BDD\u53D1\u9001\u6D88\u606F\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u771F\u53D1\u9001\u3002",
1053
+ parameters: {
1054
+ type: "object",
1055
+ required: ["conversationId", "senderId", "content"],
1056
+ properties: {
1057
+ conversationId: { type: "number", description: "\u4F1A\u8BDD ID" },
1058
+ senderId: { type: "string", description: "\u53D1\u9001\u4EBA ID/\u540D\u79F0" },
1059
+ content: { type: "string", description: "\u6D88\u606F\u5185\u5BB9" },
1060
+ confirm: { type: "boolean", default: false }
1061
+ }
1062
+ },
1063
+ risk: "medium",
1064
+ parallelSafe: false,
1065
+ async handler(args) {
1066
+ const conversationId = Number(args.conversationId);
1067
+ const body = compact({ senderId: args.senderId, content: args.content });
1068
+ if (!Number.isFinite(conversationId)) return "conversationId \u5FC5\u987B\u662F\u6570\u5B57\u3002";
1069
+ if (!body.senderId || !body.content) return "\u53D1\u9001\u6D88\u606F\u9700\u8981 senderId \u548C content\u3002";
1070
+ if (!bool(args.confirm)) {
1071
+ return `[dry-run] \u5C06\u5411 OPC \u79C1\u804A #${conversationId} \u53D1\u9001\u6D88\u606F:
1072
+ ${JSON.stringify(body, null, 2)}
1073
+
1074
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_send_chat_message \u5E76\u4F20 confirm:true\u3002`;
1075
+ }
1076
+ const result = await opts.getClient().sendMessage(conversationId, body);
1077
+ return `\u5DF2\u53D1\u9001 OPC \u79C1\u804A\u6D88\u606F:
1078
+ ${JSON.stringify(result, null, 2)}`;
1079
+ }
1080
+ };
1081
+ }
1082
+ function pullOrdersTool(opts) {
1083
+ return {
1084
+ name: "opc_platform_pull_orders",
1085
+ toolset: "opc-cloud",
1086
+ description: "\u628A OPC \u5728\u7EBF\u5E73\u53F0\u8BA2\u5355\u56DE\u6D41\u5230\u672C\u5730 plugin-opc \u6570\u636E\u5E93,\u521B\u5EFA\u5BF9\u5E94\u5408\u540C\u548C\u9879\u76EE\u3002\u9ED8\u8BA4 dry-run,confirm:true \u624D\u5199\u672C\u5730\u6570\u636E\u5E93\u3002",
1087
+ parameters: {
1088
+ type: "object",
1089
+ properties: {
1090
+ confirm: { type: "boolean", default: false }
1091
+ }
1092
+ },
1093
+ risk: "medium",
1094
+ parallelSafe: false,
1095
+ async handler(args, ctx) {
1096
+ const payload = await opts.getClient().listOrders();
1097
+ const orders = extractOrders(payload);
1098
+ if (!bool(args.confirm)) {
1099
+ return `[dry-run] \u5C06\u628A ${orders.length} \u4E2A OPC \u5E73\u53F0\u8BA2\u5355\u56DE\u6D41\u5230\u672C\u5730 OPC \u6570\u636E\u5E93:
1100
+ ${JSON.stringify(orders, null, 2)}
1101
+
1102
+ \u786E\u8BA4\u540E\u518D\u6B21\u8C03\u7528 opc_platform_pull_orders \u5E76\u4F20 confirm:true\u3002`;
1103
+ }
1104
+ const result = syncOrdersToLocalOpc(ctx.homeDir, orders, opts.getOpcDbPath());
1105
+ return `\u5DF2\u56DE\u6D41 OPC \u5E73\u53F0\u8BA2\u5355: \u65B0\u5EFA ${result.created} \u4E2A,\u8DF3\u8FC7 ${result.skipped} \u4E2A\u3002
1106
+ \u6570\u636E\u5E93: ${result.dbPath}`;
1107
+ }
1108
+ };
1109
+ }
1110
+ function bool(value) {
1111
+ return value === true || value === "true";
1112
+ }
1113
+ function str(value) {
1114
+ return typeof value === "string" ? value.trim() : "";
1115
+ }
1116
+ function list(value) {
1117
+ if (Array.isArray(value)) return value.map((item) => String(item).trim()).filter(Boolean);
1118
+ if (typeof value === "string") return value.split(",").map((item) => item.trim()).filter(Boolean);
1119
+ return [];
1120
+ }
1121
+ function compact(input) {
1122
+ const out = {};
1123
+ for (const [key, value] of Object.entries(input)) {
1124
+ if (value === void 0 || value === null || value === "") continue;
1125
+ if (Array.isArray(value) && value.length === 0) continue;
1126
+ out[key] = value;
1127
+ }
1128
+ return out;
1129
+ }
1130
+ function extractOrders(payload) {
1131
+ if (!payload || typeof payload !== "object") return [];
1132
+ const items = payload.items;
1133
+ if (!Array.isArray(items)) return [];
1134
+ return items.map((item) => item).filter((item) => Number.isFinite(Number(item.id)));
1135
+ }
1136
+ function extractItems(payload) {
1137
+ if (!payload || typeof payload !== "object") return [];
1138
+ const items = payload.items;
1139
+ return Array.isArray(items) ? items.filter((item) => Boolean(item) && typeof item === "object") : [];
1140
+ }
1141
+ function unique(values) {
1142
+ const seen = /* @__PURE__ */ new Set();
1143
+ const out = [];
1144
+ for (const value of values) {
1145
+ const clean = value.trim();
1146
+ const key = clean.toLowerCase();
1147
+ if (!clean || seen.has(key)) continue;
1148
+ seen.add(key);
1149
+ out.push(clean);
1150
+ }
1151
+ return out;
1152
+ }
1153
+ function scoreDemand(item, skills, query) {
1154
+ const demandSkills = list(item.skills);
1155
+ const text = JSON.stringify(item).toLowerCase();
1156
+ const matchedSkills = skills.filter((skill) => demandSkills.some((itemSkill) => itemSkill.toLowerCase() === skill.toLowerCase()) || text.includes(skill.toLowerCase()));
1157
+ const queryHit = query ? text.includes(query.toLowerCase()) : false;
1158
+ const budgetMax = Number(item.budgetMax || 0);
1159
+ const score = matchedSkills.length * 20 + (queryHit ? 10 : 0) + (budgetMax > 0 ? Math.min(10, Math.round(budgetMax / 1e3)) : 0);
1160
+ return {
1161
+ id: item.id,
1162
+ title: item.title,
1163
+ category: item.category,
1164
+ status: item.status,
1165
+ budgetMin: item.budgetMin,
1166
+ budgetMax: item.budgetMax,
1167
+ deadline: item.deadline,
1168
+ skills: demandSkills,
1169
+ score,
1170
+ reason: matchedSkills.length ? `\u5339\u914D\u6280\u80FD:${matchedSkills.join(", ")}` : "\u672A\u547D\u4E2D\u660E\u786E\u6280\u80FD,\u53EF\u8FDB\u4E00\u6B65\u67E5\u770B\u9700\u6C42\u8BE6\u60C5\u5224\u65AD\u8D44\u6E90\u5339\u914D\u5EA6"
1171
+ };
1172
+ }
1173
+ async function findSupplierIdByLocalProfile(client, local) {
1174
+ const companyName = str(local.companyName);
1175
+ if (!companyName) return NaN;
1176
+ const payload = await client.searchSuppliers(companyName, []);
1177
+ const items = extractItems(payload);
1178
+ const exact = items.find((item) => str(item.companyName) === companyName) || items[0];
1179
+ return Number(exact?.id);
1180
+ }
1181
+ function buildApplyMessage(local, demandPayload) {
1182
+ const demand = demandPayload && typeof demandPayload === "object" ? demandPayload : {};
1183
+ const skills = list(local.skills);
1184
+ const services = list(local.services);
1185
+ return [
1186
+ `\u4F60\u597D,\u6211\u662F${local.companyName || "OPC \u670D\u52A1\u5546"},\u60F3\u63A5\u8FD9\u4E2A\u9700\u6C42\u3002`,
1187
+ skills.length ? `\u6211\u7684\u5339\u914D\u80FD\u529B:${skills.join("\u3001")}\u3002` : "",
1188
+ services.length ? `\u53EF\u63D0\u4F9B\u670D\u52A1:${services.join("\u3001")}\u3002` : "",
1189
+ demand.title ? `\u9488\u5BF9\u300C${String(demand.title)}\u300D\u53EF\u4EE5\u5148\u6C9F\u901A\u4EA4\u4ED8\u8303\u56F4\u3001\u5468\u671F\u548C\u9A8C\u6536\u53E3\u5F84\u3002` : "\u53EF\u4EE5\u5148\u6C9F\u901A\u4EA4\u4ED8\u8303\u56F4\u3001\u5468\u671F\u548C\u9A8C\u6536\u53E3\u5F84\u3002"
1190
+ ].filter(Boolean).join("\n");
1191
+ }
1192
+
1193
+ // src/index.ts
1194
+ var VERSION = "0.1.0";
1195
+ var OpcCloudPlugin = class {
1196
+ name = "opc-cloud";
1197
+ version = VERSION;
1198
+ description = "\u661F\u73AF OPC \u4F9B\u9700\u805A\u5408\u63D2\u4EF6(\u5165\u9A7B\u3001\u4E0A\u67B6\u3001\u627E\u5355\u3001\u53D1\u5355\u3001\u63A5\u5355\u3001\u79C1\u804A\u548C\u8BA2\u5355\u56DE\u6D41)";
1199
+ client = new OpcPlatformClient({ baseUrl: "http://127.0.0.1:8787/api" });
1200
+ opcDbPath;
1201
+ async initialize(ctx) {
1202
+ const baseUrl = typeof ctx.config.baseUrl === "string" && ctx.config.baseUrl.trim() ? ctx.config.baseUrl.trim() : "http://127.0.0.1:8787/api";
1203
+ this.opcDbPath = typeof ctx.config.opcDbPath === "string" ? ctx.config.opcDbPath : void 0;
1204
+ this.client = new OpcPlatformClient({ baseUrl });
1205
+ ctx.logger.info(`[opc-cloud] \u5DF2\u8FDE\u63A5 OPC \u5E73\u53F0 API: ${baseUrl}`);
1206
+ }
1207
+ getTools() {
1208
+ return createOpcCloudTools({
1209
+ getClient: () => this.client,
1210
+ getOpcDbPath: () => this.opcDbPath
1211
+ });
1212
+ }
1213
+ getSystemPromptBlock() {
1214
+ return OPC_CLOUD_SYSTEM_PROMPT;
1215
+ }
1216
+ };
1217
+ var plugin = new OpcCloudPlugin();
1218
+ var index_default = plugin;
1219
+ export {
1220
+ OpcCloudPlugin,
1221
+ index_default as default
1222
+ };
1223
+ //# sourceMappingURL=index.js.map