@pelotto-tech/bellwether 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +229 -2
  3. package/dist/agents/claude.d.ts +8 -0
  4. package/dist/agents/claude.js +63 -0
  5. package/dist/agents/claude.js.map +1 -0
  6. package/dist/agents/copilot.d.ts +7 -0
  7. package/dist/agents/copilot.js +39 -0
  8. package/dist/agents/copilot.js.map +1 -0
  9. package/dist/agents/index.d.ts +5 -0
  10. package/dist/agents/index.js +8 -0
  11. package/dist/agents/index.js.map +1 -0
  12. package/dist/agents/paths.d.ts +2 -0
  13. package/dist/agents/paths.js +5 -0
  14. package/dist/agents/paths.js.map +1 -0
  15. package/dist/agents/types.d.ts +29 -0
  16. package/dist/agents/types.js +2 -0
  17. package/dist/agents/types.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +4 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/install.d.ts +38 -0
  22. package/dist/install.js +104 -0
  23. package/dist/install.js.map +1 -0
  24. package/dist/json-file.d.ts +12 -0
  25. package/dist/json-file.js +33 -0
  26. package/dist/json-file.js.map +1 -0
  27. package/dist/main.d.ts +2 -0
  28. package/dist/main.js +75 -0
  29. package/dist/main.js.map +1 -0
  30. package/dist/roles.d.ts +3 -0
  31. package/dist/roles.js +13 -0
  32. package/dist/roles.js.map +1 -0
  33. package/dist/run.d.ts +9 -0
  34. package/dist/run.js +41 -0
  35. package/dist/run.js.map +1 -0
  36. package/dist/skill.d.ts +9 -0
  37. package/dist/skill.js +65 -0
  38. package/dist/skill.js.map +1 -0
  39. package/package.json +52 -3
  40. package/runtime/adapter/dist/actions.d.ts +15 -0
  41. package/runtime/adapter/dist/actions.js +7 -0
  42. package/runtime/adapter/dist/actions.js.map +1 -0
  43. package/runtime/adapter/dist/bin.d.ts +2 -0
  44. package/runtime/adapter/dist/bin.js +121 -0
  45. package/runtime/adapter/dist/bin.js.map +1 -0
  46. package/runtime/adapter/dist/config.d.ts +41 -0
  47. package/runtime/adapter/dist/config.js +71 -0
  48. package/runtime/adapter/dist/config.js.map +1 -0
  49. package/runtime/adapter/dist/core.d.ts +8 -0
  50. package/runtime/adapter/dist/core.js +10 -0
  51. package/runtime/adapter/dist/core.js.map +1 -0
  52. package/runtime/adapter/dist/herdr/connection.d.ts +34 -0
  53. package/runtime/adapter/dist/herdr/connection.js +137 -0
  54. package/runtime/adapter/dist/herdr/connection.js.map +1 -0
  55. package/runtime/adapter/dist/herdr/index.d.ts +5 -0
  56. package/runtime/adapter/dist/herdr/index.js +5 -0
  57. package/runtime/adapter/dist/herdr/index.js.map +1 -0
  58. package/runtime/adapter/dist/herdr/mapper.d.ts +27 -0
  59. package/runtime/adapter/dist/herdr/mapper.js +141 -0
  60. package/runtime/adapter/dist/herdr/mapper.js.map +1 -0
  61. package/runtime/adapter/dist/herdr/paths.d.ts +29 -0
  62. package/runtime/adapter/dist/herdr/paths.js +89 -0
  63. package/runtime/adapter/dist/herdr/paths.js.map +1 -0
  64. package/runtime/adapter/dist/herdr/protocol.d.ts +102 -0
  65. package/runtime/adapter/dist/herdr/protocol.js +19 -0
  66. package/runtime/adapter/dist/herdr/protocol.js.map +1 -0
  67. package/runtime/adapter/dist/herdr/source.d.ts +99 -0
  68. package/runtime/adapter/dist/herdr/source.js +316 -0
  69. package/runtime/adapter/dist/herdr/source.js.map +1 -0
  70. package/runtime/adapter/dist/index.d.ts +6 -0
  71. package/runtime/adapter/dist/index.js +7 -0
  72. package/runtime/adapter/dist/index.js.map +1 -0
  73. package/runtime/adapter/dist/mock.d.ts +33 -0
  74. package/runtime/adapter/dist/mock.js +130 -0
  75. package/runtime/adapter/dist/mock.js.map +1 -0
  76. package/runtime/adapter/dist/random.d.ts +3 -0
  77. package/runtime/adapter/dist/random.js +18 -0
  78. package/runtime/adapter/dist/random.js.map +1 -0
  79. package/runtime/adapter/dist/recording.d.ts +22 -0
  80. package/runtime/adapter/dist/recording.js +52 -0
  81. package/runtime/adapter/dist/recording.js.map +1 -0
  82. package/runtime/adapter/dist/roles.d.ts +16 -0
  83. package/runtime/adapter/dist/roles.js +21 -0
  84. package/runtime/adapter/dist/roles.js.map +1 -0
  85. package/runtime/adapter/dist/rollup.d.ts +5 -0
  86. package/runtime/adapter/dist/rollup.js +18 -0
  87. package/runtime/adapter/dist/rollup.js.map +1 -0
  88. package/runtime/adapter/dist/scrub.d.ts +9 -0
  89. package/runtime/adapter/dist/scrub.js +56 -0
  90. package/runtime/adapter/dist/scrub.js.map +1 -0
  91. package/runtime/adapter/dist/server.d.ts +47 -0
  92. package/runtime/adapter/dist/server.js +119 -0
  93. package/runtime/adapter/dist/server.js.map +1 -0
  94. package/runtime/adapter/dist/source.d.ts +20 -0
  95. package/runtime/adapter/dist/source.js +14 -0
  96. package/runtime/adapter/dist/source.js.map +1 -0
  97. package/runtime/adapter/dist/types.d.ts +66 -0
  98. package/runtime/adapter/dist/types.js +23 -0
  99. package/runtime/adapter/dist/types.js.map +1 -0
  100. package/runtime/adapter/dist/world.d.ts +9 -0
  101. package/runtime/adapter/dist/world.js +64 -0
  102. package/runtime/adapter/dist/world.js.map +1 -0
  103. package/runtime/adapter/package.json +37 -0
  104. package/runtime/renderer/dist/assets/BitmapFont-CrMu5gVf.js +6 -0
  105. package/runtime/renderer/dist/assets/CanvasRenderer-BJUwGe2K.js +1 -0
  106. package/runtime/renderer/dist/assets/Filter-CYMWP98C.js +1 -0
  107. package/runtime/renderer/dist/assets/RenderTargetSystem-CUv6yCH8.js +184 -0
  108. package/runtime/renderer/dist/assets/WebGLRenderer-DT-aSeEc.js +156 -0
  109. package/runtime/renderer/dist/assets/WebGPURenderer-CV0poHB6.js +75 -0
  110. package/runtime/renderer/dist/assets/browserAll-BpE2Jy31.js +14 -0
  111. package/runtime/renderer/dist/assets/getTexelRangeRects-BCdgu8cV.js +195 -0
  112. package/runtime/renderer/dist/assets/index-BgIYSiaY.js +296 -0
  113. package/runtime/renderer/dist/assets/webworkerAll-DEtPMT-J.js +83 -0
  114. package/runtime/renderer/dist/index.html +36 -0
@@ -0,0 +1,21 @@
1
+ import { ROLES, isRole } from './types.js';
2
+ const MATCHABLE = ROLES.filter((r) => r !== 'generic');
3
+ function findRoleWord(text) {
4
+ if (!text)
5
+ return undefined;
6
+ const words = text.toLowerCase().split(/[^a-z]+/);
7
+ return MATCHABLE.find((role) => words.includes(role));
8
+ }
9
+ /**
10
+ * Role fallback chain from docs/PLAN.md §1b: `role` token (written by the skill
11
+ * pack or the HERDR_ROLE hook), then agent rename name, then pane title, then
12
+ * `generic`. Names and titles match when they contain a role as a whole word,
13
+ * so `reviewer-2` and `Lead architect` both resolve.
14
+ */
15
+ export function resolveRole(hints) {
16
+ const token = hints.tokens?.role?.trim().toLowerCase();
17
+ if (isRole(token))
18
+ return token;
19
+ return findRoleWord(hints.agentName) ?? findRoleWord(hints.title) ?? 'generic';
20
+ }
21
+ //# sourceMappingURL=roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.js","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAa,MAAM,YAAY,CAAC;AAWtD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AAEvD,SAAS,YAAY,CAAC,IAA+B;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAClD,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgB;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvD,IAAI,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC;AACjF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Member, Status } from './types.js';
2
+ /** Higher wins. blocked > done > working > idle > unknown (docs/PLAN.md §1). */
3
+ export declare const STATUS_PRIORITY: Record<Status, number>;
4
+ /** Party status is the worst member status. An empty party is `unknown`. */
5
+ export declare function rollup(members: Iterable<Pick<Member, 'status'>>): Status;
@@ -0,0 +1,18 @@
1
+ /** Higher wins. blocked > done > working > idle > unknown (docs/PLAN.md §1). */
2
+ export const STATUS_PRIORITY = {
3
+ blocked: 4,
4
+ done: 3,
5
+ working: 2,
6
+ idle: 1,
7
+ unknown: 0,
8
+ };
9
+ /** Party status is the worst member status. An empty party is `unknown`. */
10
+ export function rollup(members) {
11
+ let worst = 'unknown';
12
+ for (const m of members) {
13
+ if (STATUS_PRIORITY[m.status] > STATUS_PRIORITY[worst])
14
+ worst = m.status;
15
+ }
16
+ return worst;
17
+ }
18
+ //# sourceMappingURL=rollup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.js","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,4EAA4E;AAC5E,MAAM,UAAU,MAAM,CAAC,OAAyC;IAC9D,IAAI,KAAK,GAAW,SAAS,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC;YAAE,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Delta, Party } from './types.js';
2
+ /** Short stable sha256 prefix, so the same input always maps to the same label. */
3
+ export declare function hashLabel(value: string, prefix?: string): string;
4
+ /** Keeps ids, role, agent and status. Hashes label, repo, branch and session; drops free-form tokens except `role`. */
5
+ export declare function scrubParty(p: Party): Party;
6
+ /** Privacy scrub for one delta (docs/PLAN.md section 4.2). Returns a new delta; the input is untouched. */
7
+ export declare function scrubDelta(d: Delta): Delta;
8
+ /** Scrub an existing recording file into a new one. Returns the delta count. */
9
+ export declare function scrubFile(input: string, output: string): number;
@@ -0,0 +1,56 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { parseRecording } from './recording.js';
4
+ /** Short stable sha256 prefix, so the same input always maps to the same label. */
5
+ export function hashLabel(value, prefix = '') {
6
+ return prefix + createHash('sha256').update(value).digest('hex').slice(0, 8);
7
+ }
8
+ function scrubMember(m) {
9
+ const { title: _title, say: _say, ...rest } = m;
10
+ return rest;
11
+ }
12
+ /** Keeps ids, role, agent and status. Hashes label, repo, branch and session; drops free-form tokens except `role`. */
13
+ export function scrubParty(p) {
14
+ const out = {
15
+ ...p,
16
+ label: hashLabel(p.label, 'ws-'),
17
+ tokens: p.tokens?.role !== undefined ? { role: p.tokens.role } : {},
18
+ members: Object.fromEntries(Object.entries(p.members).map(([k, m]) => [k, scrubMember(m)])),
19
+ };
20
+ if (p.repo !== undefined)
21
+ out.repo = hashLabel(p.repo, 'repo-');
22
+ if (p.branch !== undefined)
23
+ out.branch = hashLabel(p.branch, 'br-');
24
+ if (p.session !== undefined)
25
+ out.session = hashLabel(p.session, 's-');
26
+ return out;
27
+ }
28
+ function scrubWorld(w) {
29
+ return {
30
+ ...w,
31
+ machine: hashLabel(w.machine, 'm-'),
32
+ parties: Object.fromEntries(Object.entries(w.parties).map(([k, p]) => [k, scrubParty(p)])),
33
+ };
34
+ }
35
+ /** Privacy scrub for one delta (docs/PLAN.md section 4.2). Returns a new delta; the input is untouched. */
36
+ export function scrubDelta(d) {
37
+ switch (d.kind) {
38
+ case 'snapshot':
39
+ return { ...d, world: scrubWorld(d.world) };
40
+ case 'member':
41
+ return { ...d, member: scrubMember(d.member) };
42
+ case 'party':
43
+ return { ...d, party: scrubParty(d.party) };
44
+ case 'say':
45
+ return { ...d, text: '' };
46
+ case 'removed':
47
+ return d;
48
+ }
49
+ }
50
+ /** Scrub an existing recording file into a new one. Returns the delta count. */
51
+ export function scrubFile(input, output) {
52
+ const deltas = parseRecording(readFileSync(input, 'utf8')).map(scrubDelta);
53
+ writeFileSync(output, deltas.map((d) => JSON.stringify(d) + '\n').join(''));
54
+ return deltas.length;
55
+ }
56
+ //# sourceMappingURL=scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.js","sourceRoot":"","sources":["../src/scrub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { WorldSource } from './source.js';
2
+ import type { Delta, World } from './types.js';
3
+ export declare const DEFAULT_PORT = 7341;
4
+ export declare const DELTA_PATH = "/deltas";
5
+ export interface DeltaServerOptions {
6
+ port?: number;
7
+ host?: string;
8
+ /** Optional directory to serve over HTTP (the built renderer), so one process runs the overlay. */
9
+ staticDir?: string;
10
+ /** Served as `/config.json` so the renderer can pick the configured skin pack. */
11
+ clientConfig?: Record<string, unknown>;
12
+ /** Also accept `prompt` and `sendKeys` client messages. Off by default; `focus` is always accepted. */
13
+ actions?: boolean;
14
+ log?: (msg: string) => void;
15
+ }
16
+ /** Client to server messages on the delta socket. */
17
+ export type ClientMessage = {
18
+ type: 'focus';
19
+ paneId: string;
20
+ } | {
21
+ type: 'prompt';
22
+ paneId: string;
23
+ text: string;
24
+ } | {
25
+ type: 'sendKeys';
26
+ paneId: string;
27
+ keys: string[];
28
+ };
29
+ /**
30
+ * Broadcasts every Delta from a source to WebSocket clients on `/deltas`.
31
+ * A client that connects late first receives a snapshot of the current world.
32
+ */
33
+ export declare class DeltaServer {
34
+ private opts;
35
+ private world;
36
+ private http;
37
+ private wss;
38
+ private clients;
39
+ private action?;
40
+ constructor(opts?: DeltaServerOptions);
41
+ get currentWorld(): World;
42
+ attach(source: WorldSource): void;
43
+ private onClientMessage;
44
+ publish(delta: Delta): void;
45
+ listen(): Promise<number>;
46
+ close(): Promise<void>;
47
+ }
@@ -0,0 +1,119 @@
1
+ import { createReadStream, statSync } from 'node:fs';
2
+ import { createServer } from 'node:http';
3
+ import { extname, join, normalize, resolve, sep } from 'node:path';
4
+ import { WebSocketServer } from 'ws';
5
+ import { applyDelta, emptyWorld, snapshotDelta } from './world.js';
6
+ export const DEFAULT_PORT = 7341;
7
+ export const DELTA_PATH = '/deltas';
8
+ const MIME = {
9
+ '.html': 'text/html; charset=utf-8',
10
+ '.js': 'text/javascript; charset=utf-8',
11
+ '.css': 'text/css; charset=utf-8',
12
+ '.json': 'application/json; charset=utf-8',
13
+ '.png': 'image/png',
14
+ '.svg': 'image/svg+xml',
15
+ };
16
+ /**
17
+ * Broadcasts every Delta from a source to WebSocket clients on `/deltas`.
18
+ * A client that connects late first receives a snapshot of the current world.
19
+ */
20
+ export class DeltaServer {
21
+ opts;
22
+ world = emptyWorld();
23
+ http;
24
+ wss;
25
+ clients = new Set();
26
+ action;
27
+ constructor(opts = {}) {
28
+ this.opts = opts;
29
+ this.http = createServer((req, res) => {
30
+ if ((req.url ?? '').split('?')[0] === '/config.json' && this.opts.clientConfig) {
31
+ res.writeHead(200, { 'content-type': MIME['.json'], 'cache-control': 'no-store' });
32
+ res.end(JSON.stringify(this.opts.clientConfig));
33
+ return;
34
+ }
35
+ const dir = this.opts.staticDir;
36
+ if (!dir) {
37
+ res.writeHead(404).end();
38
+ return;
39
+ }
40
+ const root = resolve(dir);
41
+ const urlPath = decodeURIComponent((req.url ?? '/').split('?')[0] ?? '/');
42
+ let file = normalize(join(root, urlPath));
43
+ if (file !== root && !file.startsWith(root + sep)) {
44
+ res.writeHead(403).end();
45
+ return;
46
+ }
47
+ try {
48
+ if (statSync(file).isDirectory())
49
+ file = join(file, 'index.html');
50
+ statSync(file);
51
+ }
52
+ catch {
53
+ res.writeHead(404).end();
54
+ return;
55
+ }
56
+ res.writeHead(200, { 'content-type': MIME[extname(file)] ?? 'application/octet-stream' });
57
+ createReadStream(file).pipe(res);
58
+ });
59
+ this.wss = new WebSocketServer({ server: this.http, path: DELTA_PATH });
60
+ this.wss.on('connection', (ws) => {
61
+ this.clients.add(ws);
62
+ ws.on('close', () => this.clients.delete(ws));
63
+ ws.on('message', (data) => this.onClientMessage(String(data)));
64
+ ws.send(JSON.stringify(snapshotDelta(this.world)));
65
+ });
66
+ }
67
+ get currentWorld() {
68
+ return this.world;
69
+ }
70
+ attach(source) {
71
+ this.action = source.actions;
72
+ source.on('delta', (d) => this.publish(d));
73
+ }
74
+ onClientMessage(raw) {
75
+ const action = this.action;
76
+ let msg;
77
+ try {
78
+ msg = JSON.parse(raw);
79
+ }
80
+ catch {
81
+ return;
82
+ }
83
+ if (!action || typeof msg.paneId !== 'string' || !msg.paneId)
84
+ return;
85
+ let run;
86
+ if (msg.type === 'focus')
87
+ run = action.focus(msg.paneId);
88
+ else if (this.opts.actions && msg.type === 'prompt' && typeof msg.text === 'string')
89
+ run = action.prompt(msg.paneId, msg.text);
90
+ else if (this.opts.actions && msg.type === 'sendKeys' && Array.isArray(msg.keys) && msg.keys.every((k) => typeof k === 'string')) {
91
+ run = action.sendKeys(msg.paneId, msg.keys);
92
+ }
93
+ // Rejections (pane gone, Herdr down) are logged, never sent to the client.
94
+ Promise.resolve(run).catch((err) => this.opts.log?.(`action ${String(msg.type)} failed: ${err.message}`));
95
+ }
96
+ publish(delta) {
97
+ this.world = applyDelta(this.world, delta);
98
+ const line = JSON.stringify(delta);
99
+ for (const ws of this.clients) {
100
+ if (ws.readyState === ws.OPEN)
101
+ ws.send(line);
102
+ }
103
+ }
104
+ listen() {
105
+ return new Promise((resolvePort, reject) => {
106
+ this.http.once('error', reject);
107
+ this.http.listen(this.opts.port ?? DEFAULT_PORT, this.opts.host ?? '127.0.0.1', () => {
108
+ const addr = this.http.address();
109
+ resolvePort(typeof addr === 'object' && addr ? addr.port : (this.opts.port ?? DEFAULT_PORT));
110
+ });
111
+ });
112
+ }
113
+ close() {
114
+ for (const ws of this.clients)
115
+ ws.terminate();
116
+ return new Promise((res) => this.wss.close(() => this.http.close(() => res())));
117
+ }
118
+ }
119
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type Action } from './actions.js';
2
+ import type { Delta } from './types.js';
3
+ export type DeltaListener = (delta: Delta) => void;
4
+ /** Anything that produces a Delta stream: the mock, a replay, or the live Herdr client. */
5
+ export interface WorldSource {
6
+ start(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ on(event: 'delta', fn: DeltaListener): void;
9
+ /** Actions back to the world. Mock and replay sources no-op. */
10
+ readonly actions: Action;
11
+ }
12
+ /** Small base class so sources share listener bookkeeping without Node's EventEmitter. */
13
+ export declare abstract class BaseSource implements WorldSource {
14
+ private listeners;
15
+ readonly actions: Action;
16
+ on(_event: 'delta', fn: DeltaListener): void;
17
+ protected emit(delta: Delta): void;
18
+ abstract start(): Promise<void>;
19
+ abstract stop(): Promise<void>;
20
+ }
@@ -0,0 +1,14 @@
1
+ import { NOOP_ACTION } from './actions.js';
2
+ /** Small base class so sources share listener bookkeeping without Node's EventEmitter. */
3
+ export class BaseSource {
4
+ listeners = [];
5
+ actions = NOOP_ACTION;
6
+ on(_event, fn) {
7
+ this.listeners.push(fn);
8
+ }
9
+ emit(delta) {
10
+ for (const fn of this.listeners)
11
+ fn(delta);
12
+ }
13
+ }
14
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAcxD,0FAA0F;AAC1F,MAAM,OAAgB,UAAU;IACtB,SAAS,GAAoB,EAAE,CAAC;IAC/B,OAAO,GAAW,WAAW,CAAC;IAEvC,EAAE,CAAC,MAAe,EAAE,EAAiB;QACnC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAES,IAAI,CAAC,KAAY;QACzB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS;YAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;CAIF"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The world model the adapter emits. Mirrors docs/PLAN.md §2 with the generic
3
+ * vocabulary: a Herdr workspace is a `Party`, a pane with an agent is a `Member`.
4
+ */
5
+ export declare const STATUSES: readonly ["working", "done", "blocked", "idle", "unknown"];
6
+ export type Status = (typeof STATUSES)[number];
7
+ export declare const ROLES: readonly ["orchestrator", "coordinator", "architect", "implementer", "reviewer", "researcher", "writer", "ops", "generic"];
8
+ export type Role = (typeof ROLES)[number];
9
+ export interface World {
10
+ /** 'local' or a saved-machine label. */
11
+ machine: string;
12
+ parties: Record<string, Party>;
13
+ }
14
+ /** One Herdr workspace. */
15
+ export interface Party {
16
+ /** Herdr workspace_id. */
17
+ id: string;
18
+ label: string;
19
+ /** Herdr session the workspace lives in ('default' or a named session). */
20
+ session?: string;
21
+ repo?: string;
22
+ branch?: string;
23
+ isWorktree: boolean;
24
+ /** Rollup of member statuses; see `rollup()`. */
25
+ status: Status;
26
+ members: Record<string, Member>;
27
+ tokens: Record<string, string>;
28
+ }
29
+ /** One pane running an agent. */
30
+ export interface Member {
31
+ paneId: string;
32
+ /** Herdr's agent field: 'claude', 'copilot', 'codex', ... Never branched on outside the installer. */
33
+ agent: string;
34
+ role: Role;
35
+ status: Status;
36
+ title?: string;
37
+ say?: string;
38
+ lastChange: number;
39
+ }
40
+ export type Delta = {
41
+ t: number;
42
+ kind: 'snapshot';
43
+ world: World;
44
+ } | {
45
+ t: number;
46
+ kind: 'member';
47
+ partyId: string;
48
+ member: Member;
49
+ } | {
50
+ t: number;
51
+ kind: 'party';
52
+ party: Party;
53
+ } | {
54
+ t: number;
55
+ kind: 'removed';
56
+ partyId: string;
57
+ paneId?: string;
58
+ } | {
59
+ t: number;
60
+ kind: 'say';
61
+ partyId: string;
62
+ paneId: string;
63
+ text: string;
64
+ };
65
+ export declare function isStatus(value: unknown): value is Status;
66
+ export declare function isRole(value: unknown): value is Role;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The world model the adapter emits. Mirrors docs/PLAN.md §2 with the generic
3
+ * vocabulary: a Herdr workspace is a `Party`, a pane with an agent is a `Member`.
4
+ */
5
+ export const STATUSES = ['working', 'done', 'blocked', 'idle', 'unknown'];
6
+ export const ROLES = [
7
+ 'orchestrator',
8
+ 'coordinator',
9
+ 'architect',
10
+ 'implementer',
11
+ 'reviewer',
12
+ 'researcher',
13
+ 'writer',
14
+ 'ops',
15
+ 'generic',
16
+ ];
17
+ export function isStatus(value) {
18
+ return typeof value === 'string' && STATUSES.includes(value);
19
+ }
20
+ export function isRole(value) {
21
+ return typeof value === 'string' && ROLES.includes(value);
22
+ }
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AAGnF,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,cAAc;IACd,aAAa;IACb,WAAW;IACX,aAAa;IACb,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,KAAK;IACL,SAAS;CACD,CAAC;AA4CX,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,QAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,KAA2B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Delta, World } from './types.js';
2
+ export declare function emptyWorld(machine?: string): World;
3
+ /**
4
+ * Pure reducer: returns a new World with the delta applied. Unknown party or
5
+ * pane ids are ignored rather than thrown on, because Herdr events and snapshots
6
+ * share no sequence and can arrive slightly out of order.
7
+ */
8
+ export declare function applyDelta(world: World, delta: Delta): World;
9
+ export declare function snapshotDelta(world: World, t?: number): Delta;
@@ -0,0 +1,64 @@
1
+ import { rollup } from './rollup.js';
2
+ export function emptyWorld(machine = 'local') {
3
+ return { machine, parties: {} };
4
+ }
5
+ function withRollup(party) {
6
+ return { ...party, status: rollup(Object.values(party.members)) };
7
+ }
8
+ /**
9
+ * Pure reducer: returns a new World with the delta applied. Unknown party or
10
+ * pane ids are ignored rather than thrown on, because Herdr events and snapshots
11
+ * share no sequence and can arrive slightly out of order.
12
+ */
13
+ export function applyDelta(world, delta) {
14
+ switch (delta.kind) {
15
+ case 'snapshot':
16
+ return {
17
+ ...delta.world,
18
+ parties: Object.fromEntries(Object.entries(delta.world.parties).map(([id, p]) => [id, withRollup(p)])),
19
+ };
20
+ case 'party':
21
+ return {
22
+ ...world,
23
+ parties: { ...world.parties, [delta.party.id]: withRollup(delta.party) },
24
+ };
25
+ case 'member': {
26
+ const party = world.parties[delta.partyId];
27
+ if (!party)
28
+ return world;
29
+ const members = { ...party.members, [delta.member.paneId]: delta.member };
30
+ return {
31
+ ...world,
32
+ parties: { ...world.parties, [party.id]: withRollup({ ...party, members }) },
33
+ };
34
+ }
35
+ case 'removed': {
36
+ const party = world.parties[delta.partyId];
37
+ if (!party)
38
+ return world;
39
+ if (delta.paneId === undefined) {
40
+ const { [delta.partyId]: _gone, ...rest } = world.parties;
41
+ return { ...world, parties: rest };
42
+ }
43
+ if (!party.members[delta.paneId])
44
+ return world;
45
+ const { [delta.paneId]: _gone, ...members } = party.members;
46
+ return {
47
+ ...world,
48
+ parties: { ...world.parties, [party.id]: withRollup({ ...party, members }) },
49
+ };
50
+ }
51
+ case 'say': {
52
+ const party = world.parties[delta.partyId];
53
+ const member = party?.members[delta.paneId];
54
+ if (!party || !member)
55
+ return world;
56
+ const members = { ...party.members, [member.paneId]: { ...member, say: delta.text } };
57
+ return { ...world, parties: { ...world.parties, [party.id]: { ...party, members } } };
58
+ }
59
+ }
60
+ }
61
+ export function snapshotDelta(world, t = Date.now()) {
62
+ return { t, kind: 'snapshot', world };
63
+ }
64
+ //# sourceMappingURL=world.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.js","sourceRoot":"","sources":["../src/world.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@bellwether/adapter",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "description": "Herdr world model, delta stream, mock and live sources, WebSocket server",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./core": {
16
+ "types": "./dist/core.d.ts",
17
+ "default": "./dist/core.js"
18
+ }
19
+ },
20
+ "bin": {
21
+ "bellwether-adapter": "./dist/bin.js"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.build.json",
25
+ "typecheck": "tsc -p tsconfig.json --noEmit",
26
+ "test": "vitest run",
27
+ "dev": "tsc -p tsconfig.build.json && node dist/bin.js --mock"
28
+ },
29
+ "dependencies": {
30
+ "smol-toml": "^1.9.1",
31
+ "ws": "^8.18.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/ws": "^8.5.13",
35
+ "ajv": "^8.20.0"
36
+ }
37
+ }
@@ -0,0 +1,6 @@
1
+ import{E as U,d as z,v as b,T as M,f as L,c as R,C as A,R as P,e as j,I as G,g as E,h as q,i as I,j as J,k as V,l as Z,m as $,n as Q,o as ee,p as _,w as te,q as se}from"./index-BgIYSiaY.js";class N extends U{constructor(){super(...arguments),this.chars=Object.create(null),this.lineHeight=0,this.fontFamily="",this.fontMetrics={fontSize:0,ascent:0,descent:0},this.baseLineOffset=0,this.distanceField={type:"none",range:0},this.pages=[],this.applyFillAsTint=!0,this.baseMeasurementFontSize=100,this.baseRenderedFontSize=100}get font(){return z(b,"BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead."),this.fontFamily}get pageTextures(){return z(b,"BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."),this.pages}get size(){return z(b,"BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead."),this.fontMetrics.fontSize}get distanceFieldRange(){return z(b,"BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead."),this.distanceField.range}get distanceFieldType(){return z(b,"BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead."),this.distanceField.type}destroy(e=!1){this.emit("destroy",this),this.removeAllListeners();for(const t in this.chars)this.chars[t].texture?.destroy();this.chars=null,e&&(this.pages.forEach(t=>t.texture.destroy(!0)),this.pages=null)}}const X=class D extends N{constructor(e){super(),this.resolution=1,this.pages=[],this._padding=0,this._measureCache=Object.create(null),this._currentChars=[],this._currentX=0,this._currentY=0,this._currentMaxCharHeight=0,this._currentPageIndex=-1,this._skipKerning=!1;const t={...D.defaultOptions,...e};this._textureSize=t.textureSize,this._mipmap=t.mipmap;const i=t.style.clone();t.overrideFill&&(i._fill.color=16777215,i._fill.alpha=1,i._fill.texture=M.WHITE,i._fill.fill=null),this.applyFillAsTint=t.overrideFill;const s=i.fontSize;i.fontSize=this.baseMeasurementFontSize;const n=L(i);t.overrideSize?(i._stroke&&(i._stroke.width*=this.baseRenderedFontSize/s),i.dropShadow&&(i.dropShadow.blur*=this.baseRenderedFontSize/s,i.dropShadow.distance*=this.baseRenderedFontSize/s)):i.fontSize=this.baseRenderedFontSize=s,this._style=i,this._skipKerning=t.skipKerning??!1,this.resolution=t.resolution??1,this._padding=t.padding??4,t.textureStyle&&(this._textureStyle=t.textureStyle instanceof R?t.textureStyle:new R(t.textureStyle)),this.fontMetrics=A.measureFont(n),this.lineHeight=i.lineHeight||this.fontMetrics.fontSize||i.fontSize}ensureCharacters(e){const t=A.graphemeSegmenter(e).filter(w=>!this._currentChars.includes(w)).filter((w,m,c)=>c.indexOf(w)===m);if(!t.length)return;this._currentChars=[...this._currentChars,...t];let i;this._currentPageIndex===-1?i=this._nextPage():i=this.pages[this._currentPageIndex];let{canvas:s,context:n}=i.canvasAndContext,a=i.texture.source;const o=this._style;let r=this._currentX,p=this._currentY,l=this._currentMaxCharHeight;const u=this.baseRenderedFontSize/this.baseMeasurementFontSize,x=(o.dropShadow?.distance??0)+(o._stroke?.width??0),y=this._padding+x;let S=!1;const C=s.width/this.resolution,k=s.height/this.resolution;for(let w=0;w<t.length;w++){const m=t[w],c=A.measureText(m,o,s,!1);c.lineHeight=c.height;const d=c.width*u,g=Math.ceil((o.fontStyle==="italic"?2:1)*d),f=c.height*u,B=g+y*2,T=f+y*2;if(S=!1,m!==`
2
+ `&&m!=="\r"&&m!==" "&&m!==" "&&(S=!0,l=Math.ceil(Math.max(T,l))),r+B>C&&(p+=l,l=T,r=0,p+l>k)){a.update();const F=this._nextPage();s=F.canvasAndContext.canvas,n=F.canvasAndContext.context,a=F.texture.source,r=0,p=0,l=0}const v=n.measureText(m).width/u;if(this.chars[m]={id:m.codePointAt(0),xOffset:-(y/u),yOffset:-(y/u),xAdvance:v,kerning:{}},S){this._drawGlyph(n,c,r+y,p+y,u,o);const F=a.width*u,O=a.height*u,H=new P(r/F*a.width,p/O*a.height,B/F*a.width,T/O*a.height);this.chars[m].texture=new M({source:a,frame:H}),r+=Math.ceil(B)}}a.update(),this._currentX=r,this._currentY=p,this._currentMaxCharHeight=l,this._skipKerning||this._applyKerning(t,n,u)}get pageTextures(){return z(b,"BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."),this.pages}_applyKerning(e,t,i){const s=this._measureCache;for(let n=0;n<e.length;n++){const a=e[n];for(let o=0;o<this._currentChars.length;o++){const r=this._currentChars[o];let p=s[a];p||(p=s[a]=t.measureText(a).width);let l=s[r];l||(l=s[r]=t.measureText(r).width);let u=t.measureText(a+r).width,x=u-(p+l);x&&this.chars[a]&&(this.chars[a].kerning[r]=x/i),u=t.measureText(a+r).width,x=u-(p+l),x&&this.chars[r]&&(this.chars[r].kerning[a]=x/i)}}}_nextPage(){this._currentPageIndex++;const e=this.resolution,t=j.getOptimalCanvasAndContext(this._textureSize,this._textureSize,e);this._setupContext(t.context,this._style,e);const i=e*(this.baseRenderedFontSize/this.baseMeasurementFontSize),s=new M({source:new G({resource:t.canvas,resolution:i,alphaMode:"premultiply-alpha-on-upload",autoGenerateMipmaps:this._mipmap})});this._textureStyle&&(s.source.style=this._textureStyle);const n={canvasAndContext:t,texture:s};return this.pages[this._currentPageIndex]=n,n}_setupContext(e,t,i){t.fontSize=this.baseRenderedFontSize,e.scale(i,i),e.font=L(t),t.fontSize=this.baseMeasurementFontSize,e.textBaseline=t.textBaseline;const s=t._stroke,n=s?.width??0;if(s&&(e.lineWidth=n,e.lineJoin=s.join,e.miterLimit=s.miterLimit,e.strokeStyle=E(s,e)),t._fill&&(e.fillStyle=E(t._fill,e)),t.dropShadow){const a=t.dropShadow,o=q.shared.setValue(a.color).toArray(),r=a.blur*i,p=a.distance*i;e.shadowColor=`rgba(${o[0]*255},${o[1]*255},${o[2]*255},${a.alpha})`,e.shadowBlur=r,e.shadowOffsetX=Math.cos(a.angle)*p,e.shadowOffsetY=Math.sin(a.angle)*p}else e.shadowColor="black",e.shadowBlur=0,e.shadowOffsetX=0,e.shadowOffsetY=0}_drawGlyph(e,t,i,s,n,a){const o=t.text,r=t.fontProperties,l=(a._stroke?.width??0)*n,u=i+l/2,x=s-l/2,y=r.descent*n,S=t.lineHeight*n;let C=!1;a.stroke&&l&&(C=!0,e.strokeText(o,u,x+S-y));const{shadowBlur:k,shadowOffsetX:w,shadowOffsetY:m}=e;a._fill&&(C&&(e.shadowBlur=0,e.shadowOffsetX=0,e.shadowOffsetY=0),e.fillText(o,u,x+S-y)),C&&(e.shadowBlur=k,e.shadowOffsetX=w,e.shadowOffsetY=m)}destroy(){super.destroy();for(let e=0;e<this.pages.length;e++){const{canvasAndContext:t,texture:i}=this.pages[e];j.returnCanvasAndContext(t),i.destroy(!0)}this.pages=null}};X.defaultOptions={textureSize:512,style:new I,mipmap:!0};let K=X;function ie(h,e,t,i){const s={width:0,height:0,offsetY:0,scale:e.fontSize/t.baseMeasurementFontSize,lines:[{width:0,charPositions:[],spaceWidth:0,spacesIndex:[],chars:[]}]};s.offsetY=t.baseLineOffset;let n=s.lines[0],a=null,o=!0;const r={width:0,start:0,index:0,positions:[],chars:[]},p=t.baseMeasurementFontSize/e.fontSize,l=e.letterSpacing*p,u=e.wordWrapWidth*p,x=e.lineHeight?e.lineHeight*p:t.lineHeight,y=e.wordWrap&&e.breakWords,S=J(e.whiteSpace),C=V(e.whiteSpace);if(S||C){const c=[];let d=S;for(let g=0;g<h.length;g++){let f=h[g];if(f==="\r"||f===`
3
+ `)if(C)f==="\r"&&h[g+1]===`
4
+ `&&g++,f=" ";else{S&&(d=!0),c.push(f);continue}if(Z(f))if(S&&$(f)){if(d)continue;d=!0,c.push(" ")}else d=!1,c.push(f);else d=!1,c.push(f)}h=c}const k=c=>{const d=n.width;for(let g=0;g<r.index;g++){const f=c.positions[g];n.chars.push(c.chars[g]),n.charPositions.push(f+d)}n.width+=c.width,(r.index>0||!S)&&(o=!1),r.width=0,r.index=0,r.chars.length=0},w=()=>{let c=n.chars.length-1;if(i){let d=n.chars[c];for(;$(d);)n.width-=t.chars[d].xAdvance,n.spacesIndex.pop(),d=n.chars[--c]}s.width=Math.max(s.width,n.width),n={width:0,charPositions:[],chars:[],spaceWidth:0,spacesIndex:[]},o=!0,s.lines.push(n),s.height+=x},m=c=>c-l>u;for(let c=0;c<h.length+1;c++){let d;const g=c===h.length;g||(d=h[c]);const f=t.chars[d];if(/(?:\s)/.test(d)||d==="\r"||d===`
5
+ `||g){if(!o&&e.wordWrap&&m(n.width+r.width)?(w(),k(r),!g&&f&&n.charPositions.push(0)):(r.start=n.width,k(r),!g&&f&&n.charPositions.push(0)),d==="\r"||d===`
6
+ `)w();else if(!g&&f){const F=f.xAdvance+(f.kerning?.[a]||0)+l;n.width+=F,n.spaceWidth=F,n.spacesIndex.push(n.charPositions.length),n.chars.push(d)}}else if(f){const v=f.kerning?.[a]||0,F=f.xAdvance+v+l;y&&m(r.width+F)&&(o||w(),k(r),w()),r.positions[r.index++]=r.width+v,r.chars.push(d),r.width+=F,Q(d)&&(!o&&e.wordWrap&&m(n.width+r.width)&&w(),k(r))}a=d}return w(),e.align==="center"?ne(s):e.align==="right"?ae(s):e.align==="justify"&&re(s),s}function ne(h){for(let e=0;e<h.lines.length;e++){const t=h.lines[e],i=h.width/2-t.width/2;for(let s=0;s<t.charPositions.length;s++)t.charPositions[s]+=i}}function ae(h){for(let e=0;e<h.lines.length;e++){const t=h.lines[e],i=h.width-t.width;for(let s=0;s<t.charPositions.length;s++)t.charPositions[s]+=i}}function re(h){const e=h.width;for(let t=0;t<h.lines.length-2;t++){const i=h.lines[t];let s=0,n=i.spacesIndex[s++],a=0;const o=i.spacesIndex.length,p=(e-i.width)/o;for(let l=0;l<i.charPositions.length;l++)l===n&&(n=i.spacesIndex[s++],a+=p),i.charPositions[l]+=a}}function oe(h){if(h==="")return[];typeof h=="string"&&(h=[h]);const e=[];for(let t=0,i=h.length;t<i;t++){const s=h[t];if(Array.isArray(s)){if(s.length!==2)throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${s.length}.`);if(s[0].length===0||s[1].length===0)throw new Error("[BitmapFont]: Invalid character delimiter.");const n=s[0].charCodeAt(0),a=s[1].charCodeAt(0);if(a<n)throw new Error("[BitmapFont]: Invalid character range.");for(let o=n,r=a;o<=r;o++)e.push(String.fromCharCode(o))}else e.push(...Array.from(s))}if(e.length===0)throw new Error("[BitmapFont]: Empty set when resolving characters.");return e}let W=0;class he{constructor(){this.ALPHA=[["a","z"],["A","Z"]," "],this.NUMERIC=[["0","9"]],this.ALPHANUMERIC=[["a","z"],["A","Z"],["0","9"]," "],this.ASCII=[[" ","~"]],this.defaultOptions={chars:this.ALPHANUMERIC,resolution:1,padding:4,skipKerning:!1,textureStyle:null},this.measureCache=ee(1e3)}getFont(e,t){let i=`${t.fontFamily}-bitmap`,s=!0;if(_.has(i)){const a=_.get(i);return a.ensureCharacters?.(e),a}if(t._fill.fill&&!t._stroke?(i+=t._fill.fill.styleKey,s=!1):(t._stroke||t.dropShadow)&&(i=`${t.styleKey}-bitmap`,s=!1),i+=`-${t.fontStyle}`,i+=`-${t.fontVariant}`,i+=`-${t.fontWeight}`,!_.has(i)){const a=Object.create(t);a._lineHeight=0;const o=new K({style:a,overrideFill:s,overrideSize:!0,...this.defaultOptions});W++,W>50&&te("BitmapText",`You have dynamically created ${W} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``),o.once("destroy",()=>{W--,_.remove(i)}),_.set(i,o)}const n=_.get(i);return n.ensureCharacters?.(e),n}getLayout(e,t,i=!0){const s=this.getFont(e,t),n=`${e}-${t.styleKey}-${i}`;if(this.measureCache.has(n))return this.measureCache.get(n);const a=A.graphemeSegmenter(e),o=ie(a,t,s,i);return this.measureCache.set(n,o),o}measureText(e,t,i=!0){return this.getLayout(e,t,i)}install(...e){let t=e[0];typeof t=="string"&&(t={name:t,style:e[1],chars:e[2]?.chars,resolution:e[2]?.resolution,padding:e[2]?.padding,skipKerning:e[2]?.skipKerning},z(b,"BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})"));const i=t?.name;if(!i)throw new Error("[BitmapFontManager] Property `name` is required.");t={...this.defaultOptions,...t};const s=t.style,n=s instanceof I?s:new I(s),a=t.dynamicFill??this._canUseTintForStyle(n),o=new K({style:n,overrideFill:a,skipKerning:t.skipKerning,padding:t.padding,resolution:t.resolution,overrideSize:!1,textureStyle:t.textureStyle}),r=oe(t.chars);return o.ensureCharacters(r.join("")),_.set(`${i}-bitmap`,o),o.once("destroy",()=>_.remove(`${i}-bitmap`)),o}uninstall(e){const t=`${e}-bitmap`,i=_.get(t);i&&i.destroy()}_canUseTintForStyle(e){return!e._stroke&&(!e.dropShadow||e.dropShadow.color===0)&&!e._fill.fill&&e._fill.color===16777215}}const Y=new he;class ce extends N{constructor(e,t){super();const{textures:i,data:s}=e;Object.keys(s.pages).forEach(n=>{const a=s.pages[parseInt(n,10)],o=i[a.id];this.pages.push({texture:o})}),Object.keys(s.chars).forEach(n=>{const a=s.chars[n],{frame:o,source:r,rotate:p}=i[a.page],l=se.transformRectCoords(a,o,p,new P),u=new M({frame:l,orig:new P(0,0,a.width,a.height),source:r,rotate:p});this.chars[n]={id:n.codePointAt(0),xOffset:a.xOffset,yOffset:a.yOffset,xAdvance:a.xAdvance,kerning:a.kerning??{},texture:u}}),this.baseRenderedFontSize=s.fontSize,this.baseMeasurementFontSize=s.fontSize,this.fontMetrics={ascent:0,descent:0,fontSize:s.fontSize},this.baseLineOffset=s.baseLineOffset,this.lineHeight=s.lineHeight,this.fontFamily=s.fontFamily,this.distanceField=s.distanceField??{type:"none",range:0},this.url=t}destroy(){super.destroy();for(let e=0;e<this.pages.length;e++){const{texture:t}=this.pages[e];t.destroy(!0)}this.pages=null}static install(e){Y.install(e)}static uninstall(e){Y.uninstall(e)}}export{ce as BitmapFont};