agent-relay 4.0.2 → 4.0.4

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 (175) hide show
  1. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  2. package/bin/agent-relay-broker-darwin-x64 +0 -0
  3. package/bin/agent-relay-broker-linux-arm64 +0 -0
  4. package/bin/agent-relay-broker-linux-x64 +0 -0
  5. package/dist/index.cjs +7906 -2084
  6. package/dist/packages/sdk/src/provisioner/seeder.d.ts +17 -0
  7. package/dist/packages/sdk/src/provisioner/seeder.d.ts.map +1 -0
  8. package/dist/packages/sdk/src/provisioner/seeder.js +419 -0
  9. package/dist/packages/sdk/src/provisioner/seeder.js.map +1 -0
  10. package/dist/packages/sdk/src/provisioner/token.d.ts +38 -0
  11. package/dist/packages/sdk/src/provisioner/token.d.ts.map +1 -0
  12. package/dist/packages/sdk/src/provisioner/token.js +74 -0
  13. package/dist/packages/sdk/src/provisioner/token.js.map +1 -0
  14. package/dist/src/cli/commands/core.d.ts.map +1 -1
  15. package/dist/src/cli/commands/core.js +7 -3
  16. package/dist/src/cli/commands/core.js.map +1 -1
  17. package/dist/src/cli/commands/on/provision.d.ts.map +1 -1
  18. package/dist/src/cli/commands/on/provision.js +8 -3
  19. package/dist/src/cli/commands/on/provision.js.map +1 -1
  20. package/dist/src/cli/commands/on/start.d.ts +5 -0
  21. package/dist/src/cli/commands/on/start.d.ts.map +1 -1
  22. package/dist/src/cli/commands/on/start.js +126 -88
  23. package/dist/src/cli/commands/on/start.js.map +1 -1
  24. package/dist/src/cli/commands/on/symlink-mount.d.ts +12 -0
  25. package/dist/src/cli/commands/on/symlink-mount.d.ts.map +1 -0
  26. package/dist/src/cli/commands/on/symlink-mount.js +304 -0
  27. package/dist/src/cli/commands/on/symlink-mount.js.map +1 -0
  28. package/dist/src/cli/commands/on.d.ts.map +1 -1
  29. package/dist/src/cli/commands/on.js +3 -0
  30. package/dist/src/cli/commands/on.js.map +1 -1
  31. package/install.sh +4 -0
  32. package/package.json +9 -9
  33. package/packages/acp-bridge/package.json +2 -2
  34. package/packages/brand/package.json +1 -1
  35. package/packages/cloud/package.json +2 -2
  36. package/packages/config/package.json +1 -1
  37. package/packages/hooks/package.json +4 -4
  38. package/packages/memory/package.json +2 -2
  39. package/packages/openclaw/package.json +2 -2
  40. package/packages/policy/package.json +2 -2
  41. package/packages/sdk/dist/client.d.ts +3 -10
  42. package/packages/sdk/dist/client.d.ts.map +1 -1
  43. package/packages/sdk/dist/client.js +2 -0
  44. package/packages/sdk/dist/client.js.map +1 -1
  45. package/packages/sdk/dist/provisioner/__tests__/audit.test.d.ts +2 -0
  46. package/packages/sdk/dist/provisioner/__tests__/audit.test.d.ts.map +1 -0
  47. package/packages/sdk/dist/provisioner/__tests__/audit.test.js +45 -0
  48. package/packages/sdk/dist/provisioner/__tests__/audit.test.js.map +1 -0
  49. package/packages/sdk/dist/provisioner/__tests__/compiler.test.d.ts +2 -0
  50. package/packages/sdk/dist/provisioner/__tests__/compiler.test.d.ts.map +1 -0
  51. package/packages/sdk/dist/provisioner/__tests__/compiler.test.js +345 -0
  52. package/packages/sdk/dist/provisioner/__tests__/compiler.test.js.map +1 -0
  53. package/packages/sdk/dist/provisioner/__tests__/presets.test.d.ts +2 -0
  54. package/packages/sdk/dist/provisioner/__tests__/presets.test.d.ts.map +1 -0
  55. package/packages/sdk/dist/provisioner/__tests__/presets.test.js +23 -0
  56. package/packages/sdk/dist/provisioner/__tests__/presets.test.js.map +1 -0
  57. package/packages/sdk/dist/provisioner/__tests__/seeder.test.d.ts +2 -0
  58. package/packages/sdk/dist/provisioner/__tests__/seeder.test.d.ts.map +1 -0
  59. package/packages/sdk/dist/provisioner/__tests__/seeder.test.js +224 -0
  60. package/packages/sdk/dist/provisioner/__tests__/seeder.test.js.map +1 -0
  61. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.d.ts +2 -0
  62. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.d.ts.map +1 -0
  63. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.js +191 -0
  64. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.js.map +1 -0
  65. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.d.ts +2 -0
  66. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.d.ts.map +1 -0
  67. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js +127 -0
  68. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js.map +1 -0
  69. package/packages/sdk/dist/provisioner/__tests__/token.test.d.ts +2 -0
  70. package/packages/sdk/dist/provisioner/__tests__/token.test.d.ts.map +1 -0
  71. package/packages/sdk/dist/provisioner/__tests__/token.test.js +44 -0
  72. package/packages/sdk/dist/provisioner/__tests__/token.test.js.map +1 -0
  73. package/packages/sdk/dist/provisioner/audit.d.ts +19 -0
  74. package/packages/sdk/dist/provisioner/audit.d.ts.map +1 -0
  75. package/packages/sdk/dist/provisioner/audit.js +74 -0
  76. package/packages/sdk/dist/provisioner/audit.js.map +1 -0
  77. package/packages/sdk/dist/provisioner/compiler.d.ts +23 -0
  78. package/packages/sdk/dist/provisioner/compiler.d.ts.map +1 -0
  79. package/packages/sdk/dist/provisioner/compiler.js +355 -0
  80. package/packages/sdk/dist/provisioner/compiler.js.map +1 -0
  81. package/packages/sdk/dist/provisioner/index.d.ts +9 -0
  82. package/packages/sdk/dist/provisioner/index.d.ts.map +1 -0
  83. package/packages/sdk/dist/provisioner/index.js +266 -0
  84. package/packages/sdk/dist/provisioner/index.js.map +1 -0
  85. package/packages/sdk/dist/provisioner/mount.d.ts +14 -0
  86. package/packages/sdk/dist/provisioner/mount.d.ts.map +1 -0
  87. package/packages/sdk/dist/provisioner/mount.js +329 -0
  88. package/packages/sdk/dist/provisioner/mount.js.map +1 -0
  89. package/packages/sdk/dist/provisioner/seeder.d.ts +17 -0
  90. package/packages/sdk/dist/provisioner/seeder.d.ts.map +1 -0
  91. package/packages/sdk/dist/provisioner/seeder.js +419 -0
  92. package/packages/sdk/dist/provisioner/seeder.js.map +1 -0
  93. package/packages/sdk/dist/provisioner/token.d.ts +38 -0
  94. package/packages/sdk/dist/provisioner/token.d.ts.map +1 -0
  95. package/packages/sdk/dist/provisioner/token.js +74 -0
  96. package/packages/sdk/dist/provisioner/token.js.map +1 -0
  97. package/packages/sdk/dist/provisioner/types.d.ts +133 -0
  98. package/packages/sdk/dist/provisioner/types.d.ts.map +1 -0
  99. package/packages/sdk/dist/provisioner/types.js +2 -0
  100. package/packages/sdk/dist/provisioner/types.js.map +1 -0
  101. package/packages/sdk/dist/relay.d.ts +6 -0
  102. package/packages/sdk/dist/relay.d.ts.map +1 -1
  103. package/packages/sdk/dist/relay.js +17 -5
  104. package/packages/sdk/dist/relay.js.map +1 -1
  105. package/packages/sdk/dist/types.d.ts +9 -0
  106. package/packages/sdk/dist/types.d.ts.map +1 -1
  107. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.d.ts +2 -0
  108. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.d.ts.map +1 -0
  109. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.js +331 -0
  110. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.js.map +1 -0
  111. package/packages/sdk/dist/workflows/__tests__/permission-types.test.d.ts +2 -0
  112. package/packages/sdk/dist/workflows/__tests__/permission-types.test.d.ts.map +1 -0
  113. package/packages/sdk/dist/workflows/__tests__/permission-types.test.js +124 -0
  114. package/packages/sdk/dist/workflows/__tests__/permission-types.test.js.map +1 -0
  115. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.d.ts +2 -0
  116. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.d.ts.map +1 -0
  117. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.js +526 -0
  118. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.js.map +1 -0
  119. package/packages/sdk/dist/workflows/dry-run-format.d.ts.map +1 -1
  120. package/packages/sdk/dist/workflows/dry-run-format.js +8 -0
  121. package/packages/sdk/dist/workflows/dry-run-format.js.map +1 -1
  122. package/packages/sdk/dist/workflows/runner.d.ts +14 -0
  123. package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
  124. package/packages/sdk/dist/workflows/runner.js +455 -6
  125. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  126. package/packages/sdk/dist/workflows/types.d.ts +190 -0
  127. package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
  128. package/packages/sdk/dist/workflows/types.js +29 -0
  129. package/packages/sdk/dist/workflows/types.js.map +1 -1
  130. package/packages/sdk/package.json +6 -2
  131. package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +123 -1
  132. package/packages/sdk/src/__tests__/provisioner-mount.test.ts +126 -0
  133. package/packages/sdk/src/__tests__/spawn-token.test.ts +41 -0
  134. package/packages/sdk/src/__tests__/workflow-runner.test.ts +77 -45
  135. package/packages/sdk/src/client.ts +4 -8
  136. package/packages/sdk/src/provisioner/__tests__/audit.test.ts +62 -0
  137. package/packages/sdk/src/provisioner/__tests__/compiler.test.ts +369 -0
  138. package/packages/sdk/src/provisioner/__tests__/presets.test.ts +25 -0
  139. package/packages/sdk/src/provisioner/__tests__/seeder.test.ts +284 -0
  140. package/packages/sdk/src/provisioner/__tests__/tar-seeder.test.ts +249 -0
  141. package/packages/sdk/src/provisioner/__tests__/token-factory.test.ts +172 -0
  142. package/packages/sdk/src/provisioner/__tests__/token.test.ts +53 -0
  143. package/packages/sdk/src/provisioner/audit.ts +104 -0
  144. package/packages/sdk/src/provisioner/compiler.ts +498 -0
  145. package/packages/sdk/src/provisioner/index.ts +332 -0
  146. package/packages/sdk/src/provisioner/mount.ts +419 -0
  147. package/packages/sdk/src/provisioner/seeder.ts +571 -0
  148. package/packages/sdk/src/provisioner/token.ts +112 -0
  149. package/packages/sdk/src/provisioner/types.ts +188 -0
  150. package/packages/sdk/src/relay.ts +31 -9
  151. package/packages/sdk/src/types.ts +9 -0
  152. package/packages/sdk/src/workflows/__tests__/e2e-permissions.test.ts +407 -0
  153. package/packages/sdk/src/workflows/__tests__/fixtures/.agentignore +2 -0
  154. package/packages/sdk/src/workflows/__tests__/fixtures/.reader.agentreadonly +2 -0
  155. package/packages/sdk/src/workflows/__tests__/fixtures/permission-test.yaml +42 -0
  156. package/packages/sdk/src/workflows/__tests__/permission-types.test.ts +154 -0
  157. package/packages/sdk/src/workflows/__tests__/permissions-integration.test.ts +649 -0
  158. package/packages/sdk/src/workflows/builtin-templates/bug-fix.yaml +13 -9
  159. package/packages/sdk/src/workflows/builtin-templates/code-review.yaml +12 -8
  160. package/packages/sdk/src/workflows/builtin-templates/competitive.yaml +11 -7
  161. package/packages/sdk/src/workflows/builtin-templates/documentation.yaml +16 -8
  162. package/packages/sdk/src/workflows/builtin-templates/feature-dev.yaml +13 -9
  163. package/packages/sdk/src/workflows/builtin-templates/refactor.yaml +13 -9
  164. package/packages/sdk/src/workflows/builtin-templates/review-loop.yaml +14 -10
  165. package/packages/sdk/src/workflows/builtin-templates/security-audit.yaml +19 -9
  166. package/packages/sdk/src/workflows/dry-run-format.ts +14 -1
  167. package/packages/sdk/src/workflows/runner.ts +559 -6
  168. package/packages/sdk/src/workflows/schema.json +204 -114
  169. package/packages/sdk/src/workflows/types.ts +266 -1
  170. package/packages/sdk/vitest.config.ts +5 -1
  171. package/packages/sdk-py/pyproject.toml +1 -1
  172. package/packages/telemetry/package.json +1 -1
  173. package/packages/trajectory/package.json +2 -2
  174. package/packages/user-directory/package.json +2 -2
  175. package/packages/utils/package.json +2 -2
@@ -0,0 +1,191 @@
1
+ import fs from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import path from 'node:path';
4
+ import * as tar from 'tar';
5
+ import { afterEach, describe, expect, it, vi } from 'vitest';
6
+ const bulkWriteMock = vi.hoisted(() => vi.fn());
7
+ const relayFileClientMock = vi.hoisted(() => vi.fn());
8
+ const execSyncMock = vi.hoisted(() => vi.fn());
9
+ vi.mock('@relayfile/sdk', () => ({
10
+ RelayFileClient: relayFileClientMock.mockImplementation(() => ({
11
+ bulkWrite: bulkWriteMock,
12
+ })),
13
+ }));
14
+ vi.mock('node:child_process', () => ({
15
+ execSync: execSyncMock,
16
+ }));
17
+ import { seedWorkspaceTar } from '../seeder.js';
18
+ const tempDirs = [];
19
+ function makeTempDir(prefix) {
20
+ const dir = fs.mkdtempSync(path.join(tmpdir(), prefix));
21
+ tempDirs.push(dir);
22
+ return dir;
23
+ }
24
+ function jsonResponse(payload, status = 200) {
25
+ return new Response(JSON.stringify(payload), {
26
+ status,
27
+ headers: { 'Content-Type': 'application/json' },
28
+ });
29
+ }
30
+ function listRelativeFiles(rootDir, currentDir = rootDir) {
31
+ const files = [];
32
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
33
+ for (const entry of entries) {
34
+ const absolutePath = path.join(currentDir, entry.name);
35
+ if (entry.isDirectory()) {
36
+ files.push(...listRelativeFiles(rootDir, absolutePath));
37
+ continue;
38
+ }
39
+ if (entry.isFile()) {
40
+ files.push(path.relative(rootDir, absolutePath).split(path.sep).join('/'));
41
+ }
42
+ }
43
+ return files.sort((left, right) => left.localeCompare(right));
44
+ }
45
+ async function extractTarballEntries(body) {
46
+ const archiveDir = makeTempDir('relay-tar-archive-');
47
+ const extractDir = makeTempDir('relay-tar-extract-');
48
+ const archivePath = path.join(archiveDir, 'seed.tar.gz');
49
+ fs.writeFileSync(archivePath, Buffer.from(body));
50
+ await tar.extract({ file: archivePath, cwd: extractDir, gzip: true });
51
+ return listRelativeFiles(extractDir);
52
+ }
53
+ afterEach(() => {
54
+ bulkWriteMock.mockReset();
55
+ relayFileClientMock.mockClear();
56
+ execSyncMock.mockReset();
57
+ vi.restoreAllMocks();
58
+ vi.unstubAllGlobals();
59
+ for (const dir of tempDirs.splice(0)) {
60
+ fs.rmSync(dir, { recursive: true, force: true });
61
+ }
62
+ });
63
+ describe('seedWorkspaceTar', () => {
64
+ it('creates and uploads a tar.gz to the import endpoint and respects excludeDirs', async () => {
65
+ const projectDir = makeTempDir('relay-seed-project-');
66
+ fs.mkdirSync(path.join(projectDir, 'src'), { recursive: true });
67
+ fs.mkdirSync(path.join(projectDir, 'ignored'), { recursive: true });
68
+ fs.mkdirSync(path.join(projectDir, 'node_modules', 'left-pad'), { recursive: true });
69
+ fs.writeFileSync(path.join(projectDir, 'src', 'hello.txt'), 'hello world\n');
70
+ fs.writeFileSync(path.join(projectDir, 'src', 'data.bin'), Buffer.from([0xff, 0x00, 0xaa]));
71
+ fs.writeFileSync(path.join(projectDir, 'ignored', 'skip.txt'), 'skip me\n');
72
+ fs.writeFileSync(path.join(projectDir, 'node_modules', 'left-pad', 'index.js'), 'module.exports = 1;\n');
73
+ fs.writeFileSync(path.join(projectDir, '.relayfile-mount-state.json'), '{}\n');
74
+ execSyncMock.mockReturnValue([
75
+ 'src/hello.txt',
76
+ 'src/data.bin',
77
+ 'ignored/skip.txt',
78
+ 'node_modules/left-pad/index.js',
79
+ '.relayfile-mount-state.json',
80
+ ].join('\0'));
81
+ const fetchMock = vi.fn().mockResolvedValue(jsonResponse({ imported: 2 }));
82
+ vi.stubGlobal('fetch', fetchMock);
83
+ const imported = await seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, [
84
+ 'ignored',
85
+ ]);
86
+ expect(imported).toBe(2);
87
+ expect(fetchMock).toHaveBeenCalledTimes(1);
88
+ const [url, init] = fetchMock.mock.calls[0];
89
+ expect(String(url)).toContain('/v1/workspaces/rw_demo/fs/import');
90
+ expect(init.method).toBe('POST');
91
+ expect(init.headers).toMatchObject({
92
+ Authorization: 'Bearer token',
93
+ 'Content-Type': 'application/gzip',
94
+ 'X-Correlation-Id': expect.stringMatching(/^seed-tar-rw_demo-/),
95
+ });
96
+ expect(init.body).toBeInstanceOf(Uint8Array);
97
+ const entries = await extractTarballEntries(init.body);
98
+ expect(entries).toEqual(expect.arrayContaining(['src/data.bin', 'src/hello.txt']));
99
+ expect(entries).not.toContain('ignored/skip.txt');
100
+ expect(entries).not.toContain('node_modules/left-pad/index.js');
101
+ expect(entries).not.toContain('.relayfile-mount-state.json');
102
+ });
103
+ it('falls back to seedWorkspace when the import endpoint returns 404', async () => {
104
+ const projectDir = makeTempDir('relay-seed-project-');
105
+ fs.mkdirSync(path.join(projectDir, 'src'), { recursive: true });
106
+ fs.writeFileSync(path.join(projectDir, 'src', 'hello.txt'), 'hello fallback\n');
107
+ execSyncMock.mockReturnValue('src/hello.txt\0');
108
+ bulkWriteMock.mockRejectedValue({ status: undefined });
109
+ const fetchMock = vi
110
+ .fn()
111
+ .mockResolvedValueOnce(new Response('missing', { status: 404 }))
112
+ .mockResolvedValueOnce(jsonResponse({ written: 1, errorCount: 0, errors: [] }));
113
+ vi.stubGlobal('fetch', fetchMock);
114
+ const imported = await seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, []);
115
+ expect(imported).toBe(1);
116
+ expect(fetchMock).toHaveBeenCalledTimes(2);
117
+ expect(String(fetchMock.mock.calls[0]?.[0])).toContain('/v1/workspaces/rw_demo/fs/import');
118
+ expect(String(fetchMock.mock.calls[1]?.[0])).toContain('/v1/workspaces/rw_demo/fs/bulk');
119
+ const payload = JSON.parse(String(fetchMock.mock.calls[1]?.[1].body));
120
+ expect(payload.files).toEqual([
121
+ { path: '/src/hello.txt', content: 'hello fallback\n', encoding: 'utf-8' },
122
+ ]);
123
+ });
124
+ it('throws on non-404 HTTP errors', async () => {
125
+ const projectDir = makeTempDir('relay-seed-project-');
126
+ fs.mkdirSync(path.join(projectDir, 'src'), { recursive: true });
127
+ fs.writeFileSync(path.join(projectDir, 'src', 'hello.txt'), 'hello\n');
128
+ execSyncMock.mockReturnValue('src/hello.txt\0');
129
+ const fetchMock = vi.fn().mockResolvedValue(new Response('boom', { status: 500 }));
130
+ vi.stubGlobal('fetch', fetchMock);
131
+ await expect(seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, [])).rejects.toThrow('tar import failed for workspace rw_demo: HTTP 500 boom');
132
+ });
133
+ it('works for non-git directories via the directory-walk fallback path', async () => {
134
+ const projectDir = makeTempDir('relay-seed-project-');
135
+ fs.mkdirSync(path.join(projectDir, 'src'), { recursive: true });
136
+ fs.mkdirSync(path.join(projectDir, 'nested', 'docs'), { recursive: true });
137
+ fs.mkdirSync(path.join(projectDir, 'custom-ignore'), { recursive: true });
138
+ fs.mkdirSync(path.join(projectDir, 'node_modules', 'left-pad'), { recursive: true });
139
+ fs.writeFileSync(path.join(projectDir, 'src', 'app.ts'), 'export const app = true;\n');
140
+ fs.writeFileSync(path.join(projectDir, 'nested', 'docs', 'readme.md'), '# hello\n');
141
+ fs.writeFileSync(path.join(projectDir, 'custom-ignore', 'skip.txt'), 'skip\n');
142
+ fs.writeFileSync(path.join(projectDir, 'node_modules', 'left-pad', 'index.js'), 'module.exports = 1;\n');
143
+ fs.writeFileSync(path.join(projectDir, '.relayfile-mount-state.json'), '{}\n');
144
+ execSyncMock.mockImplementation(() => {
145
+ throw new Error('not a git repo');
146
+ });
147
+ const fetchMock = vi.fn().mockResolvedValue(jsonResponse({ imported: 2 }));
148
+ vi.stubGlobal('fetch', fetchMock);
149
+ const imported = await seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, [
150
+ 'custom-ignore',
151
+ ]);
152
+ expect(imported).toBe(2);
153
+ expect(fetchMock).toHaveBeenCalledTimes(1);
154
+ const [, init] = fetchMock.mock.calls[0];
155
+ const entries = await extractTarballEntries(init.body);
156
+ expect(entries).toEqual(expect.arrayContaining(['nested/docs/readme.md', 'src/app.ts']));
157
+ expect(entries).not.toContain('custom-ignore/skip.txt');
158
+ expect(entries).not.toContain('node_modules/left-pad/index.js');
159
+ expect(entries).not.toContain('.relayfile-mount-state.json');
160
+ });
161
+ it('includes untracked files returned by git ls-files and preserves gitignore filtering', async () => {
162
+ const projectDir = makeTempDir('relay-seed-project-');
163
+ fs.mkdirSync(path.join(projectDir, 'src'), { recursive: true });
164
+ fs.mkdirSync(path.join(projectDir, 'ignored-by-git'), { recursive: true });
165
+ fs.writeFileSync(path.join(projectDir, 'src', 'tracked.ts'), 'export const tracked = true;\n');
166
+ fs.writeFileSync(path.join(projectDir, 'src', 'draft.ts'), 'export const draft = true;\n');
167
+ fs.writeFileSync(path.join(projectDir, 'ignored-by-git', 'skip.txt'), 'skip\n');
168
+ execSyncMock.mockReturnValue(['src/tracked.ts', 'src/draft.ts'].join('\0'));
169
+ const fetchMock = vi.fn().mockResolvedValue(jsonResponse({ imported: 2 }));
170
+ vi.stubGlobal('fetch', fetchMock);
171
+ const imported = await seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, []);
172
+ expect(imported).toBe(2);
173
+ expect(execSyncMock).toHaveBeenCalledWith('git ls-files -z --cached --others --exclude-standard', expect.objectContaining({ cwd: path.resolve(projectDir), encoding: 'utf-8' }));
174
+ const [, init] = fetchMock.mock.calls[0];
175
+ const entries = await extractTarballEntries(init.body);
176
+ expect(entries).toEqual(['src/draft.ts', 'src/tracked.ts']);
177
+ expect(entries).not.toContain('ignored-by-git/skip.txt');
178
+ });
179
+ it('does not fall back to a directory walk when git ls-files succeeds with no files', async () => {
180
+ const projectDir = makeTempDir('relay-seed-project-');
181
+ fs.mkdirSync(path.join(projectDir, 'ignored-by-git'), { recursive: true });
182
+ fs.writeFileSync(path.join(projectDir, 'ignored-by-git', 'skip.txt'), 'skip\n');
183
+ execSyncMock.mockReturnValue('');
184
+ const fetchMock = vi.fn().mockResolvedValue(jsonResponse({ imported: 0 }));
185
+ vi.stubGlobal('fetch', fetchMock);
186
+ const imported = await seedWorkspaceTar('https://relayfile.example/', 'token', 'rw_demo', projectDir, []);
187
+ expect(imported).toBe(0);
188
+ expect(fetchMock).not.toHaveBeenCalled();
189
+ });
190
+ });
191
+ //# sourceMappingURL=tar-seeder.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tar-seeder.test.js","sourceRoot":"","sources":["../../../src/provisioner/__tests__/tar-seeder.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token-factory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-factory.test.d.ts","sourceRoot":"","sources":["../../../src/provisioner/__tests__/token-factory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,127 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createHmac } from 'node:crypto';
3
+ import test from 'node:test';
4
+ import { DEFAULT_ADMIN_AGENT_NAME, DEFAULT_ADMIN_SCOPES, DEFAULT_WORKFLOW_TOKEN_TTL_SECONDS, WorkflowTokenFactory, mintAgentToken, } from '../token.js';
5
+ function decodeJwtPart(value) {
6
+ return JSON.parse(Buffer.from(value, 'base64url').toString('utf8'));
7
+ }
8
+ function decodeJwt(token) {
9
+ const [header, payload, signature] = token.split('.');
10
+ assert.ok(header);
11
+ assert.ok(payload);
12
+ assert.ok(signature);
13
+ return {
14
+ header: decodeJwtPart(header),
15
+ payload: decodeJwtPart(payload),
16
+ signature,
17
+ };
18
+ }
19
+ test('mintAgentToken returns a valid JWT', () => {
20
+ const token = mintAgentToken({
21
+ secret: 'test-secret',
22
+ agentName: 'worker',
23
+ workspace: 'workspace-123',
24
+ scopes: ['relayfile:fs:read:/src/index.ts'],
25
+ });
26
+ const parts = token.split('.');
27
+ const decoded = decodeJwt(token);
28
+ assert.equal(parts.length, 3);
29
+ assert.ok(parts.every((part) => /^[A-Za-z0-9_-]+$/u.test(part)));
30
+ assert.deepEqual(decoded.header, { alg: 'HS256', typ: 'JWT' });
31
+ assert.equal(decoded.payload.sub, 'agent_worker');
32
+ });
33
+ test('mintAgentToken payload contains agent_name, workspace, and scopes', () => {
34
+ const scopes = ['relayfile:fs:read:/src/index.ts', 'relayfile:fs:write:/src/index.ts'];
35
+ const token = mintAgentToken({
36
+ secret: 'test-secret',
37
+ agentName: 'compiler',
38
+ workspace: 'workspace-abc',
39
+ scopes,
40
+ });
41
+ const { payload } = decodeJwt(token);
42
+ assert.equal(payload.agent_name, 'compiler');
43
+ assert.equal(payload.wks, 'workspace-abc');
44
+ assert.equal(payload.workspace_id, 'workspace-abc');
45
+ assert.deepEqual(payload.scopes, scopes);
46
+ });
47
+ test('mintAgentToken defaults expiry to 2 hours', () => {
48
+ const token = mintAgentToken({
49
+ secret: 'test-secret',
50
+ agentName: 'worker',
51
+ workspace: 'workspace-123',
52
+ scopes: [],
53
+ });
54
+ const { payload } = decodeJwt(token);
55
+ assert.equal(payload.exp - payload.iat, DEFAULT_WORKFLOW_TOKEN_TTL_SECONDS);
56
+ assert.equal(payload.exp - payload.iat, 2 * 60 * 60);
57
+ });
58
+ test('mintAgentToken applies a custom TTL', () => {
59
+ const token = mintAgentToken({
60
+ secret: 'test-secret',
61
+ agentName: 'worker',
62
+ workspace: 'workspace-123',
63
+ scopes: [],
64
+ ttlSeconds: 90,
65
+ });
66
+ const { payload } = decodeJwt(token);
67
+ assert.equal(payload.exp - payload.iat, 90);
68
+ });
69
+ test('WorkflowTokenFactory mintAdmin uses the default admin identity and scopes', () => {
70
+ const factory = new WorkflowTokenFactory('test-secret', 'workspace-admin');
71
+ const token = factory.mintAdmin();
72
+ const { payload } = decodeJwt(token);
73
+ assert.equal(payload.agent_name, DEFAULT_ADMIN_AGENT_NAME);
74
+ assert.equal(payload.wks, 'workspace-admin');
75
+ assert.deepEqual(payload.scopes, DEFAULT_ADMIN_SCOPES);
76
+ });
77
+ test('WorkflowTokenFactory getToken returns the token minted for an agent', () => {
78
+ const factory = new WorkflowTokenFactory('test-secret', 'workspace-123');
79
+ const token = factory.mintForAgent('builder', ['relayfile:fs:read:/src/index.ts']);
80
+ assert.equal(factory.getToken('builder'), token);
81
+ });
82
+ test('WorkflowTokenFactory uses its configured TTL when minting agent tokens', () => {
83
+ const factory = new WorkflowTokenFactory('test-secret', 'workspace-123', 45);
84
+ const token = factory.mintForAgent('builder', []);
85
+ const { payload } = decodeJwt(token);
86
+ assert.equal(payload.exp - payload.iat, 45);
87
+ });
88
+ test('mintAgentToken generates a unique JTI per token', () => {
89
+ const first = decodeJwt(mintAgentToken({
90
+ secret: 'test-secret',
91
+ agentName: 'worker',
92
+ workspace: 'workspace-123',
93
+ scopes: [],
94
+ })).payload;
95
+ const second = decodeJwt(mintAgentToken({
96
+ secret: 'test-secret',
97
+ agentName: 'worker',
98
+ workspace: 'workspace-123',
99
+ scopes: [],
100
+ })).payload;
101
+ assert.notEqual(first.jti, second.jti);
102
+ assert.match(first.jti, /^tok-\d+-/u);
103
+ assert.match(second.jti, /^tok-\d+-/u);
104
+ });
105
+ test('mintAgentToken includes the expected audience claims', () => {
106
+ const token = mintAgentToken({
107
+ secret: 'test-secret',
108
+ agentName: 'worker',
109
+ workspace: 'workspace-123',
110
+ scopes: [],
111
+ });
112
+ const { payload } = decodeJwt(token);
113
+ assert.deepEqual(payload.aud, ['relayauth', 'relayfile']);
114
+ });
115
+ test('mintAgentToken signs tokens with HMAC-SHA256', () => {
116
+ const secret = 'test-secret';
117
+ const token = mintAgentToken({
118
+ secret,
119
+ agentName: 'worker',
120
+ workspace: 'workspace-123',
121
+ scopes: ['relayfile:fs:read:/src/index.ts'],
122
+ });
123
+ const [header, payload, signature] = token.split('.');
124
+ const expectedSignature = createHmac('sha256', secret).update(`${header}.${payload}`).digest('base64url');
125
+ assert.equal(signature, expectedSignature);
126
+ });
127
+ //# sourceMappingURL=token-factory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-factory.test.js","sourceRoot":"","sources":["../../../src/provisioner/__tests__/token-factory.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.test.d.ts","sourceRoot":"","sources":["../../../src/provisioner/__tests__/token.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { DEFAULT_WORKFLOW_TOKEN_TTL_SECONDS, mintAgentToken } from '../token.js';
4
+ function decodeJwtPayload(token) {
5
+ const [, payload] = token.split('.');
6
+ return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
7
+ }
8
+ test('mintAgentToken returns a valid JWT', () => {
9
+ const token = mintAgentToken({
10
+ secret: 'test-secret',
11
+ agentName: 'worker',
12
+ workspace: 'workspace-123',
13
+ scopes: ['relayfile:fs:read:/src/index.ts'],
14
+ });
15
+ const parts = token.split('.');
16
+ assert.equal(parts.length, 3);
17
+ assert.ok(parts.every((part) => /^[A-Za-z0-9_-]+$/u.test(part)));
18
+ });
19
+ test('mintAgentToken payload contains agent_name, workspace, and scopes', () => {
20
+ const scopes = ['relayfile:fs:read:/src/index.ts', 'relayfile:fs:write:/src/index.ts'];
21
+ const token = mintAgentToken({
22
+ secret: 'test-secret',
23
+ agentName: 'compiler',
24
+ workspace: 'workspace-abc',
25
+ scopes,
26
+ });
27
+ const payload = decodeJwtPayload(token);
28
+ assert.equal(payload.agent_name, 'compiler');
29
+ assert.equal(payload.wks, 'workspace-abc');
30
+ assert.equal(payload.workspace_id, 'workspace-abc');
31
+ assert.deepEqual(payload.scopes, scopes);
32
+ });
33
+ test('mintAgentToken defaults expiry to 2 hours', () => {
34
+ const token = mintAgentToken({
35
+ secret: 'test-secret',
36
+ agentName: 'worker',
37
+ workspace: 'workspace-123',
38
+ scopes: [],
39
+ });
40
+ const payload = decodeJwtPayload(token);
41
+ assert.equal(payload.exp - payload.iat, DEFAULT_WORKFLOW_TOKEN_TTL_SECONDS);
42
+ assert.equal(payload.exp - payload.iat, 2 * 60 * 60);
43
+ });
44
+ //# sourceMappingURL=token.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.test.js","sourceRoot":"","sources":["../../../src/provisioner/__tests__/token.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kCAAkC,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAEnG,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAgB,CAAC;AACvF,CAAC;AAED,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAC9C,MAAM,KAAK,GAAG,cAAc,CAAC;QAC3B,MAAM,EAAE,aAAa;QACrB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,eAAe;QAC1B,MAAM,EAAE,CAAC,iCAAiC,CAAC;KAC5C,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;IAC7E,MAAM,MAAM,GAAG,CAAC,iCAAiC,EAAE,kCAAkC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,cAAc,CAAC;QAC3B,MAAM,EAAE,aAAa;QACrB,SAAS,EAAE,UAAU;QACrB,SAAS,EAAE,eAAe;QAC1B,MAAM;KACP,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IACpD,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,KAAK,GAAG,cAAc,CAAC;QAC3B,MAAM,EAAE,aAAa;QACrB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,eAAe;QAC1B,MAAM,EAAE,EAAE;KACX,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,kCAAkC,CAAC,CAAC;IAC5E,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface PermissionAuditEntry {
2
+ timestamp: string;
3
+ agentName: string;
4
+ action: string;
5
+ details: Record<string, unknown>;
6
+ }
7
+ export declare function getDefaultPermissionAuditPath(projectDir: string): string;
8
+ export declare class PermissionAuditLog {
9
+ private readonly entries;
10
+ log(entry: Omit<PermissionAuditEntry, 'timestamp'> & {
11
+ timestamp?: string;
12
+ }): PermissionAuditEntry;
13
+ toJSON(): {
14
+ entries: PermissionAuditEntry[];
15
+ };
16
+ writeTo(filePath: string): Promise<void>;
17
+ summary(): string;
18
+ }
19
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/provisioner/audit.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAwCD,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAEtD,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,oBAAoB;IAYlG,MAAM,IAAI;QAAE,OAAO,EAAE,oBAAoB,EAAE,CAAA;KAAE;IAWvC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C,OAAO,IAAI,MAAM;CAoBlB"}
@@ -0,0 +1,74 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const DEFAULT_PERMISSION_AUDIT_RELATIVE_PATH = path.join('.agent-relay', 'permission-audit.json');
4
+ function isPlainObject(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ function sanitizeJsonValue(value, key) {
8
+ if (key && key.toLowerCase().includes('token')) {
9
+ return '[redacted]';
10
+ }
11
+ if (value === null ||
12
+ typeof value === 'string' ||
13
+ typeof value === 'number' ||
14
+ typeof value === 'boolean') {
15
+ return value;
16
+ }
17
+ if (Array.isArray(value)) {
18
+ return value.map((item) => sanitizeJsonValue(item));
19
+ }
20
+ if (isPlainObject(value)) {
21
+ return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [
22
+ entryKey,
23
+ sanitizeJsonValue(entryValue, entryKey),
24
+ ]));
25
+ }
26
+ return String(value);
27
+ }
28
+ export function getDefaultPermissionAuditPath(projectDir) {
29
+ return path.resolve(projectDir, DEFAULT_PERMISSION_AUDIT_RELATIVE_PATH);
30
+ }
31
+ export class PermissionAuditLog {
32
+ entries = [];
33
+ log(entry) {
34
+ const storedEntry = {
35
+ timestamp: entry.timestamp ?? new Date().toISOString(),
36
+ agentName: entry.agentName,
37
+ action: entry.action,
38
+ details: sanitizeJsonValue(entry.details),
39
+ };
40
+ this.entries.push(storedEntry);
41
+ return storedEntry;
42
+ }
43
+ toJSON() {
44
+ return {
45
+ entries: this.entries.map((entry) => ({
46
+ timestamp: entry.timestamp,
47
+ agentName: entry.agentName,
48
+ action: entry.action,
49
+ details: { ...entry.details },
50
+ })),
51
+ };
52
+ }
53
+ async writeTo(filePath) {
54
+ await mkdir(path.dirname(filePath), { recursive: true });
55
+ await writeFile(filePath, `${JSON.stringify(this.toJSON(), null, 2)}\n`, 'utf8');
56
+ }
57
+ summary() {
58
+ if (this.entries.length === 0) {
59
+ return 'Permission audit: 0 entries';
60
+ }
61
+ const actionCounts = new Map();
62
+ const agentNames = new Set();
63
+ for (const entry of this.entries) {
64
+ actionCounts.set(entry.action, (actionCounts.get(entry.action) ?? 0) + 1);
65
+ agentNames.add(entry.agentName);
66
+ }
67
+ const actionSummary = [...actionCounts.entries()]
68
+ .sort(([left], [right]) => left.localeCompare(right))
69
+ .map(([action, count]) => `${action}=${count}`)
70
+ .join(', ');
71
+ return `Permission audit: ${this.entries.length} entr${this.entries.length === 1 ? 'y' : 'ies'} across ${agentNames.size} agent${agentNames.size === 1 ? '' : 's'} (${actionSummary})`;
72
+ }
73
+ }
74
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/provisioner/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AgentPreset } from '../workflows/types.js';
2
+ import type { AgentPermissions, CompiledAgentPermissions, CompileInput } from './types.js';
3
+ type FileAction = 'read' | 'write';
4
+ interface ExpandedPreset {
5
+ read: string[];
6
+ write: string[];
7
+ deny: string[];
8
+ }
9
+ export declare function defaultPermissionsForPreset(preset: AgentPreset | undefined): AgentPermissions;
10
+ export declare function expandPreset(preset: AgentPermissions['access'], options?: {
11
+ projectDir?: string;
12
+ workdir?: string;
13
+ }): ExpandedPreset;
14
+ export declare function globsToScopes(globs: string[], action: FileAction): string[];
15
+ export declare function compileAgentPermissions(input: CompileInput): CompiledAgentPermissions;
16
+ export declare function mergeAcl(compilations: readonly CompiledAgentPermissions[]): Record<string, string[]>;
17
+ export declare function resolveAgentPermissions(agentName: string, permissions: AgentPermissions | undefined, projectDir: string, workspace: string): CompiledAgentPermissions;
18
+ export declare function compileAgentScopes(input: CompileInput): CompiledAgentPermissions;
19
+ export declare function mergePermissionSources(dotfileScopes: string[], yamlScopes: string[], rawScopes: string[]): string[];
20
+ export declare const expandAccessPreset: typeof expandPreset;
21
+ export declare const globToScopes: (globs: string[], action: FileAction, _projectDir?: string) => string[];
22
+ export {};
23
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/provisioner/compiler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAE7G,KAAK,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnC,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAqRD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,gBAAgB,CAY7F;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD,cAAc,CAqBhB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAO3E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,wBAAwB,CA4GrF;AAED,wBAAgB,QAAQ,CAAC,YAAY,EAAE,SAAS,wBAAwB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAkBpG;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAO1B;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,wBAAwB,CAEhF;AAED,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EAAE,EACvB,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,MAAM,EAAE,GAClB,MAAM,EAAE,CAEV;AAED,eAAO,MAAM,kBAAkB,qBAAe,CAAC;AAC/C,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,EAAE,QAAQ,UAAU,EAAE,cAAc,MAAM,KAAG,MAAM,EACjE,CAAC"}