@humanclaw/humanclaw 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -41
- package/README_EN.md +47 -40
- package/dist/index.js +221 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,12 +35,13 @@ function getDb(dbPath) {
|
|
|
35
35
|
function initSchema(db2) {
|
|
36
36
|
db2.exec(`
|
|
37
37
|
CREATE TABLE IF NOT EXISTS agents (
|
|
38
|
-
agent_id
|
|
39
|
-
name
|
|
38
|
+
agent_id TEXT PRIMARY KEY,
|
|
39
|
+
name TEXT NOT NULL,
|
|
40
40
|
capabilities TEXT NOT NULL DEFAULT '[]',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
relationship TEXT NOT NULL DEFAULT '',
|
|
42
|
+
status TEXT NOT NULL DEFAULT 'IDLE'
|
|
43
|
+
CHECK (status IN ('IDLE', 'BUSY', 'OFFLINE', 'OOM')),
|
|
44
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
44
45
|
);
|
|
45
46
|
|
|
46
47
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
@@ -73,6 +74,10 @@ function initSchema(db2) {
|
|
|
73
74
|
value TEXT NOT NULL
|
|
74
75
|
);
|
|
75
76
|
`);
|
|
77
|
+
const cols = db2.prepare("PRAGMA table_info(agents)").all();
|
|
78
|
+
if (!cols.some((c) => c.name === "relationship")) {
|
|
79
|
+
db2.exec(`ALTER TABLE agents ADD COLUMN relationship TEXT NOT NULL DEFAULT ''`);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
// src/routes/nodes.ts
|
|
@@ -89,12 +94,13 @@ function createAgent(agent, db2) {
|
|
|
89
94
|
const conn = db2 ?? getDb();
|
|
90
95
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
91
96
|
conn.prepare(
|
|
92
|
-
`INSERT INTO agents (agent_id, name, capabilities, status, created_at)
|
|
93
|
-
VALUES (?, ?, ?, ?, ?)`
|
|
97
|
+
`INSERT INTO agents (agent_id, name, capabilities, relationship, status, created_at)
|
|
98
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
94
99
|
).run(
|
|
95
100
|
agent.agent_id,
|
|
96
101
|
agent.name,
|
|
97
102
|
JSON.stringify(agent.capabilities),
|
|
103
|
+
agent.relationship || "",
|
|
98
104
|
agent.status,
|
|
99
105
|
now
|
|
100
106
|
);
|
|
@@ -167,7 +173,7 @@ router.get("/status", (_req, res) => {
|
|
|
167
173
|
});
|
|
168
174
|
});
|
|
169
175
|
router.post("/", (req, res) => {
|
|
170
|
-
const { name, capabilities, status } = req.body;
|
|
176
|
+
const { name, capabilities, relationship, status } = req.body;
|
|
171
177
|
if (!name || !Array.isArray(capabilities)) {
|
|
172
178
|
res.status(400).json({ error: "name and capabilities[] are required" });
|
|
173
179
|
return;
|
|
@@ -176,6 +182,7 @@ router.post("/", (req, res) => {
|
|
|
176
182
|
agent_id: generateId("emp"),
|
|
177
183
|
name,
|
|
178
184
|
capabilities,
|
|
185
|
+
relationship: relationship || "",
|
|
179
186
|
status: status ?? "IDLE"
|
|
180
187
|
});
|
|
181
188
|
res.status(201).json(agent);
|
|
@@ -536,7 +543,8 @@ function buildUserPrompt(prompt, agents) {
|
|
|
536
543
|
const agentList = agents.map((a) => {
|
|
537
544
|
const load = a.active_task_count > 0 ? `\u5F53\u524D\u6709 ${a.active_task_count} \u4E2A\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1` : "\u5F53\u524D\u7A7A\u95F2";
|
|
538
545
|
const speed = a.avg_delivery_hours !== null ? `\u5E73\u5747\u4EA4\u4ED8\u65F6\u95F4 ${a.avg_delivery_hours}h` : "\u6682\u65E0\u5386\u53F2\u6570\u636E";
|
|
539
|
-
|
|
546
|
+
const rel = a.relationship ? ` \u5173\u7CFB: ${a.relationship}` : "";
|
|
547
|
+
return `- ${a.name} (ID: ${a.agent_id}) \u6280\u80FD: [${a.capabilities.join(", ")}]${rel} ${load} ${speed}`;
|
|
540
548
|
}).join("\n");
|
|
541
549
|
return `\u5F53\u524D\u65F6\u95F4: ${now.toISOString()}
|
|
542
550
|
|
|
@@ -738,6 +746,61 @@ function rejectTask(traceId, newDeadline, db2) {
|
|
|
738
746
|
return getTask(traceId, conn);
|
|
739
747
|
}
|
|
740
748
|
|
|
749
|
+
// src/services/simulator.ts
|
|
750
|
+
function buildSimulatePrompt(agentName, relationship, capabilities, taskDescription, deadline) {
|
|
751
|
+
return `\u4F60\u73B0\u5728\u626E\u6F14\u4E00\u4E2A\u540D\u53EB\u300C${agentName}\u300D\u7684\u4EBA\uFF0C\u4F60\u7684\u6280\u80FD\u6807\u7B7E\u662F [${capabilities.join(", ")}]\u3002
|
|
752
|
+
${relationship ? `\u4F60\u548C\u5E03\u7F6E\u4EFB\u52A1\u7684\u4EBA\u7684\u5173\u7CFB\u662F\uFF1A${relationship}\u3002` : ""}
|
|
753
|
+
|
|
754
|
+
\u4F60\u6536\u5230\u4E86\u4E00\u4E2A\u4EFB\u52A1\uFF1A
|
|
755
|
+
${taskDescription}
|
|
756
|
+
|
|
757
|
+
\u622A\u6B62\u65F6\u95F4\uFF1A${new Date(deadline).toLocaleString("zh-CN")}
|
|
758
|
+
|
|
759
|
+
\u8BF7\u7AD9\u5728\u300C${agentName}\u300D\u7684\u89C6\u89D2\uFF0C\u7528\u8FD9\u4E2A\u4EBA\u7269\u81EA\u7136\u7684\u8BED\u6C14\u548C\u53E3\u543B\uFF0C\u5199\u4E00\u6BB5\u4EFB\u52A1\u4EA4\u4ED8\u6C47\u62A5\u3002
|
|
760
|
+
\u8981\u6C42\uFF1A
|
|
761
|
+
1. \u5185\u5BB9\u8981\u8D34\u5408\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u4F53\u73B0\u4E13\u4E1A\u80FD\u529B
|
|
762
|
+
2. \u8BED\u6C14\u7B26\u5408\u4EBA\u7269\u8EAB\u4EFD${relationship ? "\u548C\u4E0E\u4E0A\u7EA7\u7684\u5173\u7CFB" : ""}
|
|
763
|
+
3. \u6C47\u62A5\u5185\u5BB9\u5177\u4F53\u3001\u6709\u7EC6\u8282\uFF0C\u5305\u542B\u505A\u4E86\u4EC0\u4E48\u3001\u9047\u5230\u4E86\u4EC0\u4E48\u95EE\u9898\u3001\u6700\u7EC8\u7ED3\u679C\u5982\u4F55
|
|
764
|
+
4. \u5B57\u6570 200-400 \u5B57
|
|
765
|
+
5. \u76F4\u63A5\u8F93\u51FA\u6C47\u62A5\u5185\u5BB9\uFF0C\u4E0D\u8981\u52A0\u4EFB\u4F55\u683C\u5F0F\u524D\u7F00\u6216\u8BF4\u660E`;
|
|
766
|
+
}
|
|
767
|
+
async function simulateDelivery(traceId, provider, db2) {
|
|
768
|
+
const conn = db2 ?? getDb();
|
|
769
|
+
const task = getTask(traceId, conn);
|
|
770
|
+
if (!task) {
|
|
771
|
+
throw new Error(`Task not found: ${traceId}`);
|
|
772
|
+
}
|
|
773
|
+
const agent = getAgent(task.assignee_id, conn);
|
|
774
|
+
if (!agent) {
|
|
775
|
+
throw new Error(`Agent not found: ${task.assignee_id}`);
|
|
776
|
+
}
|
|
777
|
+
const llm = provider ?? createLlmProvider();
|
|
778
|
+
const response = await llm.complete({
|
|
779
|
+
messages: [
|
|
780
|
+
{
|
|
781
|
+
role: "system",
|
|
782
|
+
content: "\u4F60\u662F\u4E00\u4E2A\u89D2\u8272\u626E\u6F14\u4E13\u5BB6\uFF0C\u64C5\u957F\u6A21\u62DF\u4E0D\u540C\u4EBA\u7269\u7684\u8BED\u6C14\u548C\u6C47\u62A5\u98CE\u683C\u3002"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
role: "user",
|
|
786
|
+
content: buildSimulatePrompt(
|
|
787
|
+
agent.name,
|
|
788
|
+
agent.relationship,
|
|
789
|
+
agent.capabilities,
|
|
790
|
+
task.todo_description,
|
|
791
|
+
task.deadline
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
],
|
|
795
|
+
temperature: 0.7,
|
|
796
|
+
max_tokens: 1024
|
|
797
|
+
});
|
|
798
|
+
return {
|
|
799
|
+
trace_id: traceId,
|
|
800
|
+
simulated_delivery: response.content
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
741
804
|
// src/routes/tasks.ts
|
|
742
805
|
var router3 = Router3();
|
|
743
806
|
router3.post("/resume", (req, res) => {
|
|
@@ -776,13 +839,72 @@ router3.post("/reject", (req, res) => {
|
|
|
776
839
|
res.status(400).json({ error: message });
|
|
777
840
|
}
|
|
778
841
|
});
|
|
842
|
+
router3.post("/simulate", async (req, res) => {
|
|
843
|
+
const { trace_id } = req.body;
|
|
844
|
+
if (!trace_id) {
|
|
845
|
+
res.status(400).json({ error: "trace_id is required" });
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
const result = await simulateDelivery(trace_id);
|
|
850
|
+
res.json(result);
|
|
851
|
+
} catch (error) {
|
|
852
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
853
|
+
const status = message.includes("API Key") || message.includes("API key") ? 503 : 400;
|
|
854
|
+
res.status(status).json({ error: message });
|
|
855
|
+
}
|
|
856
|
+
});
|
|
779
857
|
var tasks_default = router3;
|
|
780
858
|
|
|
781
859
|
// src/routes/sync.ts
|
|
782
860
|
import { Router as Router4 } from "express";
|
|
783
861
|
|
|
784
|
-
// src/services/
|
|
785
|
-
function
|
|
862
|
+
// src/services/reviewer.ts
|
|
863
|
+
function buildReviewSystemPrompt() {
|
|
864
|
+
return `\u4F60\u662F HumanClaw \u4EA4\u4ED8\u5BA1\u67E5\u5458\u3002\u4F60\u7684\u5DE5\u4F5C\u662F\u5BA1\u67E5\u78B3\u57FA\u8282\u70B9\u63D0\u4EA4\u7684\u6240\u6709\u4EFB\u52A1\u4EA4\u4ED8\u7269\uFF0C\u751F\u6210\u6574\u4F53\u5BA1\u67E5\u62A5\u544A\u3002
|
|
865
|
+
|
|
866
|
+
\u5BA1\u67E5\u8981\u70B9\uFF1A
|
|
867
|
+
1. \u68C0\u67E5\u6BCF\u4E2A\u4EA4\u4ED8\u7269\u662F\u5426\u6EE1\u8DB3\u539F\u59CB\u4EFB\u52A1\u8981\u6C42
|
|
868
|
+
2. \u5982\u679C\u4EA4\u4ED8\u7269\u5305\u542B GitHub URL\uFF08PR\u3001commit\u3001issue \u7B49\uFF09\uFF0C\u5206\u6790\u5176\u5185\u5BB9\u548C\u8D28\u91CF
|
|
869
|
+
3. \u6307\u51FA\u6F5C\u5728\u95EE\u9898\u6216\u6539\u8FDB\u5EFA\u8BAE
|
|
870
|
+
4. \u7ED9\u51FA\u6574\u4F53\u5B8C\u6210\u5EA6\u8BC4\u5206\uFF081-10\uFF09\u548C\u603B\u7ED3
|
|
871
|
+
|
|
872
|
+
\u8F93\u51FA\u683C\u5F0F\uFF1A
|
|
873
|
+
- \u4F7F\u7528 Markdown \u683C\u5F0F
|
|
874
|
+
- \u5148\u7ED9\u603B\u7ED3\u8BC4\u5206\uFF0C\u518D\u9010\u4E2A\u5206\u6790\u6BCF\u4E2A\u5B50\u4EFB\u52A1\u7684\u4EA4\u4ED8\u8D28\u91CF`;
|
|
875
|
+
}
|
|
876
|
+
function buildReviewUserPrompt(originalPrompt, tasks) {
|
|
877
|
+
let prompt = `\u539F\u59CB\u9700\u6C42: ${originalPrompt}
|
|
878
|
+
|
|
879
|
+
`;
|
|
880
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
881
|
+
const t = tasks[i];
|
|
882
|
+
let resultText = "";
|
|
883
|
+
if (t.result_data) {
|
|
884
|
+
if (typeof t.result_data === "string") {
|
|
885
|
+
resultText = t.result_data;
|
|
886
|
+
} else if (typeof t.result_data === "object") {
|
|
887
|
+
const rd = t.result_data;
|
|
888
|
+
resultText = rd.text || JSON.stringify(rd, null, 2);
|
|
889
|
+
} else {
|
|
890
|
+
resultText = String(t.result_data);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
prompt += `--- \u5B50\u4EFB\u52A1 ${i + 1} ---
|
|
894
|
+
`;
|
|
895
|
+
prompt += `\u6267\u884C\u8005: ${t.assignee_id}
|
|
896
|
+
`;
|
|
897
|
+
prompt += `\u4EFB\u52A1: ${t.todo_description}
|
|
898
|
+
`;
|
|
899
|
+
prompt += `\u4EA4\u4ED8\u7269:
|
|
900
|
+
${resultText}
|
|
901
|
+
|
|
902
|
+
`;
|
|
903
|
+
}
|
|
904
|
+
prompt += "\u8BF7\u5BA1\u67E5\u4EE5\u4E0A\u6240\u6709\u4EA4\u4ED8\u7269\uFF0C\u751F\u6210\u5BA1\u67E5\u62A5\u544A\u3002";
|
|
905
|
+
return prompt;
|
|
906
|
+
}
|
|
907
|
+
async function reviewJob(jobId, provider, db2) {
|
|
786
908
|
const conn = db2 ?? getDb();
|
|
787
909
|
const job = getJobWithTasks(jobId, conn);
|
|
788
910
|
if (!job) {
|
|
@@ -791,62 +913,47 @@ function aggregateJob(jobId, db2) {
|
|
|
791
913
|
if (!isJobComplete(jobId, conn)) {
|
|
792
914
|
const resolved = job.tasks.filter((t) => t.status === "RESOLVED").length;
|
|
793
915
|
throw new Error(
|
|
794
|
-
`Job
|
|
916
|
+
`Job \u5C1A\u672A\u5168\u90E8\u5B8C\u6210: ${resolved}/${job.tasks.length} \u4E2A\u4EFB\u52A1\u5DF2\u4EA4\u4ED8`
|
|
795
917
|
);
|
|
796
918
|
}
|
|
919
|
+
const llm = provider ?? createLlmProvider();
|
|
920
|
+
const response = await llm.complete({
|
|
921
|
+
messages: [
|
|
922
|
+
{ role: "system", content: buildReviewSystemPrompt() },
|
|
923
|
+
{
|
|
924
|
+
role: "user",
|
|
925
|
+
content: buildReviewUserPrompt(
|
|
926
|
+
job.original_prompt,
|
|
927
|
+
job.tasks.map((t) => ({
|
|
928
|
+
assignee_id: t.assignee_id,
|
|
929
|
+
todo_description: t.todo_description,
|
|
930
|
+
result_data: t.result_data
|
|
931
|
+
}))
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
],
|
|
935
|
+
temperature: 0.3,
|
|
936
|
+
max_tokens: 4096
|
|
937
|
+
});
|
|
797
938
|
return {
|
|
798
|
-
job_id:
|
|
939
|
+
job_id: jobId,
|
|
799
940
|
original_prompt: job.original_prompt,
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
trace_id: t.trace_id,
|
|
803
|
-
assignee_id: t.assignee_id,
|
|
804
|
-
todo_description: t.todo_description,
|
|
805
|
-
result_data: t.result_data
|
|
806
|
-
})),
|
|
807
|
-
aggregated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
941
|
+
review: response.content,
|
|
942
|
+
reviewed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
808
943
|
};
|
|
809
944
|
}
|
|
810
|
-
async function syncToOpenClaw(aggregation) {
|
|
811
|
-
if (!aggregation.openclaw_callback) {
|
|
812
|
-
return {
|
|
813
|
-
success: true,
|
|
814
|
-
message: "No OpenClaw callback configured, skipping sync"
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
try {
|
|
818
|
-
const response = await fetch(aggregation.openclaw_callback, {
|
|
819
|
-
method: "POST",
|
|
820
|
-
headers: { "Content-Type": "application/json" },
|
|
821
|
-
body: JSON.stringify(aggregation)
|
|
822
|
-
});
|
|
823
|
-
if (!response.ok) {
|
|
824
|
-
return {
|
|
825
|
-
success: false,
|
|
826
|
-
message: `OpenClaw sync failed: ${response.status} ${response.statusText}`
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
return { success: true, message: "Synced to OpenClaw successfully" };
|
|
830
|
-
} catch (error) {
|
|
831
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
832
|
-
return { success: false, message: `OpenClaw sync error: ${message}` };
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
945
|
|
|
836
946
|
// src/routes/sync.ts
|
|
837
947
|
var router4 = Router4();
|
|
838
|
-
router4.post("/:job_id/
|
|
948
|
+
router4.post("/:job_id/review", async (req, res) => {
|
|
839
949
|
const { job_id } = req.params;
|
|
840
950
|
try {
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
res.json({
|
|
844
|
-
aggregation,
|
|
845
|
-
sync: syncResult
|
|
846
|
-
});
|
|
951
|
+
const result = await reviewJob(job_id);
|
|
952
|
+
res.json(result);
|
|
847
953
|
} catch (error) {
|
|
848
954
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
849
|
-
|
|
955
|
+
const status = message.includes("API Key") || message.includes("API key") ? 503 : 400;
|
|
956
|
+
res.status(status).json({ error: message });
|
|
850
957
|
}
|
|
851
958
|
});
|
|
852
959
|
var sync_default = router4;
|
|
@@ -1104,6 +1211,7 @@ async function loadFleet(el){
|
|
|
1104
1211
|
for(const a of d.agents){
|
|
1105
1212
|
h+='<div class="card"><div class="agent-header"><span class="dot '+a.status+'"></span><span class="agent-name">'+esc(a.name)+'</span></div>';
|
|
1106
1213
|
h+='<div class="agent-id">'+a.agent_id+'</div>';
|
|
1214
|
+
if(a.relationship)h+='<div style="font-size:11px;color:var(--purple);margin-bottom:4px">👥 '+esc(a.relationship)+'</div>';
|
|
1107
1215
|
h+='<div class="caps">';for(const c of a.capabilities)h+='<span class="cap">'+esc(c)+'</span>';h+='</div>';
|
|
1108
1216
|
h+='<div class="agent-meta"><span>\u4EFB\u52A1: '+a.active_task_count+'</span>';
|
|
1109
1217
|
if(a.avg_delivery_hours!==null)h+='<span>\u5E73\u5747\u4EA4\u4ED8: '+a.avg_delivery_hours+'h</span>';
|
|
@@ -1125,6 +1233,7 @@ window.showAddAgent=function(){
|
|
|
1125
1233
|
ov.innerHTML='<div class="form-card"><h3>+ \u6DFB\u52A0\u78B3\u57FA\u8282\u70B9</h3>'
|
|
1126
1234
|
+'<div class="fg"><label>\u8282\u70B9\u540D\u79F0</label><input id="aa-name" placeholder="\u4F8B: \u524D\u7AEF\u8001\u674E"/></div>'
|
|
1127
1235
|
+'<div class="fg"><label>\u6280\u80FD\u6807\u7B7E</label><input id="aa-caps" placeholder="\u4F8B: UI/UX, \u524D\u7AEF\u5F00\u53D1, \u6297\u538B\u80FD\u529B\u5F3A"/><div class="hint">\u591A\u4E2A\u6807\u7B7E\u7528\u9017\u53F7\u5206\u9694</div></div>'
|
|
1236
|
+
+'<div class="fg"><label>\u4E0E\u4F60\u7684\u5173\u7CFB <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009)</span></label><input id="aa-rel" placeholder="\u4F8B: \u76F4\u5C5E\u4E0B\u5C5E / \u5B9E\u4E60\u751F / \u5916\u5305\u540C\u4E8B / \u4E49\u5F1F"/><div class="hint">\u63CF\u8FF0\u6B64\u4EBA\u4E0E\u4F60\u7684\u5173\u7CFB\uFF0CAI \u89C4\u5212\u548C\u6A21\u62DF\u4EA4\u4ED8\u65F6\u4F1A\u53C2\u8003</div></div>'
|
|
1128
1237
|
+'<div class="btn-group"><button class="btn btn-primary" onclick="submitAgent()">\u6CE8\u518C\u8282\u70B9</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div></div>';
|
|
1129
1238
|
document.body.appendChild(ov);
|
|
1130
1239
|
document.getElementById('aa-name').focus();
|
|
@@ -1132,10 +1241,11 @@ window.showAddAgent=function(){
|
|
|
1132
1241
|
window.submitAgent=async function(){
|
|
1133
1242
|
const name=document.getElementById('aa-name').value.trim();
|
|
1134
1243
|
const caps=document.getElementById('aa-caps').value.trim();
|
|
1244
|
+
const rel=document.getElementById('aa-rel').value.trim();
|
|
1135
1245
|
if(!name){toast('\u8BF7\u8F93\u5165\u8282\u70B9\u540D\u79F0',false);return}
|
|
1136
1246
|
if(!caps){toast('\u8BF7\u8F93\u5165\u81F3\u5C11\u4E00\u4E2A\u6280\u80FD\u6807\u7B7E',false);return}
|
|
1137
1247
|
try{
|
|
1138
|
-
const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean)})});
|
|
1248
|
+
const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean),relationship:rel})});
|
|
1139
1249
|
const d=await r.json();
|
|
1140
1250
|
if(!r.ok){toast(d.error||'\u6CE8\u518C\u5931\u8D25',false);return}
|
|
1141
1251
|
toast('\u8282\u70B9 '+d.agent_id+' \u6CE8\u518C\u6210\u529F\uFF01',true);
|
|
@@ -1190,7 +1300,7 @@ async function loadPipeline(el){
|
|
|
1190
1300
|
allTasks.push(...j.tasks);
|
|
1191
1301
|
h+='<div class="job-card"><div class="job-hd"><span class="job-title">'+esc(j.original_prompt)+'</span><span class="job-id">'+j.job_id+'</span></div>';
|
|
1192
1302
|
h+='<div class="pbar-wrap"><div class="pbar-label">'+res+'/'+tot+' \u5DF2\u5B8C\u6210 ('+pct+'%)</div><div class="pbar"><div class="pbar-fill" style="width:'+pct+'%"></div></div></div>';
|
|
1193
|
-
if(pct===100)h+='<div style="margin-bottom:12px"><button class="btn btn-green btn-sm" onclick="
|
|
1303
|
+
if(pct===100)h+='<div style="margin-bottom:12px"><button class="btn btn-green btn-sm" onclick="reviewJob(\\''+j.job_id+'\\')">AI \u805A\u5408\u5BA1\u67E5</button></div>';
|
|
1194
1304
|
h+='<div class="kanban"><div class="lane"><div class="lane-hd y">\u5DF2\u5206\u53D1 ('+dispatched.length+')</div>'+dispatched.map(tcard).join('')+'</div>';
|
|
1195
1305
|
h+='<div class="lane"><div class="lane-hd r">\u5DF2\u8D85\u65F6 ('+overdue.length+')</div>'+overdue.map(tcard).join('')+'</div>';
|
|
1196
1306
|
h+='<div class="lane"><div class="lane-hd g">\u5DF2\u4EA4\u4ED8 ('+done.length+')</div>'+done.map(tcard).join('')+'</div></div></div>';
|
|
@@ -1247,7 +1357,6 @@ function renderPlanStep1(ov){
|
|
|
1247
1357
|
+'<div class="fg"><label>\u8F93\u5165\u4F60\u7684\u9700\u6C42</label><textarea id="plan-prompt" rows="3" placeholder="\u4F8B: \u5B8C\u6210\u9996\u9875\u91CD\u6784\uFF0C\u5305\u62EC\u5BFC\u822A\u680F\u3001\u5185\u5BB9\u533A\u548C\u9875\u811A\u7684\u54CD\u5E94\u5F0F\u6539\u7248" style="font-family:var(--font-sans);font-size:13px"></textarea></div>'
|
|
1248
1358
|
+'<div class="fg"><label>\u9009\u62E9\u53C2\u4E0E\u7684\u78B3\u57FA\u8282\u70B9 <span style="color:var(--text-dim);font-weight:400">(\u9ED8\u8BA4\u9009\u4E2D\u7A7A\u95F2\u8282\u70B9)</span></label>'
|
|
1249
1359
|
+'<div id="agent-chip-area">'+renderChips('all')+'</div></div>'
|
|
1250
|
-
+'<div class="fg"><label>OpenClaw \u56DE\u8C03\u5730\u5740 <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009)</span></label><input id="plan-callback" placeholder="https://..."/></div>'
|
|
1251
1360
|
+'<div class="btn-group">'
|
|
1252
1361
|
+'<button class="btn btn-primary" onclick="planWithAI()">AI \u89C4\u5212</button>'
|
|
1253
1362
|
+'<button class="btn btn-ghost" onclick="showManualCreate()">\u624B\u52A8\u521B\u5EFA</button>'
|
|
@@ -1258,7 +1367,6 @@ function renderPlanStep1(ov){
|
|
|
1258
1367
|
|
|
1259
1368
|
window.planWithAI=async function(){
|
|
1260
1369
|
const prompt=document.getElementById('plan-prompt').value.trim();
|
|
1261
|
-
const callback=document.getElementById('plan-callback')?.value.trim()||'';
|
|
1262
1370
|
if(!prompt){toast('\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0',false);return}
|
|
1263
1371
|
if(selectedAgentIds.size===0){toast('\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u78B3\u57FA\u8282\u70B9',false);return}
|
|
1264
1372
|
|
|
@@ -1274,7 +1382,7 @@ window.planWithAI=async function(){
|
|
|
1274
1382
|
renderPlanStep1(ov);
|
|
1275
1383
|
return;
|
|
1276
1384
|
}
|
|
1277
|
-
currentPlan={...d
|
|
1385
|
+
currentPlan={...d};
|
|
1278
1386
|
renderPlanStep2(ov);
|
|
1279
1387
|
}catch(e){
|
|
1280
1388
|
toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);
|
|
@@ -1294,7 +1402,8 @@ function renderPlanStep2(ov){
|
|
|
1294
1402
|
h+='<div class="plan-card-hd"><span class="adot '+status+'"></span><span class="plan-card-agent">'+esc(t.assignee_name)+'</span><span class="plan-card-agentid">'+t.assignee_id+'</span></div>';
|
|
1295
1403
|
h+='<div class="plan-card-desc">'+esc(t.todo_description)+'</div>';
|
|
1296
1404
|
h+='<div class="briefing-box"><div class="briefing-label">\u8BDD\u672F (\u53EF\u76F4\u63A5\u53D1\u7ED9 TA)</div><button class="briefing-copy" onclick="window._copyBriefing('+i+')">\u590D\u5236</button>'+esc(t.briefing)+'</div>';
|
|
1297
|
-
|
|
1405
|
+
const dlVal=t.deadline.slice(0,16);
|
|
1406
|
+
h+='<div class="plan-card-dl"><label style="font-size:10px;color:var(--text-dim);margin-right:6px">\u622A\u6B62\u65F6\u95F4</label><input type="datetime-local" class="plan-dl-input" data-idx="'+i+'" value="'+dlVal+'" style="background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;color:var(--text);font-family:var(--font-mono);font-size:12px;outline:none"/></div>';
|
|
1298
1407
|
h+='</div>';
|
|
1299
1408
|
}
|
|
1300
1409
|
h+='<div class="btn-group">';
|
|
@@ -1313,9 +1422,15 @@ window._copyBriefing=function(i){
|
|
|
1313
1422
|
|
|
1314
1423
|
window.dispatchPlan=async function(){
|
|
1315
1424
|
const p=currentPlan;
|
|
1316
|
-
|
|
1425
|
+
// Read adjusted deadlines from inputs
|
|
1426
|
+
const dlInputs=document.querySelectorAll('.plan-dl-input');
|
|
1427
|
+
const tasks=p.planned_tasks.map((t,i)=>{
|
|
1428
|
+
const input=dlInputs[i];
|
|
1429
|
+
const dl=input?new Date(input.value).toISOString():t.deadline;
|
|
1430
|
+
return {assignee_id:t.assignee_id,todo_description:t.todo_description,deadline:dl};
|
|
1431
|
+
});
|
|
1317
1432
|
try{
|
|
1318
|
-
const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:p.original_prompt,openclaw_callback:
|
|
1433
|
+
const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:p.original_prompt,openclaw_callback:'',tasks})});
|
|
1319
1434
|
const d=await r.json();
|
|
1320
1435
|
if(!r.ok){toast(d.error||'\u5206\u53D1\u5931\u8D25',false);return}
|
|
1321
1436
|
toast('\u4EFB\u52A1\u5DF2\u5206\u53D1\uFF01Job: '+d.job_id,true);
|
|
@@ -1331,7 +1446,6 @@ window.showManualCreate=function(){
|
|
|
1331
1446
|
const tomorrow=new Date(Date.now()+86400000).toISOString().slice(0,16);
|
|
1332
1447
|
ov.querySelector('.form-card').innerHTML='<h3>\u624B\u52A8\u521B\u5EFA\u4EFB\u52A1</h3>'
|
|
1333
1448
|
+'<div class="fg"><label>\u4EFB\u52A1\u63CF\u8FF0 (\u539F\u59CB\u9700\u6C42)</label><input id="cj-prompt" placeholder="\u4F8B: \u5B8C\u6210\u9996\u9875\u6539\u7248"/></div>'
|
|
1334
|
-
+'<div class="fg"><label>OpenClaw \u56DE\u8C03\u5730\u5740 (\u53EF\u9009)</label><input id="cj-callback" placeholder="https://..."/></div>'
|
|
1335
1449
|
+'<div style="margin-bottom:10px;display:flex;justify-content:space-between;align-items:center"><label style="font-size:13px;font-weight:600;color:var(--text)">\u5B50\u4EFB\u52A1\u5217\u8868</label><button class="btn btn-ghost btn-sm" onclick="addTaskRow()">+ \u6DFB\u52A0\u5B50\u4EFB\u52A1</button></div>'
|
|
1336
1450
|
+'<div id="task-rows"><div class="task-row"><div class="fg"><label>\u6307\u6D3E\u8282\u70B9</label><select class="tr-agent">'+optionsHtml+'</select></div><div class="fg"><label>\u4EFB\u52A1\u63CF\u8FF0</label><input class="tr-desc" placeholder="\u5177\u4F53\u8981\u505A\u4EC0\u4E48..."/></div><div class="fg"><label>\u622A\u6B62\u65F6\u95F4</label><input class="tr-deadline" type="datetime-local" value="'+tomorrow+'"/></div></div></div>'
|
|
1337
1451
|
+'<div class="btn-group"><button class="btn btn-primary" onclick="submitJob()">\u521B\u5EFA\u5E76\u5206\u53D1</button><button class="btn btn-ghost" onclick="renderPlanStep1(document.getElementById(\\'overlay\\'))">\u8FD4\u56DE AI \u89C4\u5212</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div>';
|
|
@@ -1348,7 +1462,6 @@ window.addTaskRow=function(){
|
|
|
1348
1462
|
};
|
|
1349
1463
|
window.submitJob=async function(){
|
|
1350
1464
|
const prompt=document.getElementById('cj-prompt').value.trim();
|
|
1351
|
-
const callback=document.getElementById('cj-callback').value.trim();
|
|
1352
1465
|
if(!prompt){toast('\u8BF7\u8F93\u5165\u4EFB\u52A1\u63CF\u8FF0',false);return}
|
|
1353
1466
|
const rows=document.querySelectorAll('.task-row');
|
|
1354
1467
|
const tasks=[];
|
|
@@ -1361,7 +1474,7 @@ window.submitJob=async function(){
|
|
|
1361
1474
|
}
|
|
1362
1475
|
if(!tasks.length){toast('\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u5B50\u4EFB\u52A1',false);return}
|
|
1363
1476
|
try{
|
|
1364
|
-
const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:prompt,openclaw_callback:
|
|
1477
|
+
const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:prompt,openclaw_callback:'',tasks})});
|
|
1365
1478
|
const d=await r.json();
|
|
1366
1479
|
if(!r.ok){toast(d.error||'\u521B\u5EFA\u5931\u8D25',false);return}
|
|
1367
1480
|
toast('\u4EFB\u52A1\u5DF2\u521B\u5EFA\u5E76\u5206\u53D1\uFF01Job: '+d.job_id,true);
|
|
@@ -1369,13 +1482,22 @@ window.submitJob=async function(){
|
|
|
1369
1482
|
load('pipeline');
|
|
1370
1483
|
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
1371
1484
|
};
|
|
1372
|
-
window.
|
|
1485
|
+
window.reviewJob=async function(jobId){
|
|
1486
|
+
const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';
|
|
1487
|
+
ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});
|
|
1488
|
+
ov.innerHTML='<div class="form-card"><h3>AI \u805A\u5408\u5BA1\u67E5</h3><div class="spinner-wrap"><div class="spinner"></div><div class="spinner-text">AI \u6B63\u5728\u5BA1\u67E5\u6240\u6709\u4EA4\u4ED8\u7269...</div></div></div>';
|
|
1489
|
+
document.body.appendChild(ov);
|
|
1373
1490
|
try{
|
|
1374
|
-
const r=await fetch(API+'/jobs/'+jobId+'/
|
|
1491
|
+
const r=await fetch(API+'/jobs/'+jobId+'/review',{method:'POST'});
|
|
1375
1492
|
const d=await r.json();
|
|
1376
|
-
if(!r.ok){toast(d.error||'\
|
|
1377
|
-
|
|
1378
|
-
|
|
1493
|
+
if(!r.ok){toast(d.error||'\u5BA1\u67E5\u5931\u8D25',false);ov.remove();return}
|
|
1494
|
+
const fc=ov.querySelector('.form-card');
|
|
1495
|
+
fc.innerHTML='<h3>AI \u805A\u5408\u5BA1\u67E5</h3>'
|
|
1496
|
+
+'<div style="font-size:12px;color:var(--text-dim);margin-bottom:12px">\u9700\u6C42: '+esc(d.original_prompt)+'</div>'
|
|
1497
|
+
+'<div class="result-display" style="max-height:400px">'+esc(d.review).replace(/\\n/g,'<br>')+'</div>'
|
|
1498
|
+
+'<div style="font-size:10px;color:var(--text-dim);margin-top:8px;font-family:var(--font-mono)">\u5BA1\u67E5\u65F6\u95F4: '+new Date(d.reviewed_at).toLocaleString()+'</div>'
|
|
1499
|
+
+'<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
1500
|
+
}catch(e){toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);ov.remove()}
|
|
1379
1501
|
};
|
|
1380
1502
|
|
|
1381
1503
|
// \u2500\u2500\u2500 Task Detail Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -1400,13 +1522,14 @@ window.showTaskDetail=function(traceId){
|
|
|
1400
1522
|
if(t.status==='RESOLVED'&&t.result_data){
|
|
1401
1523
|
// Show result
|
|
1402
1524
|
let resultText='';
|
|
1403
|
-
|
|
1525
|
+
if(typeof t.result_data==='object'&&t.result_data){resultText=t.result_data.text||JSON.stringify(t.result_data,null,2)}else{resultText=String(t.result_data||'')}
|
|
1404
1526
|
h+='<div class="detail-row"><div class="detail-label">\u4EA4\u4ED8\u7ED3\u679C</div><div class="result-display">'+esc(resultText)+'</div></div>';
|
|
1405
1527
|
h+='<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
1406
1528
|
}else{
|
|
1407
1529
|
// Input for resume/reject
|
|
1408
|
-
h+='<div class="fg" style="margin-top:16px"><label>\u63D0\u4EA4 Human \u4EA4\u4ED8\u7269</label><textarea id="td-payload" rows="4" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001\u5DE5\u4F5C\u6C47\u62A5\
|
|
1530
|
+
h+='<div class="fg" style="margin-top:16px"><label>\u63D0\u4EA4 Human \u4EA4\u4ED8\u7269</label><textarea id="td-payload" rows="4" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>';
|
|
1409
1531
|
h+='<div class="btn-group">';
|
|
1532
|
+
h+='<button class="btn btn-primary btn-sm" onclick="simulateDelivery(\\''+t.trace_id+'\\')">🤖 \u6A21\u62DF\u4EA4\u4ED8</button>';
|
|
1410
1533
|
h+='<button class="btn btn-green" onclick="taskResume(\\''+t.trace_id+'\\')">\u63D0\u4EA4\u4EA4\u4ED8 (Resume)</button>';
|
|
1411
1534
|
h+='<button class="btn btn-danger" onclick="taskReject(\\''+t.trace_id+'\\')">\u6253\u56DE\u91CD\u505A (Reject)</button>';
|
|
1412
1535
|
h+='<button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button>';
|
|
@@ -1442,6 +1565,21 @@ window.taskReject=async function(traceId){
|
|
|
1442
1565
|
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
1443
1566
|
};
|
|
1444
1567
|
|
|
1568
|
+
window.simulateDelivery=async function(traceId){
|
|
1569
|
+
const btn=event.target;
|
|
1570
|
+
const origText=btn.innerHTML;
|
|
1571
|
+
btn.disabled=true;btn.innerHTML='⌛ AI \u751F\u6210\u4E2D...';
|
|
1572
|
+
try{
|
|
1573
|
+
const r=await fetch(API+'/tasks/simulate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});
|
|
1574
|
+
const d=await r.json();
|
|
1575
|
+
if(!r.ok){toast(d.error||'\u6A21\u62DF\u5931\u8D25',false);btn.disabled=false;btn.innerHTML=origText;return}
|
|
1576
|
+
const ta=document.getElementById('td-payload');
|
|
1577
|
+
if(ta)ta.value=d.simulated_delivery;
|
|
1578
|
+
toast('\u5DF2\u751F\u6210\u6A21\u62DF\u4EA4\u4ED8\u5185\u5BB9',true);
|
|
1579
|
+
btn.disabled=false;btn.innerHTML=origText;
|
|
1580
|
+
}catch(e){toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);btn.disabled=false;btn.innerHTML=origText}
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1445
1583
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1446
1584
|
// TERMINAL
|
|
1447
1585
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -1449,7 +1587,7 @@ function loadTerminal(el){
|
|
|
1449
1587
|
el.innerHTML='<div class="section-hd"><h2>I/O \u4EA4\u4ED8\u7EC8\u7AEF</h2></div>'
|
|
1450
1588
|
+'<div class="form-card" style="margin-top:12px"><h3>> \u63D0\u4EA4\u78B3\u57FA\u8282\u70B9\u4EA7\u51FA</h3>'
|
|
1451
1589
|
+'<div class="fg"><label>Trace ID (\u8FFD\u8E2A\u7801)</label><input id="t-tid" placeholder="\u4F8B: TK-9527"/></div>'
|
|
1452
|
-
+'<div class="fg"><label>\u4EA4\u4ED8\u8F7D\u8377</label><textarea id="t-payload" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001\u5DE5\u4F5C\u6C47\u62A5\
|
|
1590
|
+
+'<div class="fg"><label>\u4EA4\u4ED8\u8F7D\u8377</label><textarea id="t-payload" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>'
|
|
1453
1591
|
+'<div class="btn-group"><button class="btn btn-primary" onclick="doResume()">\u63D0\u4EA4\u5E76\u6062\u590D (Resume)</button><button class="btn btn-danger" onclick="doReject()">\u6253\u56DE\u91CD\u505A (Reject)</button></div></div>';
|
|
1454
1592
|
}
|
|
1455
1593
|
window.doResume=async function(){
|
|
@@ -1621,10 +1759,20 @@ agentCmd.command("add").description("Register a new carbon-based node").action(a
|
|
|
1621
1759
|
process.exit(0);
|
|
1622
1760
|
}
|
|
1623
1761
|
const capabilities = capInput.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1762
|
+
const relInput = await p.text({
|
|
1763
|
+
message: "Relationship with you (optional):",
|
|
1764
|
+
placeholder: "e.g. direct report / intern / contractor",
|
|
1765
|
+
defaultValue: ""
|
|
1766
|
+
});
|
|
1767
|
+
if (p.isCancel(relInput)) {
|
|
1768
|
+
p.cancel("Cancelled.");
|
|
1769
|
+
process.exit(0);
|
|
1770
|
+
}
|
|
1624
1771
|
const agent = createAgent({
|
|
1625
1772
|
agent_id: generateId("emp"),
|
|
1626
1773
|
name,
|
|
1627
1774
|
capabilities,
|
|
1775
|
+
relationship: relInput || "",
|
|
1628
1776
|
status: "IDLE"
|
|
1629
1777
|
});
|
|
1630
1778
|
p.outro(
|