lyu-extension-workflow 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Workflow Extension for lyu-code
3
+ *
4
+ * /workflow — 打开本地前端页面,可视化编排多 agent 工作流
5
+ *
6
+ * 架构:
7
+ * - 命令启动本地 HTTP 服务(默认 18789)+ WebSocket
8
+ * - serve frontend/dist/ 下的静态文件(用户自行提供)
9
+ * - WebSocket 通道让前端和 Pi 通信
10
+ * - 前端发指令(addNode, connect, run, stop, ...)
11
+ * - 扩展执行操作,回传状态和结果
12
+ *
13
+ * 用法:
14
+ * /workflow — 打开编辑器
15
+ * /workflow stop — 关闭服务
16
+ */
17
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
+ export default function (pi: ExtensionAPI): void;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,iCAAiC,CAAC;AAkbzC,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,QAoDxC"}
package/dist/index.js ADDED
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Workflow Extension for lyu-code
3
+ *
4
+ * /workflow — 打开本地前端页面,可视化编排多 agent 工作流
5
+ *
6
+ * 架构:
7
+ * - 命令启动本地 HTTP 服务(默认 18789)+ WebSocket
8
+ * - serve frontend/dist/ 下的静态文件(用户自行提供)
9
+ * - WebSocket 通道让前端和 Pi 通信
10
+ * - 前端发指令(addNode, connect, run, stop, ...)
11
+ * - 扩展执行操作,回传状态和结果
12
+ *
13
+ * 用法:
14
+ * /workflow — 打开编辑器
15
+ * /workflow stop — 关闭服务
16
+ */
17
+ import * as http from "node:http";
18
+ import * as fs from "node:fs";
19
+ import * as path from "node:path";
20
+ import { URL } from "node:url";
21
+ const DEFAULT_PORT = 18789;
22
+ // ─── 简易 WebSocket (RFC 6455, 最小实现) ──────────────────────────
23
+ function wsAcceptKey(key) {
24
+ const crypto = require("node:crypto");
25
+ return crypto.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-5AB5DC828B12").digest("base64");
26
+ }
27
+ // ─── 全局状态 ──────────────────────────────────────────────────────
28
+ let server = null;
29
+ let port = DEFAULT_PORT;
30
+ let piRef = null;
31
+ const workflows = new Map();
32
+ const runStates = new Map();
33
+ const wsClients = [];
34
+ // ─── 广播 ──────────────────────────────────────────────────────────
35
+ function broadcast(type, data) {
36
+ const msg = JSON.stringify({ type, data });
37
+ for (const c of wsClients) {
38
+ try {
39
+ c.send(msg);
40
+ }
41
+ catch { /* ignore */ }
42
+ }
43
+ }
44
+ // ─── 前端静态文件 ──────────────────────────────────────────────────
45
+ const MIME = {
46
+ ".html": "text/html", ".js": "application/javascript", ".mjs": "application/javascript",
47
+ ".css": "text/css", ".json": "application/json", ".png": "image/png",
48
+ ".jpg": "image/jpeg", ".svg": "image/svg+xml", ".ico": "image/x-icon",
49
+ ".woff": "font/woff", ".woff2": "font/woff2",
50
+ };
51
+ function getFrontendDir() {
52
+ const thisDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
53
+ return path.join(thisDir, "..", "frontend", "dist");
54
+ }
55
+ function serveStatic(req, res) {
56
+ const frontendDir = getFrontendDir();
57
+ let urlPath = new URL(req.url ?? "/", "http://localhost").pathname;
58
+ // API 路由
59
+ if (urlPath.startsWith("/api/")) {
60
+ handleApi(req, res, urlPath);
61
+ return;
62
+ }
63
+ // 静态文件
64
+ if (urlPath === "/")
65
+ urlPath = "/index.html";
66
+ const filePath = path.join(frontendDir, urlPath);
67
+ try {
68
+ if (!filePath.startsWith(frontendDir)) {
69
+ res.writeHead(403);
70
+ res.end();
71
+ return;
72
+ }
73
+ const stat = fs.statSync(filePath);
74
+ if (!stat.isFile())
75
+ throw new Error("not file");
76
+ const ext = path.extname(filePath);
77
+ res.writeHead(200, { "Content-Type": (MIME[ext] || "application/octet-stream") + "; charset=utf-8" });
78
+ res.end(fs.readFileSync(filePath));
79
+ }
80
+ catch {
81
+ // SPA fallback
82
+ try {
83
+ const fallback = path.join(frontendDir, "index.html");
84
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
85
+ res.end(fs.readFileSync(fallback));
86
+ }
87
+ catch {
88
+ res.writeHead(404);
89
+ res.end("Not found — put frontend files in frontend/dist/");
90
+ }
91
+ }
92
+ }
93
+ function handleApi(_req, res, urlPath) {
94
+ const ok = (d) => { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(d)); };
95
+ if (urlPath === "/api/workflows") {
96
+ ok([...workflows.values()].map(w => ({ id: w.id, name: w.name, nodeCount: w.nodes.length, globalMaxLoops: w.globalMaxLoops })));
97
+ }
98
+ else if (urlPath === "/api/status") {
99
+ ok({ ok: true, workflows: workflows.size, clients: wsClients.length });
100
+ }
101
+ else {
102
+ ok({ error: "not found" });
103
+ }
104
+ }
105
+ // ─── WebSocket 消息处理 ────────────────────────────────────────────
106
+ function handleWsMessage(raw) {
107
+ let msg;
108
+ try {
109
+ msg = JSON.parse(raw);
110
+ }
111
+ catch {
112
+ return;
113
+ }
114
+ switch (msg.type) {
115
+ case "workflow:create": {
116
+ const wf = {
117
+ id: msg.data?.id ?? `wf-${Date.now()}`,
118
+ name: msg.data?.name ?? "Untitled Workflow",
119
+ nodes: msg.data?.nodes ?? [],
120
+ edges: msg.data?.edges ?? [],
121
+ globalMaxLoops: msg.data?.globalMaxLoops ?? 10,
122
+ createdAt: Date.now(),
123
+ updatedAt: Date.now(),
124
+ };
125
+ workflows.set(wf.id, wf);
126
+ runStates.set(wf.id, { status: "idle", iteration: 0, results: {} });
127
+ broadcast("workflow:created", wf);
128
+ break;
129
+ }
130
+ case "workflow:update": {
131
+ const wf = workflows.get(msg.data?.id);
132
+ if (wf) {
133
+ Object.assign(wf, msg.data, { updatedAt: Date.now() });
134
+ broadcast("workflow:updated", wf);
135
+ }
136
+ break;
137
+ }
138
+ case "workflow:delete": {
139
+ workflows.delete(msg.data?.id);
140
+ runStates.delete(msg.data?.id);
141
+ broadcast("workflow:deleted", { id: msg.data?.id });
142
+ break;
143
+ }
144
+ case "node:add": {
145
+ const wf = workflows.get(msg.data?.workflowId);
146
+ if (wf) {
147
+ wf.nodes.push(msg.data.node);
148
+ wf.updatedAt = Date.now();
149
+ broadcast("workflow:updated", wf);
150
+ }
151
+ break;
152
+ }
153
+ case "node:update": {
154
+ const wf = workflows.get(msg.data?.workflowId);
155
+ if (wf) {
156
+ const i = wf.nodes.findIndex(n => n.id === msg.data.node.id);
157
+ if (i >= 0) {
158
+ wf.nodes[i] = { ...wf.nodes[i], ...msg.data.node };
159
+ wf.updatedAt = Date.now();
160
+ broadcast("workflow:updated", wf);
161
+ }
162
+ }
163
+ break;
164
+ }
165
+ case "node:remove": {
166
+ const wf = workflows.get(msg.data?.workflowId);
167
+ if (wf) {
168
+ wf.nodes = wf.nodes.filter(n => n.id !== msg.data.nodeId);
169
+ wf.edges = wf.edges.filter(e => e.source !== msg.data.nodeId && e.target !== msg.data.nodeId);
170
+ wf.updatedAt = Date.now();
171
+ broadcast("workflow:updated", wf);
172
+ }
173
+ break;
174
+ }
175
+ case "edge:add": {
176
+ const wf = workflows.get(msg.data?.workflowId);
177
+ if (wf) {
178
+ wf.edges.push(msg.data.edge);
179
+ wf.updatedAt = Date.now();
180
+ broadcast("workflow:updated", wf);
181
+ }
182
+ break;
183
+ }
184
+ case "edge:remove": {
185
+ const wf = workflows.get(msg.data?.workflowId);
186
+ if (wf) {
187
+ wf.edges = wf.edges.filter(e => e.id !== msg.data.edgeId);
188
+ wf.updatedAt = Date.now();
189
+ broadcast("workflow:updated", wf);
190
+ }
191
+ break;
192
+ }
193
+ case "workflow:run": {
194
+ const wfId = msg.data?.workflowId;
195
+ const wf = workflows.get(wfId);
196
+ const state = runStates.get(wfId);
197
+ if (!wf || !state || state.status === "running")
198
+ break;
199
+ state.status = "running";
200
+ state.iteration = 0;
201
+ state.results = {};
202
+ for (const n of wf.nodes)
203
+ state.results[n.id] = { status: "pending" };
204
+ broadcast("run:state", { workflowId: wfId, state });
205
+ runWorkflow(wf, state).catch(err => {
206
+ state.status = "error";
207
+ broadcast("run:state", { workflowId: wfId, state, error: String(err) });
208
+ });
209
+ break;
210
+ }
211
+ case "workflow:stop": {
212
+ const state = runStates.get(msg.data?.workflowId);
213
+ if (state) {
214
+ state.status = "idle";
215
+ broadcast("run:state", { workflowId: msg.data?.workflowId, state });
216
+ }
217
+ break;
218
+ }
219
+ case "agent:send": {
220
+ if (piRef) {
221
+ piRef.sendUserMessage(msg.data?.message ?? "", { deliverAs: "followUp" });
222
+ }
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ // ─── Workflow 执行引擎 ─────────────────────────────────────────────
228
+ async function runWorkflow(wf, state) {
229
+ const sorted = topoSort(wf.nodes, wf.edges);
230
+ for (let iter = 0; iter < wf.globalMaxLoops; iter++) {
231
+ state.iteration = iter + 1;
232
+ broadcast("run:state", { workflowId: wf.id, state });
233
+ for (const node of sorted) {
234
+ const ns = state.results[node.id];
235
+ if (!ns)
236
+ continue;
237
+ ns.status = "running";
238
+ state.currentStep = node.id;
239
+ broadcast("run:state", { workflowId: wf.id, state });
240
+ // 收集上游输出
241
+ const upstream = wf.edges
242
+ .filter(e => e.target === node.id)
243
+ .map(e => state.results[e.source]?.output ?? "")
244
+ .filter(Boolean)
245
+ .join("\n\n");
246
+ // 通过 Pi 驱动 agent
247
+ if (piRef) {
248
+ const prompt = upstream
249
+ ? `[上游输入]\n${upstream}\n\n[请处理以上输入并输出结果]`
250
+ : node.config.systemPrompt ?? "请开始工作";
251
+ piRef.sendUserMessage(prompt, { deliverAs: "followUp" });
252
+ }
253
+ // 等待一小段时间让 Pi 处理(实际结果由 agent_end 事件回填)
254
+ await new Promise(r => setTimeout(r, 500));
255
+ ns.status = "done";
256
+ ns.iterations = (ns.iterations ?? 0) + 1;
257
+ broadcast("run:state", { workflowId: wf.id, state });
258
+ }
259
+ // 无回边则不循环
260
+ const hasBack = wf.edges.some(e => {
261
+ const si = sorted.findIndex(n => n.id === e.source);
262
+ const ti = sorted.findIndex(n => n.id === e.target);
263
+ return ti <= si;
264
+ });
265
+ if (!hasBack)
266
+ break;
267
+ }
268
+ state.status = "done";
269
+ state.currentStep = undefined;
270
+ broadcast("run:state", { workflowId: wf.id, state });
271
+ }
272
+ function topoSort(nodes, edges) {
273
+ const nodeMap = new Map(nodes.map(n => [n.id, n]));
274
+ const deg = new Map(nodes.map(n => [n.id, 0]));
275
+ const adj = new Map(nodes.map(n => [n.id, []]));
276
+ for (const e of edges) {
277
+ adj.get(e.source)?.push(e.target);
278
+ deg.set(e.target, (deg.get(e.target) ?? 0) + 1);
279
+ }
280
+ const q = [];
281
+ for (const [id, d] of deg)
282
+ if (d === 0)
283
+ q.push(id);
284
+ const result = [];
285
+ while (q.length) {
286
+ const id = q.shift();
287
+ const n = nodeMap.get(id);
288
+ if (n)
289
+ result.push(n);
290
+ for (const next of adj.get(id) ?? []) {
291
+ const d = (deg.get(next) ?? 1) - 1;
292
+ deg.set(next, d);
293
+ if (d === 0)
294
+ q.push(next);
295
+ }
296
+ }
297
+ for (const n of nodes)
298
+ if (!result.find(r => r.id === n.id))
299
+ result.push(n);
300
+ return result;
301
+ }
302
+ // ─── 启停 ──────────────────────────────────────────────────────────
303
+ function startServer() {
304
+ return new Promise((resolve, reject) => {
305
+ server = http.createServer(serveStatic);
306
+ // WebSocket upgrade (简易实现)
307
+ server.on("upgrade", (req, socket, head) => {
308
+ if (!(req.url ?? "/").startsWith("/ws")) {
309
+ socket.destroy();
310
+ return;
311
+ }
312
+ const key = req.headers["sec-websocket-key"];
313
+ if (!key) {
314
+ socket.destroy();
315
+ return;
316
+ }
317
+ socket.write("HTTP/1.1 101 Switching Protocols\r\n" +
318
+ "Upgrade: websocket\r\n" +
319
+ "Connection: Upgrade\r\n" +
320
+ `Sec-WebSocket-Accept: ${wsAcceptKey(key)}\r\n\r\n`);
321
+ const client = {
322
+ socket,
323
+ send(data) {
324
+ // 简易:发 text frame (opcode 0x1), 无掩码, 不分片
325
+ const buf = Buffer.from(data, "utf-8");
326
+ const len = buf.length;
327
+ const frames = [];
328
+ if (len < 126) {
329
+ frames.push(Buffer.from([0x81, len]));
330
+ }
331
+ else if (len < 65536) {
332
+ const h = Buffer.alloc(4);
333
+ h[0] = 0x81;
334
+ h[1] = 126;
335
+ h.writeUInt16BE(len, 2);
336
+ frames.push(h);
337
+ }
338
+ else {
339
+ const h = Buffer.alloc(10);
340
+ h[0] = 0x81;
341
+ h[1] = 127;
342
+ h.writeBigUInt64BE(BigInt(len), 2);
343
+ frames.push(h);
344
+ }
345
+ frames.push(buf);
346
+ socket.write(Buffer.concat(frames));
347
+ },
348
+ };
349
+ wsClients.push(client);
350
+ // 接收帧(简易:只处理小 text 帧)
351
+ let buf = Buffer.alloc(0);
352
+ socket.on("data", (chunk) => {
353
+ buf = Buffer.concat([buf, chunk]);
354
+ // 解析 text frame
355
+ if (buf.length < 2)
356
+ return;
357
+ const opcode = buf[0] & 0x0f;
358
+ const masked = !!(buf[1] & 0x80);
359
+ let payloadLen = buf[1] & 0x7f;
360
+ let offset = 2;
361
+ if (payloadLen === 126) {
362
+ if (buf.length < 4)
363
+ return;
364
+ payloadLen = buf.readUInt16BE(2);
365
+ offset = 4;
366
+ }
367
+ else if (payloadLen === 127) {
368
+ if (buf.length < 10)
369
+ return;
370
+ payloadLen = Number(buf.readBigUInt64BE(2));
371
+ offset = 10;
372
+ }
373
+ if (masked)
374
+ offset += 4;
375
+ if (buf.length < offset + payloadLen)
376
+ return;
377
+ let payload = buf.subarray(offset, offset + payloadLen);
378
+ if (masked) {
379
+ const mask = buf.subarray(offset - 4, offset);
380
+ payload = Buffer.from(payload.map((b, i) => b ^ mask[i % 4]));
381
+ }
382
+ buf = buf.subarray(offset + payloadLen);
383
+ if (opcode === 0x1) { // text
384
+ handleWsMessage(payload.toString("utf-8"));
385
+ }
386
+ else if (opcode === 0x8) { // close
387
+ const idx = wsClients.indexOf(client);
388
+ if (idx >= 0)
389
+ wsClients.splice(idx, 1);
390
+ }
391
+ });
392
+ socket.on("close", () => {
393
+ const idx = wsClients.indexOf(client);
394
+ if (idx >= 0)
395
+ wsClients.splice(idx, 1);
396
+ });
397
+ // 发送初始状态
398
+ client.send(JSON.stringify({
399
+ type: "connected",
400
+ data: { workflows: [...workflows.values()], port },
401
+ }));
402
+ });
403
+ server.on("error", (err) => {
404
+ if (err.code === "EADDRINUSE") {
405
+ port++;
406
+ server.listen(port, "127.0.0.1");
407
+ }
408
+ else
409
+ reject(err);
410
+ });
411
+ server.on("listening", () => resolve(port));
412
+ server.listen(port, "127.0.0.1");
413
+ });
414
+ }
415
+ function stopServer() {
416
+ for (const c of wsClients)
417
+ try {
418
+ c.socket.destroy();
419
+ }
420
+ catch { /* ok */ }
421
+ wsClients.length = 0;
422
+ server?.close();
423
+ server = null;
424
+ }
425
+ // ─── 扩展入口 ──────────────────────────────────────────────────────
426
+ export default function (pi) {
427
+ piRef = pi;
428
+ pi.registerCommand("workflow", {
429
+ description: "Open workflow editor — visually compose multi-agent pipelines",
430
+ handler: async (args, _ctx) => {
431
+ if (args.trim() === "stop") {
432
+ stopServer();
433
+ pi.sendMessage({ customType: "workflow", content: "Workflow server stopped", display: true });
434
+ return;
435
+ }
436
+ if (!server) {
437
+ const actualPort = await startServer();
438
+ const url = `http://127.0.0.1:${actualPort}`;
439
+ // 打开浏览器
440
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
441
+ try {
442
+ require("node:child_process").execSync(`${cmd} "${url}"`, { stdio: "ignore" });
443
+ }
444
+ catch { /* ok */ }
445
+ pi.sendMessage({
446
+ customType: "workflow",
447
+ content: `Workflow editor: ${url}`,
448
+ display: true,
449
+ details: { url, port: actualPort },
450
+ });
451
+ }
452
+ else {
453
+ const url = `http://127.0.0.1:${port}`;
454
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
455
+ try {
456
+ require("node:child_process").execSync(`${cmd} "${url}"`, { stdio: "ignore" });
457
+ }
458
+ catch { /* ok */ }
459
+ }
460
+ },
461
+ });
462
+ // agent_end 事件回填节点结果
463
+ pi.on("agent_end", (event) => {
464
+ for (const [wfId, state] of runStates) {
465
+ if (state.status !== "running" || !state.currentStep)
466
+ continue;
467
+ const lastMsg = event.messages[event.messages.length - 1];
468
+ if (lastMsg?.role === "assistant") {
469
+ const content = lastMsg.content;
470
+ const text = Array.isArray(content)
471
+ ? content.filter((b) => b.type === "text").map((b) => b.text).join("\n")
472
+ : typeof content === "string" ? content : "";
473
+ const ns = state.results[state.currentStep];
474
+ if (ns)
475
+ ns.output = text;
476
+ broadcast("run:nodeResult", { workflowId: wfId, nodeId: state.currentStep, output: text });
477
+ }
478
+ }
479
+ });
480
+ console.error("[workflow] Extension loaded — /workflow to open editor");
481
+ }
482
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,686 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Lyu-Code Workflow</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
9
+ <style>
10
+ /* ===========================================
11
+ CSS CUSTOM PROPERTIES (NEON CYBER THEME)
12
+ =========================================== */
13
+ :root {
14
+ --bg-primary: #0a0f1c;
15
+ --bg-secondary: #111827;
16
+ --bg-tertiary: #1a2332;
17
+ --text-primary: #e2e8f0;
18
+ --text-secondary: #94a3b8;
19
+ --text-muted: #64748b;
20
+ --accent: #22d3ee;
21
+ --accent-glow: rgba(34, 211, 238, 0.3);
22
+ --accent2: #a78bfa;
23
+ --accent2-glow: rgba(167, 139, 250, 0.3);
24
+ --success: #4ade80;
25
+ --warning: #fbbf24;
26
+ --danger: #f87171;
27
+ --border: rgba(148, 163, 184, 0.12);
28
+ --border-active: rgba(34, 211, 238, 0.4);
29
+ --font-display: "Space Grotesk", sans-serif;
30
+ --font-mono: "JetBrains Mono", monospace;
31
+ --radius: 10px;
32
+ --radius-sm: 6px;
33
+ --shadow: 0 4px 24px rgba(0,0,0,0.4);
34
+ --shadow-glow: 0 0 20px var(--accent-glow);
35
+ }
36
+
37
+ * { margin: 0; padding: 0; box-sizing: border-box; }
38
+
39
+ html, body {
40
+ height: 100%;
41
+ overflow: hidden;
42
+ font-family: var(--font-display);
43
+ background: var(--bg-primary);
44
+ color: var(--text-primary);
45
+ }
46
+
47
+ /* ===========================================
48
+ LAYOUT
49
+ =========================================== */
50
+ #app {
51
+ display: grid;
52
+ grid-template-rows: 48px 1fr;
53
+ grid-template-columns: 240px 1fr 280px;
54
+ height: 100vh;
55
+ }
56
+
57
+ /* ─── Header ──────────────────────────────── */
58
+ header {
59
+ grid-column: 1 / -1;
60
+ background: var(--bg-secondary);
61
+ border-bottom: 1px solid var(--border);
62
+ display: flex;
63
+ align-items: center;
64
+ padding: 0 16px;
65
+ gap: 12px;
66
+ }
67
+
68
+ header .logo {
69
+ font-weight: 700;
70
+ font-size: 14px;
71
+ letter-spacing: -0.3px;
72
+ background: linear-gradient(135deg, var(--accent), var(--accent2));
73
+ -webkit-background-clip: text;
74
+ -webkit-text-fill-color: transparent;
75
+ }
76
+
77
+ header .wf-name {
78
+ font-size: 13px;
79
+ color: var(--text-secondary);
80
+ border: 1px solid var(--border);
81
+ border-radius: var(--radius-sm);
82
+ padding: 3px 8px;
83
+ background: transparent;
84
+ outline: none;
85
+ font-family: var(--font-display);
86
+ width: 180px;
87
+ }
88
+ header .wf-name:focus { border-color: var(--accent); }
89
+
90
+ .toolbar {
91
+ display: flex;
92
+ gap: 4px;
93
+ margin-left: auto;
94
+ }
95
+
96
+ .btn {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ gap: 5px;
100
+ padding: 5px 10px;
101
+ border: 1px solid var(--border);
102
+ border-radius: var(--radius-sm);
103
+ background: transparent;
104
+ color: var(--text-secondary);
105
+ font-size: 12px;
106
+ font-family: var(--font-display);
107
+ cursor: pointer;
108
+ transition: all 0.15s;
109
+ }
110
+ .btn:hover { background: var(--bg-tertiary); color: var(--text-primary); border-color: var(--text-muted); }
111
+ .btn.primary { background: rgba(34,211,238,0.12); color: var(--accent); border-color: var(--accent); }
112
+ .btn.primary:hover { background: rgba(34,211,238,0.22); box-shadow: var(--shadow-glow); }
113
+ .btn.danger { color: var(--danger); border-color: rgba(248,113,113,0.3); }
114
+ .btn.danger:hover { background: rgba(248,113,113,0.1); }
115
+
116
+ .status-pill {
117
+ font-size: 11px;
118
+ padding: 2px 8px;
119
+ border-radius: 99px;
120
+ font-family: var(--font-mono);
121
+ }
122
+ .status-pill.idle { background: rgba(100,116,139,0.15); color: var(--text-muted); }
123
+ .status-pill.running { background: rgba(74,222,128,0.15); color: var(--success); animation: pulse 1.5s infinite; }
124
+ .status-pill.done { background: rgba(34,211,238,0.15); color: var(--accent); }
125
+ .status-pill.error { background: rgba(248,113,113,0.15); color: var(--danger); }
126
+
127
+ @keyframes pulse { 0%,100%{ opacity:1 } 50%{ opacity:0.6 } }
128
+
129
+ /* ─── Sidebar (Node Palette) ──────────────── */
130
+ aside {
131
+ background: var(--bg-secondary);
132
+ border-right: 1px solid var(--border);
133
+ padding: 12px;
134
+ overflow-y: auto;
135
+ }
136
+
137
+ aside h3 {
138
+ font-size: 11px;
139
+ text-transform: uppercase;
140
+ letter-spacing: 1px;
141
+ color: var(--text-muted);
142
+ margin-bottom: 10px;
143
+ }
144
+
145
+ .palette-item {
146
+ display: flex;
147
+ align-items: center;
148
+ gap: 8px;
149
+ padding: 8px 10px;
150
+ margin-bottom: 4px;
151
+ border-radius: var(--radius-sm);
152
+ border: 1px solid var(--border);
153
+ cursor: grab;
154
+ transition: all 0.15s;
155
+ font-size: 13px;
156
+ }
157
+ .palette-item:hover {
158
+ border-color: var(--accent);
159
+ background: rgba(34,211,238,0.06);
160
+ }
161
+ .palette-item .icon {
162
+ width: 28px;
163
+ height: 28px;
164
+ border-radius: 6px;
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ font-size: 14px;
169
+ }
170
+ .palette-item.agent .icon { background: rgba(34,211,238,0.15); }
171
+
172
+ .config-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
173
+ .config-section label {
174
+ display: block;
175
+ font-size: 11px;
176
+ color: var(--text-muted);
177
+ margin-bottom: 4px;
178
+ margin-top: 8px;
179
+ text-transform: uppercase;
180
+ letter-spacing: 0.5px;
181
+ }
182
+ .config-section input, .config-section textarea, .config-section select {
183
+ width: 100%;
184
+ padding: 5px 8px;
185
+ background: var(--bg-primary);
186
+ border: 1px solid var(--border);
187
+ border-radius: var(--radius-sm);
188
+ color: var(--text-primary);
189
+ font-size: 12px;
190
+ font-family: var(--font-display);
191
+ outline: none;
192
+ }
193
+ .config-section input:focus, .config-section textarea:focus, .config-section select:focus {
194
+ border-color: var(--accent);
195
+ }
196
+ .config-section textarea { min-height: 60px; resize: vertical; font-family: var(--font-mono); font-size: 11px; }
197
+
198
+ /* ─── Canvas ──────────────────────────────── */
199
+ .canvas-wrap {
200
+ position: relative;
201
+ overflow: hidden;
202
+ background:
203
+ radial-gradient(ellipse at 30% 70%, rgba(34,211,238,0.04) 0%, transparent 50%),
204
+ radial-gradient(ellipse at 70% 30%, rgba(167,139,250,0.04) 0%, transparent 50%),
205
+ var(--bg-primary);
206
+ }
207
+
208
+ /* Grid pattern */
209
+ .canvas-wrap::before {
210
+ content: '';
211
+ position: absolute;
212
+ inset: 0;
213
+ background-image:
214
+ linear-gradient(rgba(148,163,184,0.04) 1px, transparent 1px),
215
+ linear-gradient(90deg, rgba(148,163,184,0.04) 1px, transparent 1px);
216
+ background-size: 40px 40px;
217
+ pointer-events: none;
218
+ }
219
+
220
+ #canvas {
221
+ position: absolute;
222
+ inset: 0;
223
+ }
224
+
225
+ /* SVG arrows */
226
+ .canvas-wrap svg {
227
+ position: absolute;
228
+ inset: 0;
229
+ width: 100%;
230
+ height: 100%;
231
+ pointer-events: none;
232
+ }
233
+ .canvas-wrap svg path {
234
+ fill: none;
235
+ stroke: var(--accent);
236
+ stroke-width: 2;
237
+ stroke-dasharray: 8 4;
238
+ opacity: 0.5;
239
+ filter: drop-shadow(0 0 4px var(--accent-glow));
240
+ }
241
+ .canvas-wrap svg path.active {
242
+ stroke: var(--success);
243
+ opacity: 1;
244
+ stroke-dasharray: none;
245
+ }
246
+
247
+ /* ─── Nodes on canvas ─────────────────────── */
248
+ .node {
249
+ position: absolute;
250
+ width: 180px;
251
+ background: var(--bg-secondary);
252
+ border: 1px solid var(--border);
253
+ border-radius: var(--radius);
254
+ box-shadow: var(--shadow);
255
+ cursor: move;
256
+ user-select: none;
257
+ transition: box-shadow 0.15s, border-color 0.15s;
258
+ }
259
+ .node:hover { border-color: var(--text-muted); }
260
+ .node.selected { border-color: var(--accent); box-shadow: var(--shadow), var(--shadow-glow); }
261
+ .node.running { border-color: var(--success); animation: pulse 1.5s infinite; }
262
+ .node.done { border-color: rgba(74,222,128,0.4); }
263
+
264
+ .node-header {
265
+ display: flex;
266
+ align-items: center;
267
+ gap: 6px;
268
+ padding: 8px 10px;
269
+ border-bottom: 1px solid var(--border);
270
+ font-size: 12px;
271
+ font-weight: 600;
272
+ }
273
+ .node-header .dot {
274
+ width: 8px;
275
+ height: 8px;
276
+ border-radius: 50%;
277
+ background: var(--accent);
278
+ }
279
+ .node.running .node-header .dot { background: var(--success); }
280
+ .node.done .node-header .dot { background: var(--success); opacity: 0.5; }
281
+
282
+ .node-body {
283
+ padding: 8px 10px;
284
+ font-size: 11px;
285
+ color: var(--text-muted);
286
+ }
287
+ .node-body .model-tag {
288
+ display: inline-block;
289
+ padding: 1px 6px;
290
+ background: rgba(167,139,250,0.12);
291
+ color: var(--accent2);
292
+ border-radius: 4px;
293
+ font-family: var(--font-mono);
294
+ font-size: 10px;
295
+ }
296
+
297
+ /* Connection ports */
298
+ .port {
299
+ width: 10px;
300
+ height: 10px;
301
+ border-radius: 50%;
302
+ background: var(--bg-tertiary);
303
+ border: 2px solid var(--text-muted);
304
+ position: absolute;
305
+ cursor: crosshair;
306
+ transition: all 0.15s;
307
+ }
308
+ .port:hover { border-color: var(--accent); background: var(--accent-glow); transform: scale(1.3); }
309
+ .port.in { left: -5px; top: 50%; margin-top: -5px; }
310
+ .port.out { right: -5px; top: 50%; margin-top: -5px; }
311
+
312
+ /* ─── Right Panel (Run & Debug) ───────────── */
313
+ .panel {
314
+ background: var(--bg-secondary);
315
+ border-left: 1px solid var(--border);
316
+ padding: 12px;
317
+ overflow-y: auto;
318
+ font-size: 12px;
319
+ }
320
+
321
+ .panel h3 {
322
+ font-size: 11px;
323
+ text-transform: uppercase;
324
+ letter-spacing: 1px;
325
+ color: var(--text-muted);
326
+ margin-bottom: 10px;
327
+ }
328
+
329
+ .run-log {
330
+ font-family: var(--font-mono);
331
+ font-size: 11px;
332
+ line-height: 1.6;
333
+ }
334
+ .run-log .entry {
335
+ padding: 4px 6px;
336
+ margin-bottom: 2px;
337
+ border-radius: 4px;
338
+ background: var(--bg-primary);
339
+ }
340
+ .run-log .entry.error { color: var(--danger); }
341
+ .run-log .entry.success { color: var(--success); }
342
+
343
+ .iter-badge {
344
+ display: inline-block;
345
+ padding: 1px 6px;
346
+ background: rgba(251,191,36,0.12);
347
+ color: var(--warning);
348
+ border-radius: 4px;
349
+ font-family: var(--font-mono);
350
+ font-size: 10px;
351
+ }
352
+
353
+ /* ─── Empty State ─────────────────────────── */
354
+ .empty-state {
355
+ position: absolute;
356
+ inset: 0;
357
+ display: flex;
358
+ flex-direction: column;
359
+ align-items: center;
360
+ justify-content: center;
361
+ color: var(--text-muted);
362
+ gap: 8px;
363
+ }
364
+ .empty-state .big-icon { font-size: 48px; opacity: 0.3; }
365
+ .empty-state p { font-size: 14px; }
366
+ .empty-state .hint { font-size: 11px; color: var(--text-muted); opacity: 0.6; }
367
+
368
+ /* ─── Connection line being drawn ─────────── */
369
+ .temp-line {
370
+ stroke: var(--accent);
371
+ stroke-width: 2;
372
+ stroke-dasharray: 4 4;
373
+ opacity: 0.6;
374
+ }
375
+ </style>
376
+ </head>
377
+ <body>
378
+ <div id="app">
379
+ <!-- Header -->
380
+ <header>
381
+ <span class="logo">⚡ lyu-code</span>
382
+ <span style="color:var(--text-muted);font-size:12px">/</span>
383
+ <input class="wf-name" id="wfName" value="Untitled Workflow" placeholder="Workflow name" />
384
+ <span class="status-pill idle" id="statusP#ill">IDLE</span>
385
+ <div class="toolbar">
386
+ <button class="btn" onclick="addNode('agent')">+ Agent</button>
387
+ <button class="btn primary" id="runBtn" onclick="runWorkflow()">▶ Run</button>
388
+ <button class="btn" onclick="stopWorkflow()">■ Stop</button>
389
+ <button class="btn danger" onclick="clearAll()">✕ Clear</button>
390
+ </div>
391
+ </header>
392
+
393
+ <!-- Left Sidebar: Palette & Config -->
394
+ <aside>
395
+ <h3>Nodes</h3>
396
+ <div class="palette-item agent" onclick="addNode('agent')">
397
+ <div class="icon">🤖</div>
398
+ <span>Agent</span>
399
+ </div>
400
+
401
+ <div class="config-section" id="nodeConfig" style="display:none">
402
+ <h3>Selected Node</h3>
403
+ <label>Label</label>
404
+ <input type="text" id="cfgLabel" oninput="updateSelectedNode()" />
405
+ <label>Model</label>
406
+ <select id="cfgModel" onchange="updateSelectedNode()">
407
+ <option value="">default</option>
408
+ <option value="gpt-6-sol">GPT-6 Sol</option>
409
+ <option value="gpt-5.6-luna">GPT-5.6 Luna</option>
410
+ <option value="deepseek-v4-pro">DeepSeek V4 Pro</option>
411
+ <option value="glm-5.3">GLM 5.3</option>
412
+ </select>
413
+ <label>System Prompt</label>
414
+ <textarea id="cfgPrompt" oninput="updateSelectedNode()" placeholder="You are a helpful agent..."></textarea>
415
+ <label>Max Loops</label>
416
+ <input type="number" id="cfgMaxLoops" value="1" min="1" max="100" oninput="updateSelectedNode()" />
417
+ <label style="margin-top:12px">Global Max Loops</label>
418
+ <input type="number" id="cfgGlobalLoops" value="10" min="1" max="100" />
419
+ <div style="margin-top:12px">
420
+ <button class="btn danger" onclick="deleteSelectedNode()" style="width:100%">Delete Node</button>
421
+ </div>
422
+ </div>
423
+ </aside>
424
+
425
+ <!-- Canvas -->
426
+ <div class="canvas-wrap">
427
+ <div class="empty-state" id="emptyState">
428
+ <div class="big-icon">⚡</div>
429
+ <p>Drag an Agent node to start</p>
430
+ <span class="hint">or click "+ Agent" in the toolbar</span>
431
+ </div>
432
+ <div id="canvas"></div>
433
+ <svg id="arrows"></svg>
434
+ </div>
435
+
436
+ <!-- Right Panel: Run Log -->
437
+ <div class="panel">
438
+ <h3>Run Log</h3>
439
+ <div style="margin-bottom:8px">
440
+ <span style="color:var(--text-muted);font-size:11px">Iteration:</span>
441
+ <span class="iter-badge" id="iterBadge">0</span>
442
+ </div>
443
+ <div class="run-log" id="runLog">
444
+ <div class="entry" style="color:var(--text-muted)">Ready.</div>
445
+ </div>
446
+ </div>
447
+ </div>
448
+
449
+ <script>
450
+ // ─── State ──────────────────────────────────────────
451
+ let ws = null;
452
+ let nodes = [];
453
+ let edges = [];
454
+ let selectedNodeId = null;
455
+ let dragging = null; // { nodeId, offsetX, offsetY }
456
+ let connecting = null; // { fromNodeId, startX, startY }
457
+ let nextId = 1;
458
+
459
+ // ─── WebSocket ──────────────────────────────────────
460
+ function connectWS() {
461
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
462
+ ws = new WebSocket(`${proto}//${location.host}/ws`);
463
+ ws.onopen = () => log('Connected to workflow server', 'success');
464
+ ws.onclose = () => { log('Disconnected, reconnecting…', 'error'); setTimeout(connectWS, 2000); };
465
+ ws.onmessage = (e) => handleMsg(JSON.parse(e.data));
466
+ }
467
+ function send(type, data = {}) {
468
+ if (!ws || ws.readyState !== 1) return;
469
+ ws.send(JSON.stringify({ type, data, id: String(++nextId) }));
470
+ }
471
+ function handleMsg(msg) {
472
+ switch (msg.type) {
473
+ case 'workflow:updated':
474
+ nodes = msg.data.nodes || nodes;
475
+ edges = msg.data.edges || edges;
476
+ render();
477
+ break;
478
+ case 'run:state':
479
+ updateRunState(msg.data);
480
+ break;
481
+ case 'run:nodeResult':
482
+ log(`✓ ${msg.data.nodeId}: ${String(msg.data.output).slice(0, 80)}…`, 'success');
483
+ break;
484
+ }
485
+ }
486
+
487
+ // ─── Node operations ────────────────────────────────
488
+ function addNode(type) {
489
+ const id = `n${Date.now()}`;
490
+ const cx = 200 + Math.random() * 300;
491
+ const cy = 100 + Math.random() * 200;
492
+ const node = {
493
+ id, type, label: `Agent ${nodes.length + 1}`,
494
+ config: { systemPrompt: '', model: '', maxLoops: 1 },
495
+ position: { x: cx, y: cy },
496
+ };
497
+ nodes.push(node);
498
+ send('node:add', { workflowId: currentWfId(), node });
499
+ selectedNodeId = id;
500
+ render();
501
+ }
502
+
503
+ function deleteSelectedNode() {
504
+ if (!selectedNodeId) return;
505
+ nodes = nodes.filter(n => n.id !== selectedNodeId);
506
+ edges = edges.filter(e => e.source !== selectedNodeId && e.target !== selectedNodeId);
507
+ send('node:remove', { workflowId: currentWfId(), nodeId: selectedNodeId });
508
+ selectedNodeId = null;
509
+ render();
510
+ }
511
+
512
+ function updateSelectedNode() {
513
+ const n = nodes.find(n => n.id === selectedNodeId);
514
+ if (!n) return;
515
+ n.label = document.getElementById('cfgLabel').value;
516
+ n.config.model = document.getElementById('cfgModel').value;
517
+ n.config.systemPrompt = document.getElementById('cfgPrompt').value;
518
+ n.config.maxLoops = parseInt(document.getElementById('cfgMaxLoops').value) || 1;
519
+ send('node:update', { workflowId: currentWfId(), node: n });
520
+ render();
521
+ }
522
+
523
+ function currentWfId() { return 'wf-current'; }
524
+
525
+ // ─── Rendering ──────────────────────────────────────
526
+ function render() {
527
+ const canvas = document.getElementById('canvas');
528
+ const empty = document.getElementById('emptyState');
529
+ empty.style.display = nodes.length ? 'none' : 'flex';
530
+
531
+ canvas.innerHTML = '';
532
+ for (const n of nodes) {
533
+ const el = document.createElement('div');
534
+ el.className = `node${n.id === selectedNodeId ? ' selected' : ''}${n._status || ''}`;
535
+ el.style.left = n.position.x + 'px';
536
+ el.style.top = n.position.y + 'px';
537
+ el.dataset.id = n.id;
538
+ el.innerHTML = `
539
+ <div class="port in" data-port="in" data-node="${n.id}"></div>
540
+ <div class="port out" data-port="out" data-node="${n.id}"></div>
541
+ <div class="node-header"><span class="dot"></span>${n.label}</div>
542
+ <div class="node-body">
543
+ ${n.config.model ? `<span class="model-tag">${n.config.model}</span>` : '<span style="opacity:0.4">no model</span>'}
544
+ <div style="margin-top:4px">loops: ${n.config.maxLoops ?? 1}</div>
545
+ </div>`;
546
+ canvas.appendChild(el);
547
+
548
+ // Drag
549
+ el.addEventListener('mousedown', (e) => {
550
+ if (e.target.classList.contains('port')) return;
551
+ selectedNodeId = n.id;
552
+ dragging = { nodeId: n.id, offsetX: e.clientX - n.position.x, offsetY: e.clientY - n.position.y };
553
+ render();
554
+ });
555
+ }
556
+
557
+ // Select on click
558
+ canvas.addEventListener('mousedown', (e) => {
559
+ const node = e.target.closest('.node');
560
+ if (node) {
561
+ selectedNodeId = node.dataset.id;
562
+ showConfig();
563
+ render();
564
+ }
565
+ });
566
+
567
+ drawArrows();
568
+ showConfig();
569
+ }
570
+
571
+ function showConfig() {
572
+ const panel = document.getElementById('nodeConfig');
573
+ const n = nodes.find(n => n.id === selectedNodeId);
574
+ if (!n) { panel.style.display = 'none'; return; }
575
+ panel.style.display = 'block';
576
+ document.getElementById('cfgLabel').value = n.label;
577
+ document.getElementById('cfgModel').value = n.config.model || '';
578
+ document.getElementById('cfgPrompt').value = n.config.systemPrompt || '';
579
+ document.getElementById('cfgMaxLoops').value = n.config.maxLoops || 1;
580
+ }
581
+
582
+ function drawArrows() {
583
+ const svg = document.getElementById('arrows');
584
+ svg.innerHTML = '';
585
+ for (const e of edges) {
586
+ const src = nodes.find(n => n.id === e.source);
587
+ const tgt = nodes.find(n => n.id === e.target);
588
+ if (!src || !tgt) continue;
589
+ const x1 = src.position.x + 180, y1 = src.position.y + 25;
590
+ const x2 = tgt.position.x, y2 = tgt.position.y + 25;
591
+ const mx = (x1 + x2) / 2;
592
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
593
+ path.setAttribute('d', `M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}`);
594
+ if (e._active) path.classList.add('active');
595
+ svg.appendChild(path);
596
+ }
597
+ }
598
+
599
+ // ─── Drag ───────────────────────────────────────────
600
+ document.addEventListener('mousemove', (e) => {
601
+ if (dragging) {
602
+ const n = nodes.find(n => n.id === dragging.nodeId);
603
+ if (n) {
604
+ n.position.x = e.clientX - dragging.offsetX;
605
+ n.position.y = e.clientY - dragging.offsetY;
606
+ render();
607
+ }
608
+ }
609
+ });
610
+ document.addEventListener('mouseup', () => { dragging = null; });
611
+
612
+ // ─── Port connections ───────────────────────────────
613
+ document.addEventListener('mousedown', (e) => {
614
+ const port = e.target.closest('.port');
615
+ if (!port) return;
616
+ e.stopPropagation();
617
+ if (port.dataset.port === 'out') {
618
+ connecting = { fromNodeId: port.dataset.node };
619
+ } else if (port.dataset.port === 'in' && connecting) {
620
+ const edgeId = `e${Date.now()}`;
621
+ edges.push({ id: edgeId, source: connecting.fromNodeId, target: port.dataset.node });
622
+ send('edge:add', { workflowId: currentWfId(), edge: edges[edges.length - 1] });
623
+ connecting = null;
624
+ render();
625
+ }
626
+ });
627
+
628
+ // ─── Run / Stop ─────────────────────────────────────
629
+ function runWorkflow() {
630
+ // Ensure workflow exists
631
+ send('workflow:create', {
632
+ id: currentWfId(),
633
+ name: document.getElementById('wfName').value,
634
+ nodes, edges,
635
+ globalMaxLoops: parseInt(document.getElementById('cfgGlobalLoops').value) || 10,
636
+ });
637
+ setTimeout(() => {
638
+ send('workflow:run', { workflowId: currentWfId() });
639
+ log('▶ Workflow started', 'success');
640
+ }, 100);
641
+ }
642
+
643
+ function stopWorkflow() {
644
+ send('workflow:stop', { workflowId: currentWfId() });
645
+ log('■ Stopped', 'error');
646
+ }
647
+
648
+ function clearAll() {
649
+ nodes = [];
650
+ edges = [];
651
+ selectedNodeId = null;
652
+ send('workflow:delete', { id: currentWfId() });
653
+ render();
654
+ }
655
+
656
+ function updateRunState(data) {
657
+ const state = data.state;
658
+ if (!state) return;
659
+ const pill = document.getElementById('statusP#ill');
660
+ pill.className = `status-pill ${state.status}`;
661
+ pill.textContent = state.status.toUpperCase();
662
+ document.getElementById('iterBadge').textContent = state.iteration;
663
+
664
+ // Update node visual states
665
+ for (const n of nodes) {
666
+ const ns = state.results?.[n.id];
667
+ n._status = ns ? ns.status : '';
668
+ }
669
+ render();
670
+ }
671
+
672
+ function log(text, cls = '') {
673
+ const el = document.getElementById('runLog');
674
+ const entry = document.createElement('div');
675
+ entry.className = `entry ${cls}`;
676
+ entry.textContent = text;
677
+ el.appendChild(entry);
678
+ el.scrollTop = el.scrollHeight;
679
+ }
680
+
681
+ // ─── Init ───────────────────────────────────────────
682
+ connectWS();
683
+ render();
684
+ </script>
685
+ </body>
686
+ </html>
package/package.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "lyu-extension-workflow",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Workflow extension for lyu-code — visually compose multi-agent pipelines",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc -p tsconfig.json"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "frontend/dist"
13
+ ],
14
+ "pi": {
15
+ "extensions": [
16
+ "dist/index.js"
17
+ ]
18
+ },
19
+ "dependencies": {
20
+ "@earendil-works/pi-coding-agent": "^0.87.1",
21
+ "@earendil-works/pi-ai": "^0.87.1"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.8.0",
25
+ "@types/node": "^22.0.0"
26
+ },
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/WeeeeeKdY/lyu-code.git",
31
+ "directory": "packages/extensions/workflow"
32
+ }
33
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.