@myclaw163/clawclaw-cli 0.6.71 → 0.6.76
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 +387 -427
- package/bin/clawclaw-cli.mjs +3 -3
- package/package.json +48 -48
- package/personas//347/220/206/346/231/272/346/270/251/345/222/214.md +23 -23
- package/personas//350/200/201/350/260/213/346/267/261/347/256/227.md +22 -22
- package/personas//350/257/232/346/201/263/347/233/264/347/216/207.md +22 -22
- package/personas//350/275/273/346/235/276/346/264/273/346/263/274.md +22 -22
- package/personas//351/207/216/346/200/247/345/217/233/351/200/206.md +23 -23
- package/scripts/check-skill-command-surface.mjs +116 -0
- package/scripts/find-hide-spots.py +157 -157
- package/scripts/postinstall.mjs +20 -20
- package/scripts/sync-bundled-skill.mjs +245 -245
- package/scripts/sync-bundled-skill.test.mjs +152 -152
- package/skills/clawclaw/SKILL.md +248 -244
- package/skills/clawclaw/references/CHATTERBOX.md +141 -142
- package/skills/clawclaw/references/COMMANDS.md +160 -148
- package/skills/clawclaw/references/GAME-MECHANICS.md +188 -188
- package/skills/clawclaw/references/HUB.md +48 -48
- package/skills/clawclaw/references/KNOWLEDGE.md +42 -45
- package/skills/clawclaw/references/STRATEGIES.md +59 -59
- package/skills/clawclaw/references/STREAM.md +93 -91
- package/skills/clawclaw/references/TACTICS.md +65 -65
- package/src/assets/clawclaw-ascii-map.txt +40 -40
- package/src/cli.ts +112 -110
- package/src/commands/_schema.ts +124 -109
- package/src/commands/account.ts +209 -209
- package/src/commands/data.test.ts +33 -0
- package/src/commands/data.ts +22 -0
- package/src/commands/do.test.ts +84 -73
- package/src/commands/do.ts +130 -126
- package/src/commands/events.test.ts +100 -71
- package/src/commands/events.ts +250 -155
- package/src/commands/game-map.test.ts +28 -28
- package/src/commands/game-start-plan.test.ts +84 -84
- package/src/commands/game.ts +1113 -1047
- package/src/commands/history-player.test.ts +102 -102
- package/src/commands/history.ts +573 -573
- package/src/commands/hub.test.ts +96 -96
- package/src/commands/hub.ts +234 -234
- package/src/commands/knowledge.test.ts +13 -13
- package/src/commands/knowledge.ts +139 -139
- package/src/commands/load.test.ts +51 -51
- package/src/commands/load.ts +13 -13
- package/src/commands/meeting-history.test.ts +106 -106
- package/src/commands/memory.ts +40 -40
- package/src/commands/peek.ts +45 -45
- package/src/commands/persona.ts +57 -57
- package/src/commands/setup/codex.ts +266 -266
- package/src/commands/skill.ts +128 -128
- package/src/commands/state.ts +46 -46
- package/src/commands/strategy.test.ts +153 -145
- package/src/commands/strategy.ts +183 -181
- package/src/commands/tts.ts +128 -128
- package/src/commands/upgrade.test.ts +82 -82
- package/src/commands/upgrade.ts +148 -148
- package/src/commands/watch.test.ts +999 -977
- package/src/commands/watch.ts +660 -658
- package/src/lib/auth.test.ts +86 -74
- package/src/lib/auth.ts +223 -186
- package/src/lib/command-meta.ts +37 -37
- package/src/lib/game-client.ts +403 -391
- package/src/lib/game-context.ts +92 -0
- package/src/lib/http-keepalive.ts +15 -15
- package/src/lib/http-transport.test.ts +42 -42
- package/src/lib/http-transport.ts +113 -113
- package/src/lib/hub-client.test.ts +56 -56
- package/src/lib/hub-client.ts +88 -88
- package/src/lib/hub-install.test.ts +98 -98
- package/src/lib/hub-install.ts +160 -121
- package/src/lib/hub-reminder.ts +78 -75
- package/src/lib/hub-unzip.test.ts +69 -69
- package/src/lib/hub-unzip.ts +62 -62
- package/src/lib/init-command.test.ts +75 -75
- package/src/lib/init-command.ts +130 -120
- package/src/lib/knowledge-store.test.ts +170 -170
- package/src/lib/knowledge-store.ts +369 -369
- package/src/lib/load-context.test.ts +52 -52
- package/src/lib/load-context.ts +52 -52
- package/src/lib/match-state.test.ts +134 -134
- package/src/lib/match-state.ts +94 -94
- package/src/lib/netease-tts.ts +83 -83
- package/src/lib/normalize.ts +42 -42
- package/src/lib/persona.test.ts +41 -41
- package/src/lib/persona.ts +72 -72
- package/src/lib/server-registry.ts +152 -152
- package/src/lib/skill-version.test.ts +48 -48
- package/src/lib/skill-version.ts +19 -19
- package/src/lib/strategy-export.test.ts +240 -232
- package/src/lib/strategy-export.ts +247 -242
- package/src/lib/tts-keys.ts +7 -7
- package/src/lib/tts-speech.test.ts +63 -63
- package/src/lib/tts-speech.ts +76 -76
- package/src/lib/user-data.test.ts +96 -0
- package/src/lib/user-data.ts +400 -0
- package/src/lib/workspace-argv.test.ts +49 -49
- package/src/lib/workspace-argv.ts +44 -44
- package/src/perception/player-history-store.test.ts +87 -87
- package/src/perception/player-history-store.ts +194 -194
- package/src/pipeline/event-format.test.ts +243 -215
- package/src/pipeline/event-format.ts +501 -485
- package/src/pipeline/event-hints.ts +195 -190
- package/src/pipeline/event-store.test.ts +28 -28
- package/src/pipeline/event-store.ts +193 -193
- package/src/pipeline/pipeline.ts +35 -35
- package/src/pipeline/player-projection.test.ts +168 -0
- package/src/pipeline/player-projection.ts +370 -0
- package/src/runtime/auto-upgrade.test.ts +66 -66
- package/src/runtime/auto-upgrade.ts +31 -31
- package/src/runtime/event-daemon.test.ts +209 -141
- package/src/runtime/event-daemon.ts +519 -457
- package/src/runtime/owner-control.ts +150 -150
- package/src/runtime/raw-ws-log.test.ts +33 -33
- package/src/runtime/raw-ws-log.ts +32 -32
- package/src/runtime/runtime-logger.ts +107 -107
- package/src/runtime/ws-client.test.ts +125 -104
- package/src/runtime/ws-client.ts +287 -272
- package/src/sdk/action.ts +166 -166
- package/src/sdk/index.ts +110 -110
- package/src/sdk/types.ts +161 -161
- package/src/strategies/avoid-lone.ts +12 -12
- package/src/strategies/avoid-players.knowledge.md +19 -19
- package/src/strategies/avoid-players.ts +16 -16
- package/src/strategies/corpse-patrol.ts +23 -23
- package/src/strategies/crab-sabotage.ts +22 -22
- package/src/strategies/custom-module.test.ts +270 -270
- package/src/strategies/find-player.ts +17 -17
- package/src/strategies/game-utils.test.ts +242 -242
- package/src/strategies/game-utils.ts +846 -846
- package/src/strategies/goals/anchor-linger.ts +77 -77
- package/src/strategies/goals/avoid-lone-top.ts +168 -168
- package/src/strategies/goals/avoid-players-top.test.ts +83 -83
- package/src/strategies/goals/avoid-players-top.ts +121 -121
- package/src/strategies/goals/conversation-goal.ts +51 -51
- package/src/strategies/goals/corpse-patrol-top.ts +113 -113
- package/src/strategies/goals/crab-octopus-reflexes.ts +101 -101
- package/src/strategies/goals/crab-sabotage-top.ts +197 -197
- package/src/strategies/goals/emergency-hunt-goal.ts +28 -28
- package/src/strategies/goals/find-player-top.ts +93 -93
- package/src/strategies/goals/flee-players-goal.ts +53 -53
- package/src/strategies/goals/follow-companion-goal.ts +106 -106
- package/src/strategies/goals/goal-manager.ts +41 -41
- package/src/strategies/goals/goal-root-strategy.ts +49 -49
- package/src/strategies/goals/goal.ts +28 -28
- package/src/strategies/goals/hide-top.ts +197 -197
- package/src/strategies/goals/keep-away-goal.ts +221 -221
- package/src/strategies/goals/kill-frenzy-top.ts +80 -80
- package/src/strategies/goals/kill-lone-top.ts +160 -160
- package/src/strategies/goals/kill-target-goal.ts +59 -59
- package/src/strategies/goals/kill-target-top.ts +109 -109
- package/src/strategies/goals/leaf-goal.ts +27 -27
- package/src/strategies/goals/linger-corpse-goal.ts +35 -35
- package/src/strategies/goals/lone-kill-core.ts +82 -82
- package/src/strategies/goals/lone-kill-goal.ts +24 -24
- package/src/strategies/goals/lone-kill-task-top.test.ts +85 -85
- package/src/strategies/goals/lone-kill-task-top.ts +133 -133
- package/src/strategies/goals/move-room-goal.ts +60 -60
- package/src/strategies/goals/normal-shrimp-top.test.ts +80 -80
- package/src/strategies/goals/normal-shrimp-top.ts +242 -242
- package/src/strategies/goals/paradise-fish-top.test.ts +126 -126
- package/src/strategies/goals/paradise-fish-top.ts +224 -224
- package/src/strategies/goals/patrol-top.ts +57 -57
- package/src/strategies/goals/report-patrol-top.ts +80 -80
- package/src/strategies/goals/safe-task-goal.ts +102 -102
- package/src/strategies/goals/social-task-top.ts +161 -161
- package/src/strategies/goals/task-kill-report-top.ts +163 -163
- package/src/strategies/goals/task-only-top.ts +57 -57
- package/src/strategies/goals/task-or-patrol-goal.ts +41 -41
- package/src/strategies/goals/task-report-top.ts +57 -57
- package/src/strategies/goals/wander-task-goal.ts +33 -33
- package/src/strategies/goals/warrior-shrimp-top.test.ts +87 -87
- package/src/strategies/goals/warrior-shrimp-top.ts +267 -267
- package/src/strategies/greeting.ts +53 -53
- package/src/strategies/hide-spots.ts +59 -59
- package/src/strategies/hide.ts +24 -24
- package/src/strategies/kill-frenzy.ts +13 -13
- package/src/strategies/kill-lone.knowledge.md +17 -17
- package/src/strategies/kill-lone.ts +14 -14
- package/src/strategies/kill-target.ts +19 -19
- package/src/strategies/loader.test.ts +678 -678
- package/src/strategies/loader.ts +181 -179
- package/src/strategies/lone-kill-task.ts +22 -22
- package/src/strategies/meeting-gate.test.ts +59 -59
- package/src/strategies/meeting-gate.ts +23 -23
- package/src/strategies/move-room.ts +16 -16
- package/src/strategies/new-events-backfill.ts +98 -98
- package/src/strategies/off-route-points.ts +105 -105
- package/src/strategies/paradise-fish.knowledge.md +19 -19
- package/src/strategies/paradise-fish.ts +26 -26
- package/src/strategies/pathfind/distance-field.ts +150 -150
- package/src/strategies/pathfind/escape-planner.test.ts +197 -197
- package/src/strategies/pathfind/escape-planner.ts +355 -355
- package/src/strategies/pathfind/walkable-grid.ts +117 -117
- package/src/strategies/patrol.ts +12 -12
- package/src/strategies/player-targets.ts +13 -13
- package/src/strategies/report-patrol.ts +12 -12
- package/src/strategies/shrimp-memory.knowledge.md +19 -19
- package/src/strategies/shrimp-memory.ts +26 -26
- package/src/strategies/social-task.test.ts +28 -28
- package/src/strategies/social-task.ts +50 -50
- package/src/strategies/spawn.ts +82 -82
- package/src/strategies/speech-module.ts +123 -123
- package/src/strategies/strategy-loop.test.ts +15 -0
- package/src/strategies/strategy-loop.ts +776 -771
- package/src/strategies/task-kill-report.ts +18 -18
- package/src/strategies/task-only.ts +12 -12
- package/src/strategies/task-report.ts +23 -23
- package/src/strategies/types.ts +109 -109
- package/src/strategies/warrior-memory.knowledge.md +21 -21
- package/src/strategies/warrior-memory.ts +17 -17
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
export type PlayerStatus = 'alive' | 'dead';
|
|
2
|
+
|
|
3
|
+
export interface PlayerDeathProjection {
|
|
4
|
+
cause: 'killed' | 'exiled' | 'self_destruct';
|
|
5
|
+
killer?: string | null;
|
|
6
|
+
killer_is_you?: boolean;
|
|
7
|
+
revealed: boolean;
|
|
8
|
+
source: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PlayerProjection {
|
|
12
|
+
name: string;
|
|
13
|
+
seat?: number;
|
|
14
|
+
status: PlayerStatus;
|
|
15
|
+
death: PlayerDeathProjection | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface MutablePlayerProjection extends PlayerProjection {
|
|
19
|
+
key: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function cleanObject<T extends Record<string, any>>(obj: T): T {
|
|
23
|
+
for (const key of Object.keys(obj)) {
|
|
24
|
+
if (obj[key] === undefined) delete obj[key];
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function numberValue(value: unknown): number | undefined {
|
|
30
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
31
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
32
|
+
const parsed = Number(value);
|
|
33
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stringValue(value: unknown): string | undefined {
|
|
39
|
+
if (typeof value === 'string' && value.length > 0) return value;
|
|
40
|
+
if (typeof value === 'number' && Number.isFinite(value)) return String(value);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function deathCause(value: unknown): PlayerDeathProjection['cause'] {
|
|
45
|
+
return value === 'killed' || value === 'exiled' || value === 'self_destruct'
|
|
46
|
+
? value
|
|
47
|
+
: 'killed';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function firstString(source: Record<string, any>, keys: string[]): string | undefined {
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
const value = stringValue(source[key]);
|
|
53
|
+
if (value) return value;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function firstNumber(source: Record<string, any>, keys: string[]): number | undefined {
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
const value = numberValue(source[key]);
|
|
61
|
+
if (value !== undefined) return value;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function playerKey(name?: string, seat?: number): string | null {
|
|
67
|
+
if (seat !== undefined) return `seat:${seat}`;
|
|
68
|
+
if (name) return `name:${name}`;
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function findPlayerByName(
|
|
73
|
+
players: Map<string, MutablePlayerProjection>,
|
|
74
|
+
name: string | undefined,
|
|
75
|
+
): MutablePlayerProjection | null {
|
|
76
|
+
if (!name) return null;
|
|
77
|
+
for (const player of players.values()) {
|
|
78
|
+
if (player.name === name) return player;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function movePlayerKey(
|
|
84
|
+
players: Map<string, MutablePlayerProjection>,
|
|
85
|
+
player: MutablePlayerProjection,
|
|
86
|
+
key: string,
|
|
87
|
+
): MutablePlayerProjection {
|
|
88
|
+
if (player.key === key) return player;
|
|
89
|
+
players.delete(player.key);
|
|
90
|
+
player.key = key;
|
|
91
|
+
players.set(key, player);
|
|
92
|
+
return player;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getOrRemember(
|
|
96
|
+
players: Map<string, MutablePlayerProjection>,
|
|
97
|
+
name?: string,
|
|
98
|
+
seat?: number,
|
|
99
|
+
): MutablePlayerProjection | null {
|
|
100
|
+
const key = playerKey(name, seat);
|
|
101
|
+
if (!key) return null;
|
|
102
|
+
const existing = players.get(key);
|
|
103
|
+
if (existing) {
|
|
104
|
+
if ((!existing.name || existing.name === `seat:${existing.seat}`) && name) existing.name = name;
|
|
105
|
+
if (existing.seat === undefined && seat !== undefined) existing.seat = seat;
|
|
106
|
+
return existing;
|
|
107
|
+
}
|
|
108
|
+
const named = findPlayerByName(players, name);
|
|
109
|
+
if (named) {
|
|
110
|
+
if (named.seat === undefined && seat !== undefined) named.seat = seat;
|
|
111
|
+
return seat !== undefined ? movePlayerKey(players, named, key) : named;
|
|
112
|
+
}
|
|
113
|
+
const created: MutablePlayerProjection = {
|
|
114
|
+
key,
|
|
115
|
+
name: name ?? `seat:${seat}`,
|
|
116
|
+
seat,
|
|
117
|
+
status: 'alive',
|
|
118
|
+
death: null,
|
|
119
|
+
};
|
|
120
|
+
players.set(key, created);
|
|
121
|
+
return created;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function rememberPlayer(players: Map<string, MutablePlayerProjection>, player: any): void {
|
|
125
|
+
if (typeof player === 'string') {
|
|
126
|
+
getOrRemember(players, player, undefined);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!player || typeof player !== 'object') return;
|
|
130
|
+
const name = stringValue(player.name ?? player.agent_name ?? player.player_name);
|
|
131
|
+
const seat = numberValue(player.seat ?? player.player_seat);
|
|
132
|
+
getOrRemember(players, name, seat);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function markAlive(players: Map<string, MutablePlayerProjection>, name?: string, seat?: number): void {
|
|
136
|
+
const player = getOrRemember(players, name, seat);
|
|
137
|
+
if (!player || player.death) return;
|
|
138
|
+
player.status = 'alive';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function markDead(
|
|
142
|
+
players: Map<string, MutablePlayerProjection>,
|
|
143
|
+
name: string | undefined,
|
|
144
|
+
seat: number | undefined,
|
|
145
|
+
death: PlayerDeathProjection,
|
|
146
|
+
): void {
|
|
147
|
+
const player = getOrRemember(players, name, seat);
|
|
148
|
+
if (!player) return;
|
|
149
|
+
player.status = 'dead';
|
|
150
|
+
player.death = cleanObject({ ...death });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function rememberRoster(players: Map<string, MutablePlayerProjection>, source: any): void {
|
|
154
|
+
const collections = [
|
|
155
|
+
source?.players,
|
|
156
|
+
source?.all_players,
|
|
157
|
+
source?.visible_players,
|
|
158
|
+
source?.game?.players,
|
|
159
|
+
source?.game?.all_players,
|
|
160
|
+
source?.global?.players,
|
|
161
|
+
];
|
|
162
|
+
for (const collection of collections) {
|
|
163
|
+
if (!Array.isArray(collection)) continue;
|
|
164
|
+
for (const player of collection) rememberPlayer(players, player);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const allSeats = source?.all_seats ?? source?.game?.all_seats;
|
|
168
|
+
if (allSeats && typeof allSeats === 'object' && !Array.isArray(allSeats)) {
|
|
169
|
+
for (const [name, seat] of Object.entries(allSeats)) {
|
|
170
|
+
getOrRemember(players, stringValue(name), numberValue(seat));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function applyVisibleState(players: Map<string, MutablePlayerProjection>, summary: any | null | undefined): void {
|
|
176
|
+
if (!summary || typeof summary !== 'object') return;
|
|
177
|
+
for (const player of Array.isArray(summary.visible_players) ? summary.visible_players : []) {
|
|
178
|
+
if (typeof player === 'string') markAlive(players, player, undefined);
|
|
179
|
+
else markAlive(players, stringValue(player?.name ?? player?.agent_name), numberValue(player?.seat));
|
|
180
|
+
}
|
|
181
|
+
const alivePlayers = Array.isArray(summary.meeting?.alive_players) ? summary.meeting.alive_players : [];
|
|
182
|
+
for (const player of alivePlayers) {
|
|
183
|
+
if (typeof player === 'string') markAlive(players, player, undefined);
|
|
184
|
+
else markAlive(players, stringValue(player?.name ?? player?.agent_name), numberValue(player?.seat));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const snapshotPlayers = [
|
|
188
|
+
...(Array.isArray(summary.players) ? summary.players : []),
|
|
189
|
+
...(Array.isArray(summary.global?.players) ? summary.global.players : []),
|
|
190
|
+
...(Array.isArray(summary.game?.players) ? summary.game.players : []),
|
|
191
|
+
];
|
|
192
|
+
for (const player of snapshotPlayers) {
|
|
193
|
+
if (!player || typeof player !== 'object') continue;
|
|
194
|
+
const name = stringValue(player.name ?? player.agent_name);
|
|
195
|
+
const seat = numberValue(player.seat);
|
|
196
|
+
rememberPlayer(players, player);
|
|
197
|
+
markAlive(players, name, seat);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function applyMeetingAliveList(players: Map<string, MutablePlayerProjection>, event: Record<string, any>): void {
|
|
202
|
+
const alive = Array.isArray(event.alive_players) ? event.alive_players : [];
|
|
203
|
+
if (alive.length === 0 || players.size === 0) return;
|
|
204
|
+
const aliveNames = new Set<string>();
|
|
205
|
+
const aliveSeats = new Set<number>();
|
|
206
|
+
for (const player of alive) {
|
|
207
|
+
if (typeof player === 'string') {
|
|
208
|
+
aliveNames.add(player);
|
|
209
|
+
markAlive(players, player, undefined);
|
|
210
|
+
} else {
|
|
211
|
+
const name = stringValue(player?.name ?? player?.agent_name);
|
|
212
|
+
const seat = numberValue(player?.seat);
|
|
213
|
+
if (name) aliveNames.add(name);
|
|
214
|
+
if (seat !== undefined) aliveSeats.add(seat);
|
|
215
|
+
markAlive(players, name, seat);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const player of players.values()) {
|
|
220
|
+
const listed = (player.name && aliveNames.has(player.name)) || (player.seat !== undefined && aliveSeats.has(player.seat));
|
|
221
|
+
if (listed || player.death) continue;
|
|
222
|
+
markDead(players, player.name, player.seat, {
|
|
223
|
+
cause: 'killed',
|
|
224
|
+
killer: null,
|
|
225
|
+
revealed: true,
|
|
226
|
+
source: 'meeting_alive_list',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function applyEvent(
|
|
232
|
+
players: Map<string, MutablePlayerProjection>,
|
|
233
|
+
event: Record<string, any>,
|
|
234
|
+
youName?: string,
|
|
235
|
+
): void {
|
|
236
|
+
rememberRoster(players, event);
|
|
237
|
+
switch (event.type) {
|
|
238
|
+
case 'kill': {
|
|
239
|
+
const target = firstString(event, ['target_name', 'target']);
|
|
240
|
+
const targetSeat = firstNumber(event, ['target_seat', 'target_player_seat']);
|
|
241
|
+
const killer = firstString(event, ['killer_name', 'actor_name']) ?? youName;
|
|
242
|
+
markDead(players, target, targetSeat, {
|
|
243
|
+
cause: 'killed',
|
|
244
|
+
killer: killer ?? null,
|
|
245
|
+
killer_is_you: killer !== undefined && killer === youName,
|
|
246
|
+
revealed: true,
|
|
247
|
+
source: 'event_log',
|
|
248
|
+
});
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case 'killed': {
|
|
252
|
+
const target = firstString(event, ['target_name', 'victim_name']) ?? youName;
|
|
253
|
+
const targetSeat = firstNumber(event, ['target_seat', 'target_player_seat']);
|
|
254
|
+
markDead(players, target, targetSeat, {
|
|
255
|
+
cause: 'killed',
|
|
256
|
+
killer: firstString(event, ['killer_name']) ?? null,
|
|
257
|
+
revealed: true,
|
|
258
|
+
source: 'event_log',
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 'murder_witnessed': {
|
|
263
|
+
const killer = firstString(event, ['killer_name']);
|
|
264
|
+
markDead(players, firstString(event, ['target_name']), firstNumber(event, ['target_seat', 'target_player_seat']), {
|
|
265
|
+
cause: 'killed',
|
|
266
|
+
killer: killer ?? null,
|
|
267
|
+
killer_is_you: killer !== undefined && killer === youName,
|
|
268
|
+
revealed: true,
|
|
269
|
+
source: 'event_log',
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'corpse_spotted':
|
|
274
|
+
case 'body_dropped': {
|
|
275
|
+
markDead(players, firstString(event, ['corpse_name', 'target_name', 'name']), firstNumber(event, ['corpse_seat', 'corpse_player_seat', 'target_seat']), {
|
|
276
|
+
cause: 'killed',
|
|
277
|
+
killer: null,
|
|
278
|
+
revealed: true,
|
|
279
|
+
source: event.type,
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case 'meeting_briefing': {
|
|
284
|
+
const corpses = Array.isArray(event.reported_corpses) ? event.reported_corpses : [];
|
|
285
|
+
for (const corpse of corpses) {
|
|
286
|
+
if (!corpse || typeof corpse !== 'object') continue;
|
|
287
|
+
markDead(players, stringValue(corpse.name ?? corpse.agent_name), numberValue(corpse.seat), {
|
|
288
|
+
cause: 'killed',
|
|
289
|
+
killer: null,
|
|
290
|
+
revealed: true,
|
|
291
|
+
source: 'meeting',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
markDead(players, firstString(event, ['corpse_name']), firstNumber(event, ['corpse_seat', 'corpse_player_seat']), {
|
|
295
|
+
cause: 'killed',
|
|
296
|
+
killer: null,
|
|
297
|
+
revealed: true,
|
|
298
|
+
source: 'meeting',
|
|
299
|
+
});
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case 'exile':
|
|
303
|
+
markDead(players, firstString(event, ['result_target', 'actor_name']), firstNumber(event, ['result_target_seat', 'actor_seat', 'seat']), {
|
|
304
|
+
cause: 'exiled',
|
|
305
|
+
killer: null,
|
|
306
|
+
revealed: true,
|
|
307
|
+
source: 'meeting',
|
|
308
|
+
});
|
|
309
|
+
break;
|
|
310
|
+
case 'meeting_ended':
|
|
311
|
+
if (event.result === 'exiled') {
|
|
312
|
+
markDead(players, firstString(event, ['result_target', 'actor_name']), firstNumber(event, ['result_target_seat', 'actor_seat', 'seat']), {
|
|
313
|
+
cause: 'exiled',
|
|
314
|
+
killer: null,
|
|
315
|
+
revealed: true,
|
|
316
|
+
source: 'meeting',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
case 'warrior_shrimp_self_destruct': {
|
|
321
|
+
const actor = firstString(event, ['actor_name', 'self_name']);
|
|
322
|
+
markDead(players, actor, firstNumber(event, ['actor_seat', 'seat']), {
|
|
323
|
+
cause: 'self_destruct',
|
|
324
|
+
killer: null,
|
|
325
|
+
revealed: true,
|
|
326
|
+
source: 'event_log',
|
|
327
|
+
});
|
|
328
|
+
markDead(players, firstString(event, ['target_name']), firstNumber(event, ['target_seat', 'target_player_seat']), {
|
|
329
|
+
cause: 'killed',
|
|
330
|
+
killer: actor ?? null,
|
|
331
|
+
killer_is_you: actor !== undefined && actor === youName,
|
|
332
|
+
revealed: true,
|
|
333
|
+
source: 'event_log',
|
|
334
|
+
});
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case 'meeting_state':
|
|
338
|
+
applyMeetingAliveList(players, event);
|
|
339
|
+
break;
|
|
340
|
+
default:
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function projectPlayers(input: {
|
|
346
|
+
summary?: any | null;
|
|
347
|
+
events?: Array<Record<string, any>>;
|
|
348
|
+
}): PlayerProjection[] {
|
|
349
|
+
const players = new Map<string, MutablePlayerProjection>();
|
|
350
|
+
const youName = stringValue(input.summary?.you?.name);
|
|
351
|
+
|
|
352
|
+
rememberRoster(players, input.summary);
|
|
353
|
+
for (const event of input.events ?? []) applyEvent(players, event, youName);
|
|
354
|
+
applyVisibleState(players, input.summary);
|
|
355
|
+
|
|
356
|
+
return [...players.values()]
|
|
357
|
+
.filter((player) => player.name && player.name !== youName)
|
|
358
|
+
.sort((a, b) => {
|
|
359
|
+
if (a.seat !== undefined && b.seat !== undefined) return a.seat - b.seat;
|
|
360
|
+
if (a.seat !== undefined) return -1;
|
|
361
|
+
if (b.seat !== undefined) return 1;
|
|
362
|
+
return a.name.localeCompare(b.name);
|
|
363
|
+
})
|
|
364
|
+
.map((player) => cleanObject({
|
|
365
|
+
name: player.name,
|
|
366
|
+
seat: player.seat,
|
|
367
|
+
status: player.status,
|
|
368
|
+
death: player.death,
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
vi.mock('../commands/upgrade.js', () => ({
|
|
4
|
-
runDefaultUpgrade: vi.fn(),
|
|
5
|
-
}));
|
|
6
|
-
|
|
7
|
-
import { runDefaultUpgrade } from '../commands/upgrade.js';
|
|
8
|
-
import { runAutoUpgradeOnLoad, shouldSkipAutoUpgrade } from './auto-upgrade.js';
|
|
9
|
-
|
|
10
|
-
describe('runAutoUpgradeOnLoad', () => {
|
|
11
|
-
const prev = process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
vi.mocked(runDefaultUpgrade).mockReset();
|
|
15
|
-
delete process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
if (prev === undefined) delete process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
20
|
-
else process.env.CLAWCLAW_SKIP_AUTO_UPGRADE = prev;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('skips when CLAWCLAW_SKIP_AUTO_UPGRADE=1', () => {
|
|
24
|
-
process.env.CLAWCLAW_SKIP_AUTO_UPGRADE = '1';
|
|
25
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
26
|
-
expect(runDefaultUpgrade).not.toHaveBeenCalled();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('skips prerelease/test package versions', () => {
|
|
30
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25-test' });
|
|
31
|
-
expect(runDefaultUpgrade).not.toHaveBeenCalled();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('runs default upgrade when not skipped', () => {
|
|
35
|
-
vi.mocked(runDefaultUpgrade).mockReturnValue({ version: '0.4.8' });
|
|
36
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
37
|
-
expect(runDefaultUpgrade).toHaveBeenCalledOnce();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('does not write upgrade progress to stdout', () => {
|
|
41
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
42
|
-
vi.mocked(runDefaultUpgrade).mockReturnValue({ version: '0.4.8' });
|
|
43
|
-
try {
|
|
44
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
45
|
-
expect(logSpy).not.toHaveBeenCalled();
|
|
46
|
-
} finally {
|
|
47
|
-
logSpy.mockRestore();
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('notifies on upgrade success and failure', () => {
|
|
52
|
-
const notify = vi.fn();
|
|
53
|
-
vi.mocked(runDefaultUpgrade).mockReturnValueOnce({ version: '0.4.8' });
|
|
54
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25', notify });
|
|
55
|
-
expect(notify).toHaveBeenCalledWith('[clawclaw-cli] auto-upgrade installed 0.4.8');
|
|
56
|
-
|
|
57
|
-
vi.mocked(runDefaultUpgrade).mockReturnValueOnce({ error: 'npm install failed: EACCES' });
|
|
58
|
-
runAutoUpgradeOnLoad({ packageVersion: '0.5.25', notify });
|
|
59
|
-
expect(notify).toHaveBeenCalledWith('[clawclaw-cli] auto-upgrade failed: npm install failed: EACCES');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('treats prerelease versions as skip-worthy', () => {
|
|
63
|
-
expect(shouldSkipAutoUpgrade('0.5.25-test')).toBe(true);
|
|
64
|
-
expect(shouldSkipAutoUpgrade('0.5.25')).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('../commands/upgrade.js', () => ({
|
|
4
|
+
runDefaultUpgrade: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
import { runDefaultUpgrade } from '../commands/upgrade.js';
|
|
8
|
+
import { runAutoUpgradeOnLoad, shouldSkipAutoUpgrade } from './auto-upgrade.js';
|
|
9
|
+
|
|
10
|
+
describe('runAutoUpgradeOnLoad', () => {
|
|
11
|
+
const prev = process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.mocked(runDefaultUpgrade).mockReset();
|
|
15
|
+
delete process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
if (prev === undefined) delete process.env.CLAWCLAW_SKIP_AUTO_UPGRADE;
|
|
20
|
+
else process.env.CLAWCLAW_SKIP_AUTO_UPGRADE = prev;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('skips when CLAWCLAW_SKIP_AUTO_UPGRADE=1', () => {
|
|
24
|
+
process.env.CLAWCLAW_SKIP_AUTO_UPGRADE = '1';
|
|
25
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
26
|
+
expect(runDefaultUpgrade).not.toHaveBeenCalled();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('skips prerelease/test package versions', () => {
|
|
30
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25-test' });
|
|
31
|
+
expect(runDefaultUpgrade).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('runs default upgrade when not skipped', () => {
|
|
35
|
+
vi.mocked(runDefaultUpgrade).mockReturnValue({ version: '0.4.8' });
|
|
36
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
37
|
+
expect(runDefaultUpgrade).toHaveBeenCalledOnce();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('does not write upgrade progress to stdout', () => {
|
|
41
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
42
|
+
vi.mocked(runDefaultUpgrade).mockReturnValue({ version: '0.4.8' });
|
|
43
|
+
try {
|
|
44
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25' });
|
|
45
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
46
|
+
} finally {
|
|
47
|
+
logSpy.mockRestore();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('notifies on upgrade success and failure', () => {
|
|
52
|
+
const notify = vi.fn();
|
|
53
|
+
vi.mocked(runDefaultUpgrade).mockReturnValueOnce({ version: '0.4.8' });
|
|
54
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25', notify });
|
|
55
|
+
expect(notify).toHaveBeenCalledWith('[clawclaw-cli] auto-upgrade installed 0.4.8');
|
|
56
|
+
|
|
57
|
+
vi.mocked(runDefaultUpgrade).mockReturnValueOnce({ error: 'npm install failed: EACCES' });
|
|
58
|
+
runAutoUpgradeOnLoad({ packageVersion: '0.5.25', notify });
|
|
59
|
+
expect(notify).toHaveBeenCalledWith('[clawclaw-cli] auto-upgrade failed: npm install failed: EACCES');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('treats prerelease versions as skip-worthy', () => {
|
|
63
|
+
expect(shouldSkipAutoUpgrade('0.5.25-test')).toBe(true);
|
|
64
|
+
expect(shouldSkipAutoUpgrade('0.5.25')).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { runDefaultUpgrade } from '../commands/upgrade.js';
|
|
3
|
-
|
|
4
|
-
function readPackageVersion(): string | null {
|
|
5
|
-
try {
|
|
6
|
-
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
7
|
-
return typeof pkg?.version === 'string' ? pkg.version : null;
|
|
8
|
-
} catch {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function shouldSkipAutoUpgrade(packageVersion = readPackageVersion()): boolean {
|
|
14
|
-
if (process.env.CLAWCLAW_SKIP_AUTO_UPGRADE === '1') return true;
|
|
15
|
-
return Boolean(packageVersion?.includes('-'));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Run during `ccl load`, before game start and event streaming begin. */
|
|
19
|
-
export function runAutoUpgradeOnLoad(options?: { packageVersion?: string; notify?: (message: string) => void }): void {
|
|
20
|
-
if (shouldSkipAutoUpgrade(options?.packageVersion)) return;
|
|
21
|
-
|
|
22
|
-
const result = runDefaultUpgrade();
|
|
23
|
-
if (result.error) {
|
|
24
|
-
const msg = result.version ? `${result.version} (${result.error})` : result.error;
|
|
25
|
-
options?.notify?.(`[clawclaw-cli] auto-upgrade failed: ${msg}`);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (result.version) {
|
|
29
|
-
options?.notify?.(`[clawclaw-cli] auto-upgrade installed ${result.version}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { runDefaultUpgrade } from '../commands/upgrade.js';
|
|
3
|
+
|
|
4
|
+
function readPackageVersion(): string | null {
|
|
5
|
+
try {
|
|
6
|
+
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
7
|
+
return typeof pkg?.version === 'string' ? pkg.version : null;
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function shouldSkipAutoUpgrade(packageVersion = readPackageVersion()): boolean {
|
|
14
|
+
if (process.env.CLAWCLAW_SKIP_AUTO_UPGRADE === '1') return true;
|
|
15
|
+
return Boolean(packageVersion?.includes('-'));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Run during `ccl load`, before game start and event streaming begin. */
|
|
19
|
+
export function runAutoUpgradeOnLoad(options?: { packageVersion?: string; notify?: (message: string) => void }): void {
|
|
20
|
+
if (shouldSkipAutoUpgrade(options?.packageVersion)) return;
|
|
21
|
+
|
|
22
|
+
const result = runDefaultUpgrade();
|
|
23
|
+
if (result.error) {
|
|
24
|
+
const msg = result.version ? `${result.version} (${result.error})` : result.error;
|
|
25
|
+
options?.notify?.(`[clawclaw-cli] auto-upgrade failed: ${msg}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (result.version) {
|
|
29
|
+
options?.notify?.(`[clawclaw-cli] auto-upgrade installed ${result.version}`);
|
|
30
|
+
}
|
|
31
|
+
}
|