@dharma-ai-labs/agent-fabric 0.2.99 → 0.2.101
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/AGENT_FABRIC_ONBOARDING.md +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -8
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +33 -4
- package/dist/index.test.js.map +1 -1
- package/dist/onboardingStage.d.ts +1 -1
- package/dist/onboardingStage.d.ts.map +1 -1
- package/dist/onboardingStage.js.map +1 -1
- package/dist/relayAutostart.d.ts +36 -0
- package/dist/relayAutostart.d.ts.map +1 -0
- package/dist/relayAutostart.js +167 -0
- package/dist/relayAutostart.js.map +1 -0
- package/dist/relayAutostart.test.d.ts +2 -0
- package/dist/relayAutostart.test.d.ts.map +1 -0
- package/dist/relayAutostart.test.js +103 -0
- package/dist/relayAutostart.test.js.map +1 -0
- package/dist/relaySupervisor.d.ts +11 -0
- package/dist/relaySupervisor.d.ts.map +1 -0
- package/dist/relaySupervisor.js +56 -0
- package/dist/relaySupervisor.js.map +1 -0
- package/dist/relaySupervisor.test.d.ts +2 -0
- package/dist/relaySupervisor.test.d.ts.map +1 -0
- package/dist/relaySupervisor.test.js +127 -0
- package/dist/relaySupervisor.test.js.map +1 -0
- package/dist/repositoryOnboardingCaller.test.js +9 -2
- package/dist/repositoryOnboardingCaller.test.js.map +1 -1
- package/dist/skillPreparationCache.test.js +2 -0
- package/dist/skillPreparationCache.test.js.map +1 -1
- package/dist/usage.d.ts +1 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +1 -1
- package/dist/usage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type OnboardingStage = 'local_skill_inventory' | 'first_learning_preview' | 'first_learning_sync' | 'package_snapshot' | 'package_publication' | 'role_registration' | 'native_skill_install' | 'relay_start' | 'readiness';
|
|
1
|
+
export type OnboardingStage = 'local_skill_inventory' | 'first_learning_preview' | 'first_learning_sync' | 'package_snapshot' | 'package_publication' | 'role_registration' | 'native_skill_install' | 'relay_start' | 'autostart' | 'readiness';
|
|
2
2
|
export declare function withOnboardingStage<T>(stage: OnboardingStage, workspaceId: string, resumeCommand: string, operation: () => Promise<T>): Promise<T>;
|
|
3
3
|
//# sourceMappingURL=onboardingStage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboardingStage.d.ts","sourceRoot":"","sources":["../src/onboardingStage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,uBAAuB,GACvB,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,aAAa,GACb,WAAW,CAAC;AAEhB,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAUZ"}
|
|
1
|
+
{"version":3,"file":"onboardingStage.d.ts","sourceRoot":"","sources":["../src/onboardingStage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,uBAAuB,GACvB,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,GACrB,mBAAmB,GACnB,sBAAsB,GACtB,aAAa,GACb,WAAW,GACX,WAAW,CAAC;AAEhB,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAUZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboardingStage.js","sourceRoot":"","sources":["../src/onboardingStage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"onboardingStage.js","sourceRoot":"","sources":["../src/onboardingStage.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAsB,EACtB,WAAmB,EACnB,aAAqB,EACrB,SAA2B;IAE3B,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,KAAK,MAAM,mBAAmB,WAAW,qBAAqB,aAAa,EAAE,EAC7G,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type Runner = (file: string, args: string[]) => Promise<{
|
|
2
|
+
stdout: string;
|
|
3
|
+
}>;
|
|
4
|
+
export interface RelayAutostartOptions {
|
|
5
|
+
home: string;
|
|
6
|
+
userHome?: string;
|
|
7
|
+
platform?: NodeJS.Platform;
|
|
8
|
+
run?: Runner;
|
|
9
|
+
}
|
|
10
|
+
interface Registration {
|
|
11
|
+
schema: 'dharma.relay-autostart/v1';
|
|
12
|
+
backend: 'systemd-user' | 'windows-task';
|
|
13
|
+
launcher: string;
|
|
14
|
+
policy: string;
|
|
15
|
+
workspace: string;
|
|
16
|
+
version: string;
|
|
17
|
+
taskName: string | null;
|
|
18
|
+
}
|
|
19
|
+
export type RelayAutostartState = {
|
|
20
|
+
state: 'enabled' | 'disabled' | 'unavailable' | 'unsupported';
|
|
21
|
+
backend: Registration['backend'] | null;
|
|
22
|
+
version?: string;
|
|
23
|
+
reason?: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function linuxRelayUnit(launcher: string, policy: string, workspace: string, home?: string): string;
|
|
26
|
+
export declare function windowsRelayStartupScript(launcher: string, policy: string, home?: string): string;
|
|
27
|
+
export declare function relayAutostartStatus(options: RelayAutostartOptions): Promise<RelayAutostartState>;
|
|
28
|
+
export declare function enableRelayAutostart(options: RelayAutostartOptions & {
|
|
29
|
+
workspace: string;
|
|
30
|
+
launcher: string;
|
|
31
|
+
policy: string;
|
|
32
|
+
version: string;
|
|
33
|
+
}): Promise<RelayAutostartState>;
|
|
34
|
+
export declare function disableRelayAutostart(options: RelayAutostartOptions): Promise<RelayAutostartState>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=relayAutostart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayAutostart.d.ts","sourceRoot":"","sources":["../src/relayAutostart.ts"],"names":[],"mappings":"AAUA,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,cAAc,GAAG,cAAc,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;IAC9D,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAoBF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,UAOhG;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,UAKxF;AAsBD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsBvG;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG;IAC1E,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;CACtE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgE/B;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAiBxG"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
const UNIT_NAME = 'dharma-agent-fabric.service';
|
|
9
|
+
const defaultRunner = async (file, args) => {
|
|
10
|
+
const { stdout } = await execFileAsync(file, args, { timeout: 15_000, maxBuffer: 64 * 1024 });
|
|
11
|
+
return { stdout };
|
|
12
|
+
};
|
|
13
|
+
function safeLine(value) {
|
|
14
|
+
if (!value || /[\r\n\0]/.test(value))
|
|
15
|
+
throw new Error('Autostart paths must be nonempty single lines.');
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function systemdValue(value) {
|
|
19
|
+
return `"${safeLine(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%')}"`;
|
|
20
|
+
}
|
|
21
|
+
function psLiteral(value) {
|
|
22
|
+
return `'${safeLine(value).replace(/'/g, "''")}'`;
|
|
23
|
+
}
|
|
24
|
+
export function linuxRelayUnit(launcher, policy, workspace, home) {
|
|
25
|
+
return `[Unit]\nDescription=Dharma Agent Fabric relay\nAfter=network-online.target\nWants=network-online.target\n\n`
|
|
26
|
+
+ `[Service]\nType=simple\nWorkingDirectory=${systemdValue(workspace)}\n`
|
|
27
|
+
+ (home ? `Environment=${systemdValue(`DHARMA_HOME=${home}`)}\n` : '')
|
|
28
|
+
+ `ExecStart=${systemdValue(launcher)} relay supervise --policy ${systemdValue(policy)}\n`
|
|
29
|
+
+ `Restart=on-failure\nRestartSec=10s\nTimeoutStopSec=30s\n\n`
|
|
30
|
+
+ `[Install]\nWantedBy=default.target\n`;
|
|
31
|
+
}
|
|
32
|
+
export function windowsRelayStartupScript(launcher, policy, home) {
|
|
33
|
+
return `$ErrorActionPreference = 'Stop'\r\n`
|
|
34
|
+
+ (home ? `$env:DHARMA_HOME = ${psLiteral(home)}\r\n` : '')
|
|
35
|
+
+ `& ${psLiteral(launcher)} relay supervise --policy ${psLiteral(policy)}\r\n`
|
|
36
|
+
+ `exit $LASTEXITCODE\r\n`;
|
|
37
|
+
}
|
|
38
|
+
function encodedPowerShell(command) {
|
|
39
|
+
return ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(command, 'utf16le').toString('base64')];
|
|
40
|
+
}
|
|
41
|
+
function registrationPath(home) { return join(home, 'relay', 'autostart.json'); }
|
|
42
|
+
function unitPath(userHome) { return join(userHome, '.config', 'systemd', 'user', UNIT_NAME); }
|
|
43
|
+
function scriptPath(home) { return join(home, 'relay', 'autostart.ps1'); }
|
|
44
|
+
function taskName(home) {
|
|
45
|
+
return `DharmaAgentFabric-${createHash('sha256').update(home.toLowerCase()).digest('hex').slice(0, 12)}`;
|
|
46
|
+
}
|
|
47
|
+
async function readRegistration(home) {
|
|
48
|
+
try {
|
|
49
|
+
const value = JSON.parse(await readFile(registrationPath(home), 'utf8'));
|
|
50
|
+
if (value.schema !== 'dharma.relay-autostart/v1'
|
|
51
|
+
|| !['systemd-user', 'windows-task'].includes(value.backend))
|
|
52
|
+
return null;
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function relayAutostartStatus(options) {
|
|
60
|
+
const registration = await readRegistration(options.home);
|
|
61
|
+
if (!registration)
|
|
62
|
+
return { state: 'disabled', backend: null };
|
|
63
|
+
const platform = options.platform || process.platform;
|
|
64
|
+
const run = options.run || defaultRunner;
|
|
65
|
+
if ((platform === 'linux' && registration.backend !== 'systemd-user')
|
|
66
|
+
|| (platform === 'win32' && registration.backend !== 'windows-task')) {
|
|
67
|
+
return { state: 'unavailable', backend: registration.backend, version: registration.version, reason: 'platform_mismatch' };
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const result = registration.backend === 'systemd-user'
|
|
71
|
+
? await run('systemctl', ['--user', 'is-enabled', UNIT_NAME])
|
|
72
|
+
: await run('powershell.exe', encodedPowerShell(`$task = Get-ScheduledTask -TaskName ${psLiteral(registration.taskName || '')} -ErrorAction SilentlyContinue; `
|
|
73
|
+
+ `if ($task -and $task.State -ne 'Disabled') { 'enabled' } else { 'disabled' }`));
|
|
74
|
+
return { state: result.stdout.trim() === 'enabled' ? 'enabled' : 'disabled',
|
|
75
|
+
backend: registration.backend, version: registration.version };
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return { state: 'unavailable', backend: registration.backend, version: registration.version,
|
|
79
|
+
reason: registration.backend === 'systemd-user' ? 'systemd_user_unavailable' : 'task_scheduler_unavailable' };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export async function enableRelayAutostart(options) {
|
|
83
|
+
const platform = options.platform || process.platform;
|
|
84
|
+
if (platform !== 'linux' && platform !== 'win32') {
|
|
85
|
+
throw new Error(`Relay autostart is unsupported on ${platform}.`);
|
|
86
|
+
}
|
|
87
|
+
const userHome = options.userHome || homedir();
|
|
88
|
+
const run = options.run || defaultRunner;
|
|
89
|
+
const previous = await readRegistration(options.home);
|
|
90
|
+
if (previous && previous.backend !== (platform === 'linux' ? 'systemd-user' : 'windows-task')) {
|
|
91
|
+
throw new Error('Existing relay autostart belongs to a different operating system.');
|
|
92
|
+
}
|
|
93
|
+
const registration = {
|
|
94
|
+
schema: 'dharma.relay-autostart/v1',
|
|
95
|
+
backend: platform === 'linux' ? 'systemd-user' : 'windows-task',
|
|
96
|
+
launcher: safeLine(options.launcher), policy: safeLine(options.policy),
|
|
97
|
+
workspace: safeLine(options.workspace), version: options.version,
|
|
98
|
+
taskName: platform === 'win32' ? taskName(options.home) : null,
|
|
99
|
+
};
|
|
100
|
+
if (platform === 'win32' && !previous) {
|
|
101
|
+
const probe = await run('powershell.exe', encodedPowerShell(`$task = Get-ScheduledTask -TaskName ${psLiteral(registration.taskName)} -ErrorAction SilentlyContinue; `
|
|
102
|
+
+ `if ($null -ne $task) { 'exists' } else { 'absent' }`));
|
|
103
|
+
if (probe.stdout.trim() !== 'absent') {
|
|
104
|
+
throw new Error('autostart_conflict: a Windows startup task exists without this enrollment receipt.');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const destination = platform === 'linux' ? unitPath(userHome) : scriptPath(options.home);
|
|
108
|
+
const existingContents = await readFile(destination, 'utf8').catch((error) => {
|
|
109
|
+
if (error.code === 'ENOENT')
|
|
110
|
+
return null;
|
|
111
|
+
throw error;
|
|
112
|
+
});
|
|
113
|
+
const ownedContents = previous && (platform === 'linux'
|
|
114
|
+
? linuxRelayUnit(previous.launcher, previous.policy, previous.workspace, options.home)
|
|
115
|
+
: windowsRelayStartupScript(previous.launcher, previous.policy, options.home));
|
|
116
|
+
if (existingContents !== null && existingContents !== ownedContents) {
|
|
117
|
+
throw new Error('autostart_conflict: the user startup entry is not owned by this enrollment.');
|
|
118
|
+
}
|
|
119
|
+
await mkdir(dirname(destination), { recursive: true, mode: 0o700 });
|
|
120
|
+
await mkdir(dirname(registrationPath(options.home)), { recursive: true, mode: 0o700 });
|
|
121
|
+
await writeFile(destination, platform === 'linux'
|
|
122
|
+
? linuxRelayUnit(options.launcher, options.policy, options.workspace, options.home)
|
|
123
|
+
: windowsRelayStartupScript(options.launcher, options.policy, options.home), { mode: 0o600 });
|
|
124
|
+
await writeFile(registrationPath(options.home), `${JSON.stringify(registration, null, 2)}\n`, { mode: 0o600 });
|
|
125
|
+
if (platform === 'linux') {
|
|
126
|
+
await run('systemctl', ['--user', 'daemon-reload']);
|
|
127
|
+
await run('systemctl', ['--user', 'enable', UNIT_NAME]);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const script = scriptPath(options.home);
|
|
131
|
+
const command = `$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name; `
|
|
132
|
+
+ `$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
|
|
133
|
+
+ `-Argument ${psLiteral(`-NoProfile -NonInteractive -File "${script}"`)} `
|
|
134
|
+
+ `-WorkingDirectory ${psLiteral(options.workspace)}; `
|
|
135
|
+
+ `$trigger = New-ScheduledTaskTrigger -AtLogOn -User $identity; `
|
|
136
|
+
+ `$principal = New-ScheduledTaskPrincipal -UserId $identity -LogonType Interactive -RunLevel Limited; `
|
|
137
|
+
+ `$settings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -ExecutionTimeLimit ([TimeSpan]::Zero) `
|
|
138
|
+
+ `-RestartCount 10 -RestartInterval (New-TimeSpan -Minutes 1); `
|
|
139
|
+
+ `Register-ScheduledTask -TaskName ${psLiteral(registration.taskName)} -Action $action `
|
|
140
|
+
+ `-Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null`;
|
|
141
|
+
await run('powershell.exe', encodedPowerShell(command));
|
|
142
|
+
}
|
|
143
|
+
const status = await relayAutostartStatus(options);
|
|
144
|
+
if (status.state !== 'enabled')
|
|
145
|
+
throw new Error(`Relay autostart registration was not verified (${status.state}).`);
|
|
146
|
+
return status;
|
|
147
|
+
}
|
|
148
|
+
export async function disableRelayAutostart(options) {
|
|
149
|
+
const registration = await readRegistration(options.home);
|
|
150
|
+
if (!registration)
|
|
151
|
+
return { state: 'disabled', backend: null };
|
|
152
|
+
const run = options.run || defaultRunner;
|
|
153
|
+
if (registration.backend === 'systemd-user') {
|
|
154
|
+
await run('systemctl', ['--user', 'disable', UNIT_NAME]);
|
|
155
|
+
await rm(unitPath(options.userHome || homedir()), { force: true });
|
|
156
|
+
await run('systemctl', ['--user', 'daemon-reload']);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
if (registration.taskName !== taskName(options.home))
|
|
160
|
+
throw new Error('Relay autostart task identity is invalid.');
|
|
161
|
+
await run('powershell.exe', encodedPowerShell(`Unregister-ScheduledTask -TaskName ${psLiteral(registration.taskName)} -Confirm:$false -ErrorAction SilentlyContinue`));
|
|
162
|
+
await rm(scriptPath(options.home), { force: true });
|
|
163
|
+
}
|
|
164
|
+
await rm(registrationPath(options.home), { force: true });
|
|
165
|
+
return { state: 'disabled', backend: registration.backend, version: registration.version };
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=relayAutostart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayAutostart.js","sourceRoot":"","sources":["../src/relayAutostart.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayAutostart.test.d.ts","sourceRoot":"","sources":["../src/relayAutostart.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { disableRelayAutostart, enableRelayAutostart, linuxRelayUnit, relayAutostartStatus, windowsRelayStartupScript, } from './relayAutostart.js';
|
|
7
|
+
test('Linux startup invokes only the pinned launcher and canonical policy', () => {
|
|
8
|
+
const unit = linuxRelayUnit('/home/a b/repo/.dharma/bin/dharma', '/home/a b/repo/.dharma/approved-policy.json', '/home/a b/repo');
|
|
9
|
+
assert.match(unit, /ExecStart="\/home\/a b\/repo\/\.dharma\/bin\/dharma" relay supervise --policy/);
|
|
10
|
+
assert.match(unit, /Restart=on-failure/);
|
|
11
|
+
assert.doesNotMatch(unit, /grant|token|password/i);
|
|
12
|
+
});
|
|
13
|
+
test('Windows startup escapes paths as PowerShell literals without embedding credentials', () => {
|
|
14
|
+
const script = windowsRelayStartupScript("C:\\A's Repo\\.dharma\\bin\\dharma.cmd", "C:\\A's Repo\\.dharma\\approved-policy.json");
|
|
15
|
+
assert.match(script, /A''s Repo/);
|
|
16
|
+
assert.match(script, /relay supervise --policy/);
|
|
17
|
+
assert.doesNotMatch(script, /grant|token|password/i);
|
|
18
|
+
});
|
|
19
|
+
test('Linux enable, status and disable preserve an explicit OS registration receipt', async () => {
|
|
20
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-autostart-'));
|
|
21
|
+
const calls = [];
|
|
22
|
+
const run = async (file, args) => {
|
|
23
|
+
calls.push(`${file} ${args.join(' ')}`);
|
|
24
|
+
return { stdout: args.includes('is-enabled') ? 'enabled\n' : '' };
|
|
25
|
+
};
|
|
26
|
+
const options = {
|
|
27
|
+
platform: 'linux', home: join(root, 'dharma'), userHome: root,
|
|
28
|
+
workspace: join(root, 'repo'), launcher: join(root, 'repo', '.dharma', 'bin', 'dharma'),
|
|
29
|
+
policy: join(root, 'repo', '.dharma', 'approved-policy.json'), version: '0.2.101', run,
|
|
30
|
+
};
|
|
31
|
+
const installed = await enableRelayAutostart(options);
|
|
32
|
+
assert.equal(installed.state, 'enabled');
|
|
33
|
+
assert.match(await readFile(join(root, '.config', 'systemd', 'user', 'dharma-agent-fabric.service'), 'utf8'), /relay supervise/);
|
|
34
|
+
assert.match(calls.join('\n'), /systemctl --user enable dharma-agent-fabric.service/);
|
|
35
|
+
assert.equal((await relayAutostartStatus(options)).state, 'enabled');
|
|
36
|
+
assert.equal((await disableRelayAutostart(options)).state, 'disabled');
|
|
37
|
+
assert.match(calls.join('\n'), /systemctl --user disable dharma-agent-fabric.service/);
|
|
38
|
+
});
|
|
39
|
+
test('startup status never calls a missing OS registration healthy', async () => {
|
|
40
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-autostart-missing-'));
|
|
41
|
+
const result = await relayAutostartStatus({
|
|
42
|
+
platform: 'linux', home: join(root, 'dharma'), userHome: root,
|
|
43
|
+
run: async () => { throw new Error('No systemd user bus'); },
|
|
44
|
+
});
|
|
45
|
+
assert.equal(result.state, 'disabled');
|
|
46
|
+
});
|
|
47
|
+
test('Windows task registration uses the enrolled user without an embedded password', async () => {
|
|
48
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-windows-autostart-'));
|
|
49
|
+
const calls = [];
|
|
50
|
+
const run = async (_file, args) => {
|
|
51
|
+
const decoded = Buffer.from(args.at(-1) || '', 'base64').toString('utf16le');
|
|
52
|
+
calls.push(decoded);
|
|
53
|
+
return { stdout: decoded.includes("'exists'") ? 'absent\n'
|
|
54
|
+
: decoded.includes('Get-ScheduledTask') ? 'enabled\n' : '' };
|
|
55
|
+
};
|
|
56
|
+
const options = {
|
|
57
|
+
platform: 'win32', home: join(root, 'dharma'), userHome: root,
|
|
58
|
+
workspace: 'C:\\Work Space\\repo', launcher: 'C:\\Work Space\\repo\\.dharma\\bin\\dharma.cmd',
|
|
59
|
+
policy: 'C:\\Work Space\\repo\\.dharma\\approved-policy.json', version: '0.2.101', run,
|
|
60
|
+
};
|
|
61
|
+
assert.equal((await enableRelayAutostart(options)).state, 'enabled');
|
|
62
|
+
const script = await readFile(join(root, 'dharma', 'relay', 'autostart.ps1'), 'utf8');
|
|
63
|
+
assert.match(script, /DHARMA_HOME/);
|
|
64
|
+
assert.match(calls.join('\n'), /-LogonType Interactive\b/);
|
|
65
|
+
assert.doesNotMatch(calls.join('\n'), /-Password|bootstrapGrant|--grant/);
|
|
66
|
+
assert.equal((await disableRelayAutostart(options)).state, 'disabled');
|
|
67
|
+
assert.match(calls.at(-1) || '', /Unregister-ScheduledTask/);
|
|
68
|
+
});
|
|
69
|
+
test('failed OS registration remains unavailable instead of reporting completion', async () => {
|
|
70
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-failed-autostart-'));
|
|
71
|
+
const options = {
|
|
72
|
+
platform: 'linux', home: join(root, 'dharma'), userHome: root,
|
|
73
|
+
workspace: join(root, 'repo'), launcher: join(root, 'repo', '.dharma', 'bin', 'dharma'),
|
|
74
|
+
policy: join(root, 'repo', '.dharma', 'approved-policy.json'), version: '0.2.101',
|
|
75
|
+
run: async () => { throw new Error('No systemd user bus'); },
|
|
76
|
+
};
|
|
77
|
+
await assert.rejects(enableRelayAutostart(options), /No systemd user bus/);
|
|
78
|
+
assert.equal((await relayAutostartStatus(options)).state, 'unavailable');
|
|
79
|
+
});
|
|
80
|
+
test('registration refuses to replace an unmanaged user startup entry', async () => {
|
|
81
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-autostart-conflict-'));
|
|
82
|
+
const unit = join(root, '.config', 'systemd', 'user', 'dharma-agent-fabric.service');
|
|
83
|
+
await mkdir(join(root, '.config', 'systemd', 'user'), { recursive: true });
|
|
84
|
+
await writeFile(unit, '[Service]\nExecStart=/custom/relay\n');
|
|
85
|
+
await assert.rejects(enableRelayAutostart({
|
|
86
|
+
platform: 'linux', home: join(root, 'dharma'), userHome: root,
|
|
87
|
+
workspace: join(root, 'repo'), launcher: join(root, 'repo', 'dharma'),
|
|
88
|
+
policy: join(root, 'repo', 'policy.json'), version: '0.2.101',
|
|
89
|
+
run: async () => ({ stdout: '' }),
|
|
90
|
+
}), /autostart_conflict/);
|
|
91
|
+
assert.equal(await readFile(unit, 'utf8'), '[Service]\nExecStart=/custom/relay\n');
|
|
92
|
+
});
|
|
93
|
+
test('Windows registration refuses an existing task without an ownership receipt', async () => {
|
|
94
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-task-conflict-'));
|
|
95
|
+
await assert.rejects(enableRelayAutostart({
|
|
96
|
+
platform: 'win32', home: join(root, 'dharma'), userHome: root,
|
|
97
|
+
workspace: 'C:\\Work', launcher: 'C:\\Work\\dharma.cmd',
|
|
98
|
+
policy: 'C:\\Work\\policy.json', version: '0.2.101',
|
|
99
|
+
run: async (_file, args) => ({ stdout: Buffer.from(args.at(-1) || '', 'base64').toString('utf16le')
|
|
100
|
+
.includes('Get-ScheduledTask') ? 'exists\n' : '' }),
|
|
101
|
+
}), /autostart_conflict/);
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=relayAutostart.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayAutostart.test.js","sourceRoot":"","sources":["../src/relayAutostart.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
export declare function relayRestartDelayMs(consecutiveShortRuns: number): number;
|
|
3
|
+
export declare function superviseRelay(input: {
|
|
4
|
+
start: () => ChildProcess;
|
|
5
|
+
signal: AbortSignal;
|
|
6
|
+
now?: () => number;
|
|
7
|
+
wait?: (delayMs: number, signal: AbortSignal) => Promise<void>;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
restarts: number;
|
|
10
|
+
}>;
|
|
11
|
+
//# sourceMappingURL=relaySupervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relaySupervisor.d.ts","sourceRoot":"","sources":["../src/relaySupervisor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,wBAAgB,mBAAmB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAExE;AAeD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,KAAK,EAAE,MAAM,YAAY,CAAC;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuChC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function relayRestartDelayMs(consecutiveShortRuns) {
|
|
2
|
+
return Math.min(30_000, 1_000 * 2 ** Math.min(Math.max(0, consecutiveShortRuns - 1), 5));
|
|
3
|
+
}
|
|
4
|
+
async function waitForRestart(delayMs, signal) {
|
|
5
|
+
if (signal.aborted)
|
|
6
|
+
return;
|
|
7
|
+
await new Promise((resolveWait) => {
|
|
8
|
+
const finish = () => {
|
|
9
|
+
clearTimeout(timer);
|
|
10
|
+
signal.removeEventListener('abort', finish);
|
|
11
|
+
resolveWait();
|
|
12
|
+
};
|
|
13
|
+
const timer = setTimeout(finish, delayMs);
|
|
14
|
+
signal.addEventListener('abort', finish, { once: true });
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export async function superviseRelay(input) {
|
|
18
|
+
const now = input.now || Date.now;
|
|
19
|
+
const wait = input.wait || waitForRestart;
|
|
20
|
+
let consecutiveShortRuns = 0;
|
|
21
|
+
let restarts = 0;
|
|
22
|
+
while (!input.signal.aborted) {
|
|
23
|
+
const startedAt = now();
|
|
24
|
+
let child;
|
|
25
|
+
try {
|
|
26
|
+
child = input.start();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
consecutiveShortRuns += 1;
|
|
30
|
+
restarts += 1;
|
|
31
|
+
await wait(relayRestartDelayMs(consecutiveShortRuns), input.signal);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
await new Promise((resolveExit) => {
|
|
35
|
+
const onAbort = () => { child.kill('SIGTERM'); };
|
|
36
|
+
const onExit = () => {
|
|
37
|
+
input.signal.removeEventListener('abort', onAbort);
|
|
38
|
+
child.removeListener('error', onExit);
|
|
39
|
+
child.removeListener('exit', onExit);
|
|
40
|
+
resolveExit();
|
|
41
|
+
};
|
|
42
|
+
child.once('error', onExit);
|
|
43
|
+
child.once('exit', onExit);
|
|
44
|
+
input.signal.addEventListener('abort', onAbort, { once: true });
|
|
45
|
+
if (input.signal.aborted)
|
|
46
|
+
onAbort();
|
|
47
|
+
});
|
|
48
|
+
if (input.signal.aborted)
|
|
49
|
+
break;
|
|
50
|
+
consecutiveShortRuns = now() - startedAt >= 60_000 ? 1 : consecutiveShortRuns + 1;
|
|
51
|
+
restarts += 1;
|
|
52
|
+
await wait(relayRestartDelayMs(consecutiveShortRuns), input.signal);
|
|
53
|
+
}
|
|
54
|
+
return { restarts };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=relaySupervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relaySupervisor.js","sourceRoot":"","sources":["../src/relaySupervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relaySupervisor.test.d.ts","sourceRoot":"","sources":["../src/relaySupervisor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFile, spawn } from 'node:child_process';
|
|
3
|
+
import { EventEmitter, once } from 'node:events';
|
|
4
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import { relayRestartDelayMs, superviseRelay } from './relaySupervisor.js';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
test('relay restart backoff is bounded', () => {
|
|
13
|
+
assert.equal(relayRestartDelayMs(1), 1_000);
|
|
14
|
+
assert.equal(relayRestartDelayMs(2), 2_000);
|
|
15
|
+
assert.equal(relayRestartDelayMs(6), 30_000);
|
|
16
|
+
assert.equal(relayRestartDelayMs(100), 30_000);
|
|
17
|
+
});
|
|
18
|
+
test('supervisor restarts unexpected exits and stops its child on shutdown', async () => {
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const waits = [];
|
|
21
|
+
let starts = 0;
|
|
22
|
+
const result = await superviseRelay({
|
|
23
|
+
signal: controller.signal,
|
|
24
|
+
wait: async (delayMs) => { waits.push(delayMs); },
|
|
25
|
+
start: () => {
|
|
26
|
+
starts += 1;
|
|
27
|
+
const child = new EventEmitter();
|
|
28
|
+
child.kill = () => {
|
|
29
|
+
queueMicrotask(() => child.emit('exit', 0, null));
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
queueMicrotask(() => {
|
|
33
|
+
if (starts === 3)
|
|
34
|
+
controller.abort();
|
|
35
|
+
else
|
|
36
|
+
child.emit('exit', 1, null);
|
|
37
|
+
});
|
|
38
|
+
return child;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
assert.equal(starts, 3);
|
|
42
|
+
assert.deepEqual(waits, [1_000, 2_000]);
|
|
43
|
+
assert.equal(result.restarts, 2);
|
|
44
|
+
});
|
|
45
|
+
test('supervisor does not start a relay after shutdown', async () => {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
controller.abort();
|
|
48
|
+
let starts = 0;
|
|
49
|
+
const result = await superviseRelay({
|
|
50
|
+
signal: controller.signal,
|
|
51
|
+
start: () => { starts += 1; throw new Error('must not start'); },
|
|
52
|
+
});
|
|
53
|
+
assert.equal(starts, 0);
|
|
54
|
+
assert.equal(result.restarts, 0);
|
|
55
|
+
});
|
|
56
|
+
test('a detached supervisor records its workspace and relay stop shuts it down', {
|
|
57
|
+
skip: process.platform === 'win32',
|
|
58
|
+
}, async () => {
|
|
59
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-relay-supervisor-process-'));
|
|
60
|
+
const home = join(root, 'home');
|
|
61
|
+
const workspace = join(root, 'repo');
|
|
62
|
+
const policyPath = join(workspace, '.dharma', 'approved-policy.json');
|
|
63
|
+
await mkdir(join(workspace, '.dharma'), { recursive: true });
|
|
64
|
+
await mkdir(join(home, 'registry'), { recursive: true });
|
|
65
|
+
await writeFile(policyPath, '{}\n');
|
|
66
|
+
await writeFile(join(home, 'device.json'), JSON.stringify({ organizationId: 'org_test', deviceId: 'device_test' }));
|
|
67
|
+
await writeFile(join(home, 'registry', 'workspaces.json'), JSON.stringify([{ path: workspace, workspaceId: 'workspace_test' }]));
|
|
68
|
+
const env = { ...process.env, DHARMA_HOME: home };
|
|
69
|
+
const bin = fileURLToPath(new URL('./bin.js', import.meta.url));
|
|
70
|
+
const child = spawn(process.execPath, [bin, 'relay', 'supervise', '--policy', policyPath], {
|
|
71
|
+
env, stdio: 'ignore', detached: true,
|
|
72
|
+
});
|
|
73
|
+
try {
|
|
74
|
+
let binding = null;
|
|
75
|
+
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
76
|
+
binding = await readFile(join(home, 'relay', 'supervisor-workspace.json'), 'utf8')
|
|
77
|
+
.then(value => JSON.parse(value)).catch(() => null);
|
|
78
|
+
if (binding)
|
|
79
|
+
break;
|
|
80
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 100));
|
|
81
|
+
}
|
|
82
|
+
assert.deepEqual(binding && { workspaceId: binding.workspaceId, policyPath: binding.policyPath }, {
|
|
83
|
+
workspaceId: 'workspace_test', policyPath,
|
|
84
|
+
});
|
|
85
|
+
const { stdout } = await execFileAsync(process.execPath, [bin, 'relay', 'stop'], { env, timeout: 10_000 });
|
|
86
|
+
assert.deepEqual(JSON.parse(stdout), { ok: true, stopped: true, vaultPreserved: true });
|
|
87
|
+
const status = await execFileAsync(process.execPath, [bin, 'status'], { env, timeout: 10_000 });
|
|
88
|
+
assert.equal(JSON.parse(status.stdout).supervisor, 'stopped');
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
if (child.exitCode === null)
|
|
92
|
+
child.kill('SIGTERM');
|
|
93
|
+
await rm(root, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
test('relay stop terminates an orphaned receiver after the supervisor exits', async () => {
|
|
97
|
+
const root = await mkdtemp(join(tmpdir(), 'dharma-relay-orphan-stop-'));
|
|
98
|
+
const home = join(root, 'home');
|
|
99
|
+
const relayDir = join(home, 'relay');
|
|
100
|
+
await mkdir(relayDir, { recursive: true });
|
|
101
|
+
const supervisor = spawn(process.execPath, ['-e', 'console.log("ready"); setInterval(() => {}, 1000)'], {
|
|
102
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
103
|
+
});
|
|
104
|
+
const receiver = spawn(process.execPath, ['-e', 'console.log("ready"); setInterval(() => {}, 1000)'], {
|
|
105
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
assert.ok(supervisor.pid);
|
|
109
|
+
assert.ok(receiver.pid);
|
|
110
|
+
await Promise.all([once(supervisor.stdout, 'data'), once(receiver.stdout, 'data')]);
|
|
111
|
+
await writeFile(join(relayDir, 'supervisor.pid'), `${supervisor.pid}\n`);
|
|
112
|
+
await writeFile(join(relayDir, 'relay.pid'), `${receiver.pid}\n`);
|
|
113
|
+
const bin = fileURLToPath(new URL('./bin.js', import.meta.url));
|
|
114
|
+
const { stdout } = await execFileAsync(process.execPath, [bin, 'relay', 'stop'], {
|
|
115
|
+
env: { ...process.env, DHARMA_HOME: home }, timeout: 10_000,
|
|
116
|
+
});
|
|
117
|
+
assert.deepEqual(JSON.parse(stdout), { ok: true, stopped: true, vaultPreserved: true });
|
|
118
|
+
assert.equal(supervisor.exitCode !== null || supervisor.signalCode !== null, true);
|
|
119
|
+
assert.equal(receiver.exitCode !== null || receiver.signalCode !== null, true);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
supervisor.kill('SIGTERM');
|
|
123
|
+
receiver.kill('SIGTERM');
|
|
124
|
+
await rm(root, { recursive: true, force: true });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=relaySupervisor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relaySupervisor.test.js","sourceRoot":"","sources":["../src/relaySupervisor.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAC5C,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;IACtF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;QAClC,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,IAAI,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/C,KAAK,EAAE,GAAG,EAAE;YACV,MAAM,IAAI,CAAC,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,YAAY,EAAkB,CAAC;YACjD,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE;gBAChB,cAAc,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBAClD,OAAO,IAAI,CAAC;YACd,CAAC,CAAC;YACF,cAAc,CAAC,GAAG,EAAE;gBAClB,IAAI,MAAM,KAAK,CAAC;oBAAE,UAAU,CAAC,KAAK,EAAE,CAAC;;oBAChC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;IAClE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,UAAU,CAAC,KAAK,EAAE,CAAC;IACnB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;QAClC,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;KACjE,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0EAA0E,EAAE;IAC/E,IAAI,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;CACnC,EAAE,KAAK,IAAI,EAAE;IACZ,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kCAAkC,CAAC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;IACtE,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IACpH,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC;IACjI,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAClD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE;QACzF,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI;KACrC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,IAAI,OAAO,GAAuD,IAAI,CAAC;QACvE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,EAAE,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YACjD,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,2BAA2B,CAAC,EAAE,MAAM,CAAC;iBAC/E,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAgD,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACrG,IAAI,OAAO;gBAAE,MAAM;YACnB,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE;YAChG,WAAW,EAAE,gBAAgB,EAAE,UAAU;SAC1C,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3G,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QACxF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAChG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAChE,CAAC;YAAS,CAAC;QACT,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;IACvF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,2BAA2B,CAAC,CAAC,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,mDAAmD,CAAC,EAAE;QACtG,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;KACpC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,mDAAmD,CAAC,EAAE;QACpG,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;KACpC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACtF,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;QACzE,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE;YAC/E,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM;SAC5D,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC;QACnF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC;IACjF,CAAC;YAAS,CAAC;QACT,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC,CAAC"}
|