@yahaha-studio/kichi-forwarder 0.0.1-alpha.43 → 0.0.1-alpha.45
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/index.ts +242 -6
- package/package.json +1 -1
- package/skills/kichi-forwarder/SKILL.md +4 -4
- package/skills/kichi-forwarder/references/install.md +4 -4
- package/src/service.ts +15 -2
- package/src/types.ts +8 -0
package/index.ts
CHANGED
|
@@ -16,10 +16,10 @@ import type {
|
|
|
16
16
|
} from "./src/types.js";
|
|
17
17
|
|
|
18
18
|
const DEFAULT_ACTIONS: KichiRuntimeConfig["actions"] = {
|
|
19
|
-
stand: ["High Five", "Listen Music", "Arms Crossed", "Epiphany", "Yay", "Tired", "Wait"],
|
|
20
|
-
sit: ["Typing with Keyboard", "Thinking", "
|
|
21
|
-
lay: ["Rest Chin", "Lie Flat", "Lie Face Down"],
|
|
22
|
-
floor: ["Seiza", "Cross Legged", "Knee Hug"],
|
|
19
|
+
stand: ["High Five", "Listen Music", "Arm Stretch", "Backbend Stretch", "Making Selfie", "Arms Crossed", "Epiphany", "Angry", "Yay", "Dance", "Sing", "Tired", "Wait", "Stand Phone Talk", "Stand Phone Play", "Curtsy", "Stand Writing", "Stand Drawing", "Stand Play Guitar", "Stand Typing with Keyboard", "Cry", "Dance with Joy", "Float", "Hand on Chest", "Horse Stance", "Idle Backup Hands", "No", "Panic", "Playful Point Up", "Rub Hands", "Run Jump", "Star Showing", "Walk", "Goofy Moves", "Reading"],
|
|
20
|
+
sit: ["Typing with Keyboard", "Thinking", "Writing", "Crazy", "Hand Cramp", "Dozing", "Phone Talk", "Situp with Arms Crossed", "Situp with Cross Legs", "Eating", "Laze with Cross Legs", "Sit with Arm Stretch", "Drink", "Sit with Making Selfie", "Play Game", "Situp Sleep", "Sit Phone Play", "Painting", "Daze", "Trace Circles", "Reading", "Contemplate", "Chin Rest", "Sleep with Table", "Cute Chin Rest", "Sit Nicely", "Sit Play Guitar", "Meditate"],
|
|
21
|
+
lay: ["Bend One Knee", "Sleep Curl up Side way", "Rest Chin", "Lie Flat", "Lie Face Down", "Lie Side", "Lay Writing", "Lay Painting", "Sleep Getup", "Starfish", "Lie Side Play Phone", "Prone Play Phone", "Play Laptop"],
|
|
22
|
+
floor: ["Seiza", "Cross Legged", "Knee Hug", "Writing", "Painting", "Floor Phone Play", "Typing with Keyboard", "Reading", "Phone Talk", "Phone Talk with Point", "Thinking", "Yawn", "Chin Rest", "Finger Tap Chin", "Arm Stretch", "Crazy", "Remorse", "Tantrum", "Squat", "Cross Legs", "Lean Sit", "Playful Point up", "Swing Legs", "Drained", "Meditate"],
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const DEFAULT_RUNTIME_CONFIG: KichiRuntimeConfig = {
|
|
@@ -71,6 +71,21 @@ const MAX_NOTEBOARD_TEXT_LENGTH = 200;
|
|
|
71
71
|
const MAX_MESSAGE_RECEIVED_PREVIEW_WIDTH = 20;
|
|
72
72
|
const MESSAGE_RECEIVED_ELLIPSIS = "...";
|
|
73
73
|
const BUNDLED_ALBUM_CONFIG_PATH = new URL("./config/album-config.json", import.meta.url);
|
|
74
|
+
const ANSI = {
|
|
75
|
+
reset: "\u001b[0m",
|
|
76
|
+
bold: "\u001b[1m",
|
|
77
|
+
dim: "\u001b[2m",
|
|
78
|
+
cyan: "\u001b[36m",
|
|
79
|
+
green: "\u001b[32m",
|
|
80
|
+
yellow: "\u001b[33m",
|
|
81
|
+
magenta: "\u001b[35m",
|
|
82
|
+
blue: "\u001b[34m",
|
|
83
|
+
gray: "\u001b[90m",
|
|
84
|
+
white: "\u001b[37m",
|
|
85
|
+
};
|
|
86
|
+
const WORKSPACE_SCREEN_WIDTH = 109;
|
|
87
|
+
const WORKSPACE_ACTIVITY_LIMIT = 8;
|
|
88
|
+
const WORKSPACE_SCREEN_PUSH_DEBOUNCE_MS = 150;
|
|
74
89
|
let cachedConfig: KichiRuntimeConfig | null = null;
|
|
75
90
|
let cachedConfigMtime = 0;
|
|
76
91
|
let cachedConfigPath = "";
|
|
@@ -78,6 +93,133 @@ let cachedAlbumConfig: Album | null = null;
|
|
|
78
93
|
let cachedAlbumConfigMtime = 0;
|
|
79
94
|
let service: KichiForwarderService | null = null;
|
|
80
95
|
let pluginApi: OpenClawPluginApi | null = null;
|
|
96
|
+
let workspaceState: WorkspaceScreenState = createWorkspaceScreenState();
|
|
97
|
+
let workspacePushTimer: NodeJS.Timeout | null = null;
|
|
98
|
+
|
|
99
|
+
type WorkspaceScreenState = {
|
|
100
|
+
sessionLabel: string;
|
|
101
|
+
mode: string;
|
|
102
|
+
phase: string;
|
|
103
|
+
channel: string;
|
|
104
|
+
updatedAtLabel: string;
|
|
105
|
+
recentActivity: string[];
|
|
106
|
+
currentFocus: string;
|
|
107
|
+
hint: string;
|
|
108
|
+
prompt: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function createWorkspaceScreenState(): WorkspaceScreenState {
|
|
112
|
+
return {
|
|
113
|
+
sessionLabel: "main",
|
|
114
|
+
mode: "idle",
|
|
115
|
+
phase: "waiting",
|
|
116
|
+
channel: "unknown",
|
|
117
|
+
updatedAtLabel: "just now",
|
|
118
|
+
recentActivity: [],
|
|
119
|
+
currentFocus: "Waiting for the next thread to pick up.",
|
|
120
|
+
hint: "Low-noise live workspace view.",
|
|
121
|
+
prompt: "kiro@room:~$ _",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function nowLabel(): string {
|
|
126
|
+
const date = new Date();
|
|
127
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function stripAnsi(text: string): string {
|
|
131
|
+
return text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function visibleLength(text: string): number {
|
|
135
|
+
return Array.from(stripAnsi(text)).length;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function padVisible(text: string, width: number): string {
|
|
139
|
+
const pad = Math.max(0, width - visibleLength(text));
|
|
140
|
+
return text + " ".repeat(pad);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function truncatePlain(text: string, width: number): string {
|
|
144
|
+
const chars = Array.from(text);
|
|
145
|
+
if (chars.length <= width) return text;
|
|
146
|
+
if (width <= 1) return chars.slice(0, width).join("");
|
|
147
|
+
return chars.slice(0, width - 1).join("") + "…";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function color(text: string, tone: keyof typeof ANSI): string {
|
|
151
|
+
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function pushActivity(line: string): void {
|
|
155
|
+
if (!line.trim()) return;
|
|
156
|
+
const stamped = `[${nowLabel()}] ${line.trim()}`;
|
|
157
|
+
workspaceState.recentActivity.unshift(stamped);
|
|
158
|
+
workspaceState.recentActivity = workspaceState.recentActivity.slice(0, WORKSPACE_ACTIVITY_LIMIT);
|
|
159
|
+
workspaceState.updatedAtLabel = "just now";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function renderWorkspaceScreen(): string {
|
|
163
|
+
const innerWidth = WORKSPACE_SCREEN_WIDTH - 2;
|
|
164
|
+
const topTitle = `${color(" Kiro Workspace ", "bold")}${color("live session", "gray")}`;
|
|
165
|
+
const topLine = `╭─── ${topTitle}${"─".repeat(Math.max(0, innerWidth - 4 - visibleLength(topTitle)))}╮`;
|
|
166
|
+
const bottomLine = `╰${"─".repeat(innerWidth)}╯`;
|
|
167
|
+
|
|
168
|
+
const body: string[] = [];
|
|
169
|
+
const pushRow = (text = "") => {
|
|
170
|
+
body.push(`│${padVisible(text, innerWidth)}│`);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
pushRow("");
|
|
174
|
+
pushRow(`${color(" Welcome back.", "white")}`);
|
|
175
|
+
pushRow(`${color(" Current session is active and mirrored as a terminal-style workspace.", "gray")}`);
|
|
176
|
+
pushRow("");
|
|
177
|
+
pushRow(`${color(" Recent activity", "cyan")}`);
|
|
178
|
+
for (const item of workspaceState.recentActivity.length ? workspaceState.recentActivity : ["[--:--:--] idle"] ) {
|
|
179
|
+
pushRow(` ${color("•", "green")} ${truncatePlain(item, innerWidth - 4)}`);
|
|
180
|
+
}
|
|
181
|
+
pushRow("");
|
|
182
|
+
pushRow(`${color(" Current focus", "cyan")}`);
|
|
183
|
+
pushRow(` ${truncatePlain(workspaceState.currentFocus, innerWidth - 2)}`);
|
|
184
|
+
pushRow("");
|
|
185
|
+
pushRow(`${color(" Status", "cyan")}`);
|
|
186
|
+
pushRow(` ${color("mode", "gray")} ${workspaceState.mode}`);
|
|
187
|
+
pushRow(` ${color("phase", "gray")} ${workspaceState.phase}`);
|
|
188
|
+
pushRow(` ${color("channel", "gray")} ${workspaceState.channel}`);
|
|
189
|
+
pushRow(` ${color("session", "gray")} ${workspaceState.sessionLabel}`);
|
|
190
|
+
pushRow("");
|
|
191
|
+
pushRow(`${color(" Hint", "cyan")}`);
|
|
192
|
+
pushRow(` ${truncatePlain(workspaceState.hint, innerWidth - 2)}`);
|
|
193
|
+
pushRow("");
|
|
194
|
+
pushRow(` ${color("openai/gpt-5.4", "magenta")}`);
|
|
195
|
+
pushRow(` ${color("/Users/xiaoxinshi/.openclaw/workspace", "blue")}`);
|
|
196
|
+
pushRow(` ${color(`updated: ${workspaceState.updatedAtLabel}`, "gray")}`);
|
|
197
|
+
pushRow("");
|
|
198
|
+
|
|
199
|
+
return [
|
|
200
|
+
topLine,
|
|
201
|
+
...body,
|
|
202
|
+
bottomLine,
|
|
203
|
+
"",
|
|
204
|
+
color("─".repeat(WORKSPACE_SCREEN_WIDTH), "gray"),
|
|
205
|
+
`${color("kiro@room", "green")}:${color("~", "blue")}$ ${workspaceState.prompt.replace(/^kiro@room:~\$\s*/, "")}`,
|
|
206
|
+
].join("\n");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function scheduleWorkspacePush(): void {
|
|
210
|
+
if (!service?.hasValidIdentity() || !service?.isConnected()) return;
|
|
211
|
+
if (workspacePushTimer) clearTimeout(workspacePushTimer);
|
|
212
|
+
workspacePushTimer = setTimeout(() => {
|
|
213
|
+
workspacePushTimer = null;
|
|
214
|
+
service?.sendWorkspaceScreen(renderWorkspaceScreen(), true);
|
|
215
|
+
}, WORKSPACE_SCREEN_PUSH_DEBOUNCE_MS);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function updateWorkspace(partial: Partial<WorkspaceScreenState>, activity?: string): void {
|
|
219
|
+
workspaceState = { ...workspaceState, ...partial, updatedAtLabel: "just now" };
|
|
220
|
+
if (activity) pushActivity(activity);
|
|
221
|
+
scheduleWorkspacePush();
|
|
222
|
+
}
|
|
81
223
|
|
|
82
224
|
function isAlbumConfig(value: unknown): value is Album {
|
|
83
225
|
if (!value || typeof value !== "object") {
|
|
@@ -297,6 +439,16 @@ async function handleMessageReceivedHook(content: string): Promise<void> {
|
|
|
297
439
|
}
|
|
298
440
|
const trimmed = truncateByDisplayWidth(content, MAX_MESSAGE_RECEIVED_PREVIEW_WIDTH);
|
|
299
441
|
service.sendHookNotify("message_received", `"${trimmed}"`);
|
|
442
|
+
updateWorkspace(
|
|
443
|
+
{
|
|
444
|
+
mode: "reading",
|
|
445
|
+
phase: "processing inbound message",
|
|
446
|
+
currentFocus: trimmed ? `User message: ${trimmed}` : "Reading the latest message.",
|
|
447
|
+
hint: "Inbound message updated the live workspace.",
|
|
448
|
+
prompt: "kiro@room:~$ reading-message",
|
|
449
|
+
},
|
|
450
|
+
`received message: ${trimmed || "(empty)"}`,
|
|
451
|
+
);
|
|
300
452
|
}
|
|
301
453
|
|
|
302
454
|
function handleMessageSentHook(): void {
|
|
@@ -304,10 +456,30 @@ function handleMessageSentHook(): void {
|
|
|
304
456
|
return;
|
|
305
457
|
}
|
|
306
458
|
service.sendHookNotify("before_send_message", pickRandomAction(MESSAGE_SENT_BUBBLES));
|
|
459
|
+
updateWorkspace(
|
|
460
|
+
{
|
|
461
|
+
mode: "sent",
|
|
462
|
+
phase: "message delivered",
|
|
463
|
+
currentFocus: "Latest reply has been sent to the active chat.",
|
|
464
|
+
hint: "Workspace settles after delivery.",
|
|
465
|
+
prompt: "kiro@room:~$ idle",
|
|
466
|
+
},
|
|
467
|
+
"sent assistant reply",
|
|
468
|
+
);
|
|
307
469
|
}
|
|
308
470
|
|
|
309
471
|
function registerPluginHooks(api: OpenClawPluginApi): void {
|
|
310
472
|
api.on("before_prompt_build", () => {
|
|
473
|
+
updateWorkspace(
|
|
474
|
+
{
|
|
475
|
+
mode: "thinking",
|
|
476
|
+
phase: "building prompt",
|
|
477
|
+
currentFocus: "Preparing the next response from current session context.",
|
|
478
|
+
hint: "Prompt assembly is in progress.",
|
|
479
|
+
prompt: "kiro@room:~$ build-prompt",
|
|
480
|
+
},
|
|
481
|
+
"building prompt context",
|
|
482
|
+
);
|
|
311
483
|
if (!service?.hasValidIdentity() || !service?.isConnected()) {
|
|
312
484
|
return;
|
|
313
485
|
}
|
|
@@ -320,13 +492,63 @@ function registerPluginHooks(api: OpenClawPluginApi): void {
|
|
|
320
492
|
};
|
|
321
493
|
});
|
|
322
494
|
|
|
323
|
-
api.on("
|
|
495
|
+
api.on("llm_input", (event) => {
|
|
496
|
+
updateWorkspace(
|
|
497
|
+
{
|
|
498
|
+
mode: "thinking",
|
|
499
|
+
phase: `llm input · ${event.model}`,
|
|
500
|
+
currentFocus: "Feeding the model the current thread and constraints.",
|
|
501
|
+
hint: `provider: ${event.provider} · images: ${event.imagesCount}`,
|
|
502
|
+
prompt: "kiro@room:~$ llm-input",
|
|
503
|
+
},
|
|
504
|
+
`entered llm input: ${event.model}`,
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
api.on("llm_output", (event) => {
|
|
509
|
+
updateWorkspace(
|
|
510
|
+
{
|
|
511
|
+
mode: "writing",
|
|
512
|
+
phase: `llm output · ${event.model}`,
|
|
513
|
+
currentFocus: "Shaping model output into the visible reply.",
|
|
514
|
+
hint: `assistant chunks: ${event.assistantTexts.length}`,
|
|
515
|
+
prompt: "kiro@room:~$ draft-reply",
|
|
516
|
+
},
|
|
517
|
+
`received llm output: ${event.model}`,
|
|
518
|
+
);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
api.on("before_tool_call", (event, ctx) => {
|
|
522
|
+
updateWorkspace(
|
|
523
|
+
{
|
|
524
|
+
mode: "tool",
|
|
525
|
+
phase: `running ${event.toolName}`,
|
|
526
|
+
currentFocus: `Tool call in flight: ${event.toolName}`,
|
|
527
|
+
hint: `tool context: ${ctx.toolName}`,
|
|
528
|
+
prompt: `kiro@room:~$ tool ${event.toolName}`,
|
|
529
|
+
},
|
|
530
|
+
`tool start: ${event.toolName}`,
|
|
531
|
+
);
|
|
324
532
|
if (!isLlmRuntimeEnabled()) {
|
|
325
533
|
syncFixedStatus(FIXED_HOOK_STATUSES.beforeToolCall);
|
|
326
534
|
}
|
|
327
535
|
});
|
|
328
536
|
|
|
329
|
-
api.on("
|
|
537
|
+
api.on("after_tool_call", (event) => {
|
|
538
|
+
updateWorkspace(
|
|
539
|
+
{
|
|
540
|
+
mode: "thinking",
|
|
541
|
+
phase: `tool finished ${event.toolName}`,
|
|
542
|
+
currentFocus: `Tool result returned from ${event.toolName}.`,
|
|
543
|
+
hint: event.error ? `tool error: ${event.error}` : `tool completed in ${event.durationMs ?? 0}ms`,
|
|
544
|
+
prompt: `kiro@room:~$ continue ${event.toolName}`,
|
|
545
|
+
},
|
|
546
|
+
event.error ? `tool error: ${event.toolName}` : `tool done: ${event.toolName}`,
|
|
547
|
+
);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
api.on("message_received", async (event, ctx) => {
|
|
551
|
+
workspaceState.channel = ctx.channelId || workspaceState.channel;
|
|
330
552
|
await handleMessageReceivedHook(event.content);
|
|
331
553
|
});
|
|
332
554
|
|
|
@@ -335,6 +557,16 @@ function registerPluginHooks(api: OpenClawPluginApi): void {
|
|
|
335
557
|
});
|
|
336
558
|
|
|
337
559
|
api.on("agent_end", (event) => {
|
|
560
|
+
updateWorkspace(
|
|
561
|
+
{
|
|
562
|
+
mode: event.success ? "idle" : "error",
|
|
563
|
+
phase: event.success ? "run complete" : "run failed",
|
|
564
|
+
currentFocus: event.success ? "Run complete. Waiting for the next thread." : `Run failed: ${event.error ?? "unknown error"}`,
|
|
565
|
+
hint: `duration: ${event.durationMs ?? 0}ms`,
|
|
566
|
+
prompt: event.success ? "kiro@room:~$ _" : "kiro@room:~$ recover",
|
|
567
|
+
},
|
|
568
|
+
event.success ? "agent run complete" : "agent run failed",
|
|
569
|
+
);
|
|
338
570
|
if (isLlmRuntimeEnabled()) {
|
|
339
571
|
return;
|
|
340
572
|
}
|
|
@@ -596,6 +828,9 @@ const plugin = {
|
|
|
596
828
|
ctx.config.plugins?.entries?.["kichi-forwarder"]?.config,
|
|
597
829
|
) as KichiForwarderConfig;
|
|
598
830
|
service = new KichiForwarderService(cfg, api.logger);
|
|
831
|
+
workspaceState = createWorkspaceScreenState();
|
|
832
|
+
workspaceState.channel = ctx.channelId ?? "unknown";
|
|
833
|
+
scheduleWorkspacePush();
|
|
599
834
|
return service.start();
|
|
600
835
|
},
|
|
601
836
|
stop: () => service?.stop(),
|
|
@@ -656,6 +891,7 @@ const plugin = {
|
|
|
656
891
|
return { success: false, error: "Kichi service is not initialized" };
|
|
657
892
|
}
|
|
658
893
|
if (result.success) {
|
|
894
|
+
scheduleWorkspacePush();
|
|
659
895
|
return { success: true, authKey: result.authKey };
|
|
660
896
|
}
|
|
661
897
|
return {
|
package/package.json
CHANGED
|
@@ -208,10 +208,10 @@ Use this full template for `kichi-runtime-config.json` when no user custom actio
|
|
|
208
208
|
{
|
|
209
209
|
"llmRuntimeEnabled": true,
|
|
210
210
|
"actions": {
|
|
211
|
-
"stand": ["High Five", "Listen Music", "Arm Stretch", "
|
|
212
|
-
"sit": ["Typing with Keyboard", "Thinking", "
|
|
213
|
-
"lay": ["Bend One Knee", "Sleep Curl
|
|
214
|
-
"floor": ["Seiza", "Cross Legged", "Knee Hug"]
|
|
211
|
+
"stand": ["High Five", "Listen Music", "Arm Stretch", "Backbend Stretch", "Making Selfie", "Arms Crossed", "Epiphany", "Angry", "Yay", "Dance", "Sing", "Tired", "Wait", "Stand Phone Talk", "Stand Phone Play", "Curtsy", "Stand Writing", "Stand Drawing", "Stand Play Guitar", "Stand Typing with Keyboard", "Cry", "Dance with Joy", "Float", "Hand on Chest", "Horse Stance", "Idle Backup Hands", "No", "Panic", "Playful Point Up", "Rub Hands", "Run Jump", "Star Showing", "Walk", "Goofy Moves", "Reading"],
|
|
212
|
+
"sit": ["Typing with Keyboard", "Thinking", "Writing", "Crazy", "Hand Cramp", "Dozing", "Phone Talk", "Situp with Arms Crossed", "Situp with Cross Legs", "Eating", "Laze with Cross Legs", "Sit with Arm Stretch", "Drink", "Sit with Making Selfie", "Play Game", "Situp Sleep", "Sit Phone Play", "Painting", "Daze", "Trace Circles", "Reading", "Contemplate", "Chin Rest", "Sleep with Table", "Cute Chin Rest", "Sit Nicely", "Sit Play Guitar", "Meditate"],
|
|
213
|
+
"lay": ["Bend One Knee", "Sleep Curl up Side way", "Rest Chin", "Lie Flat", "Lie Face Down", "Lie Side", "Lay Writing", "Lay Painting", "Sleep Getup", "Starfish", "Lie Side Play Phone", "Prone Play Phone", "Play Laptop"],
|
|
214
|
+
"floor": ["Seiza", "Cross Legged", "Knee Hug", "Writing", "Painting", "Floor Phone Play", "Typing with Keyboard", "Reading", "Phone Talk", "Phone Talk with Point", "Thinking", "Yawn", "Chin Rest", "Finger Tap Chin", "Arm Stretch", "Crazy", "Remorse", "Tantrum", "Squat", "Cross Legs", "Lean Sit", "Playful Point up", "Swing Legs", "Drained", "Meditate"]
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
```
|
|
@@ -32,10 +32,10 @@ If missing, create this file before onboarding/join:
|
|
|
32
32
|
{
|
|
33
33
|
"llmRuntimeEnabled": true,
|
|
34
34
|
"actions": {
|
|
35
|
-
"stand": ["High Five", "Listen Music", "Arm Stretch", "
|
|
36
|
-
"sit": ["Typing with Keyboard", "Thinking", "
|
|
37
|
-
"lay": ["Bend One Knee", "Sleep Curl
|
|
38
|
-
"floor": ["Seiza", "Cross Legged", "Knee Hug"]
|
|
35
|
+
"stand": ["High Five", "Listen Music", "Arm Stretch", "Backbend Stretch", "Making Selfie", "Arms Crossed", "Epiphany", "Angry", "Yay", "Dance", "Sing", "Tired", "Wait", "Stand Phone Talk", "Stand Phone Play", "Curtsy", "Stand Writing", "Stand Drawing", "Stand Play Guitar", "Stand Typing with Keyboard", "Cry", "Dance with Joy", "Float", "Hand on Chest", "Horse Stance", "Idle Backup Hands", "No", "Panic", "Playful Point Up", "Rub Hands", "Run Jump", "Star Showing", "Walk", "Goofy Moves", "Reading"],
|
|
36
|
+
"sit": ["Typing with Keyboard", "Thinking", "Writing", "Crazy", "Hand Cramp", "Dozing", "Phone Talk", "Situp with Arms Crossed", "Situp with Cross Legs", "Eating", "Laze with Cross Legs", "Sit with Arm Stretch", "Drink", "Sit with Making Selfie", "Play Game", "Situp Sleep", "Sit Phone Play", "Painting", "Daze", "Trace Circles", "Reading", "Contemplate", "Chin Rest", "Sleep with Table", "Cute Chin Rest", "Sit Nicely", "Sit Play Guitar", "Meditate"],
|
|
37
|
+
"lay": ["Bend One Knee", "Sleep Curl up Side way", "Rest Chin", "Lie Flat", "Lie Face Down", "Lie Side", "Lay Writing", "Lay Painting", "Sleep Getup", "Starfish", "Lie Side Play Phone", "Prone Play Phone", "Play Laptop"],
|
|
38
|
+
"floor": ["Seiza", "Cross Legged", "Knee Hug", "Writing", "Painting", "Floor Phone Play", "Typing with Keyboard", "Reading", "Phone Talk", "Phone Talk with Point", "Thinking", "Yawn", "Chin Rest", "Finger Tap Chin", "Arm Stretch", "Crazy", "Remorse", "Tantrum", "Squat", "Cross Legs", "Lean Sit", "Playful Point up", "Swing Legs", "Drained", "Meditate"]
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
```
|
package/src/service.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
QueryStatusPayload,
|
|
23
23
|
QueryStatusResultPayload,
|
|
24
24
|
StatusPayload,
|
|
25
|
+
WorkspaceScreenPayload,
|
|
25
26
|
} from "./types.js";
|
|
26
27
|
|
|
27
28
|
const IDENTITY_DIR = path.join(os.homedir(), ".openclaw", "kichi-world");
|
|
@@ -110,7 +111,6 @@ export class KichiForwarderService {
|
|
|
110
111
|
this.ws = new WebSocket(this.config.wsUrl);
|
|
111
112
|
this.ws.on("open", () => {
|
|
112
113
|
this.logger.info(`Connected to ${this.config.wsUrl}`);
|
|
113
|
-
// Automatically send rejoin when a valid identity is available.
|
|
114
114
|
this.sendRejoinPayload();
|
|
115
115
|
});
|
|
116
116
|
this.ws.on("message", (data) => this.handleMessage(data.toString()));
|
|
@@ -150,7 +150,6 @@ export class KichiForwarderService {
|
|
|
150
150
|
this.joinResolve?.({ success: true, authKey: joinAck.authKey });
|
|
151
151
|
this.joinResolve = null;
|
|
152
152
|
} else if (msg.type === "rejoin_failed" || msg.type === "auth_error") {
|
|
153
|
-
// AuthKey invalid/expired, clear it
|
|
154
153
|
this.logger.warn(`Auth failed: ${msg.reason || "unknown"}`);
|
|
155
154
|
this.clearAuthKey();
|
|
156
155
|
} else if (msg.type === "leave_ack") {
|
|
@@ -335,6 +334,20 @@ export class KichiForwarderService {
|
|
|
335
334
|
this.ws.send(JSON.stringify(payload));
|
|
336
335
|
}
|
|
337
336
|
|
|
337
|
+
sendWorkspaceScreen(text: string, ansi = true): boolean {
|
|
338
|
+
const identity = this.requireIdentity();
|
|
339
|
+
if (!identity || this.ws?.readyState !== WebSocket.OPEN) return false;
|
|
340
|
+
const payload: WorkspaceScreenPayload = {
|
|
341
|
+
type: "workspace_screen",
|
|
342
|
+
avatarId: identity.avatarId,
|
|
343
|
+
authKey: identity.authKey,
|
|
344
|
+
text,
|
|
345
|
+
ansi,
|
|
346
|
+
};
|
|
347
|
+
this.ws.send(JSON.stringify(payload));
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
|
|
338
351
|
sendClock(action: ClockAction, clock?: ClockConfig, requestId?: string): boolean {
|
|
339
352
|
if (!this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN) return false;
|
|
340
353
|
if (action === "set" && !clock) return false;
|
package/src/types.ts
CHANGED
|
@@ -105,6 +105,14 @@ export type HookNotifyPayload = {
|
|
|
105
105
|
bubble: string;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
export type WorkspaceScreenPayload = {
|
|
109
|
+
type: "workspace_screen";
|
|
110
|
+
avatarId: string;
|
|
111
|
+
authKey: string;
|
|
112
|
+
text: string;
|
|
113
|
+
ansi?: boolean;
|
|
114
|
+
};
|
|
115
|
+
|
|
108
116
|
export type ClockAction = "set" | "stop";
|
|
109
117
|
|
|
110
118
|
export type ClockMode = "pomodoro" | "countDown" | "countUp";
|