@nameczz/skill-sync 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.
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/src/autoSync.d.ts +36 -0
- package/dist/src/autoSync.js +235 -0
- package/dist/src/autoSync.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +211 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/codexArchive.d.ts +38 -0
- package/dist/src/codexArchive.js +340 -0
- package/dist/src/codexArchive.js.map +1 -0
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +78 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/copy.d.ts +1 -0
- package/dist/src/copy.js +42 -0
- package/dist/src/copy.js.map +1 -0
- package/dist/src/directoryPicker.d.ts +8 -0
- package/dist/src/directoryPicker.js +49 -0
- package/dist/src/directoryPicker.js.map +1 -0
- package/dist/src/format.d.ts +2 -0
- package/dist/src/format.js +27 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/frontmatter.d.ts +5 -0
- package/dist/src/frontmatter.js +36 -0
- package/dist/src/frontmatter.js.map +1 -0
- package/dist/src/git.d.ts +25 -0
- package/dist/src/git.js +227 -0
- package/dist/src/git.js.map +1 -0
- package/dist/src/hash.d.ts +1 -0
- package/dist/src/hash.js +34 -0
- package/dist/src/hash.js.map +1 -0
- package/dist/src/importSkill.d.ts +6 -0
- package/dist/src/importSkill.js +58 -0
- package/dist/src/importSkill.js.map +1 -0
- package/dist/src/init.d.ts +5 -0
- package/dist/src/init.js +13 -0
- package/dist/src/init.js.map +1 -0
- package/dist/src/installSkill.d.ts +6 -0
- package/dist/src/installSkill.js +62 -0
- package/dist/src/installSkill.js.map +1 -0
- package/dist/src/json.d.ts +2 -0
- package/dist/src/json.js +11 -0
- package/dist/src/json.js.map +1 -0
- package/dist/src/metadata.d.ts +11 -0
- package/dist/src/metadata.js +115 -0
- package/dist/src/metadata.js.map +1 -0
- package/dist/src/paths.d.ts +22 -0
- package/dist/src/paths.js +103 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/removeLocalSkill.d.ts +5 -0
- package/dist/src/removeLocalSkill.js +79 -0
- package/dist/src/removeLocalSkill.js.map +1 -0
- package/dist/src/resolveConflict.d.ts +10 -0
- package/dist/src/resolveConflict.js +146 -0
- package/dist/src/resolveConflict.js.map +1 -0
- package/dist/src/scanner.d.ts +5 -0
- package/dist/src/scanner.js +57 -0
- package/dist/src/scanner.js.map +1 -0
- package/dist/src/server.d.ts +10 -0
- package/dist/src/server.js +494 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/sessionUsage.d.ts +14 -0
- package/dist/src/sessionUsage.js +180 -0
- package/dist/src/sessionUsage.js.map +1 -0
- package/dist/src/skillDependencies.d.ts +2 -0
- package/dist/src/skillDependencies.js +56 -0
- package/dist/src/skillDependencies.js.map +1 -0
- package/dist/src/skillMentions.d.ts +3 -0
- package/dist/src/skillMentions.js +111 -0
- package/dist/src/skillMentions.js.map +1 -0
- package/dist/src/status.d.ts +3 -0
- package/dist/src/status.js +134 -0
- package/dist/src/status.js.map +1 -0
- package/dist/src/stopSyncingSkill.d.ts +2 -0
- package/dist/src/stopSyncingSkill.js +31 -0
- package/dist/src/stopSyncingSkill.js.map +1 -0
- package/dist/src/sync.d.ts +48 -0
- package/dist/src/sync.js +741 -0
- package/dist/src/sync.js.map +1 -0
- package/dist/src/types.d.ts +84 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/updateLocalSkill.d.ts +6 -0
- package/dist/src/updateLocalSkill.js +19 -0
- package/dist/src/updateLocalSkill.js.map +1 -0
- package/dist/src/usage.d.ts +6 -0
- package/dist/src/usage.js +84 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/src/usageMonitor.d.ts +17 -0
- package/dist/src/usageMonitor.js +90 -0
- package/dist/src/usageMonitor.js.map +1 -0
- package/dist/web/assets/index-CPJdd8n0.js +59 -0
- package/dist/web/assets/index-T4bm09OX.css +2 -0
- package/dist/web/index.html +13 -0
- package/dist/web/style-options/common.css +515 -0
- package/dist/web/style-options/console.html +143 -0
- package/dist/web/style-options/desktop.html +144 -0
- package/dist/web/style-options/index.html +36 -0
- package/dist/web/style-options/workbench.html +112 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zilliz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Skill Sync
|
|
2
|
+
|
|
3
|
+
[](./.github/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@nameczz/skill-sync)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
|
|
8
|
+
A local-first web and CLI workbench for syncing AI agent skills across machines with Git. It currently supports Codex and Agents skill folders, with room for more runtimes later.
|
|
9
|
+
|
|
10
|
+
Skills are just folders on your machine. That is great for hacking, but awkward once you have more than one computer, a growing skill library, or a mix of Codex and Agents skill directories. Skill Sync gives those folders a small control plane: choose what to track, keep local copies in sync, inspect conflicts, and push changes through your own Git repository.
|
|
11
|
+
|
|
12
|
+
中文简介:Skill Sync 是一个本地优先的 skill 同步工具。你可以用自己的 Git 仓库在多台电脑之间同步 Codex / Agents skills,可视化查看本地/仓库差异、最近使用时间、Codex 归档会话,并支持自动同步。
|
|
13
|
+
|
|
14
|
+
## Screenshots
|
|
15
|
+
|
|
16
|
+
These are placeholders. Replace them with real screenshots before sharing a launch post or article.
|
|
17
|
+
|
|
18
|
+
| Skills dashboard | Conflict / compare flow |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
|  |  |
|
|
21
|
+
|
|
22
|
+
Suggested real captures:
|
|
23
|
+
- `docs/screenshots/skills-dashboard.png`
|
|
24
|
+
- `docs/screenshots/codex-archive.png`
|
|
25
|
+
- `docs/screenshots/compare-conflict.png`
|
|
26
|
+
- `docs/screenshots/dark-mode.png`
|
|
27
|
+
|
|
28
|
+
## What It Does
|
|
29
|
+
|
|
30
|
+
- Tracks selected skills from `~/.codex/skills` and `~/.agents/skills`.
|
|
31
|
+
- Syncs skill folders through a Git repository that you control.
|
|
32
|
+
- Supports one-click add, install, update, stop syncing, and local delete flows.
|
|
33
|
+
- Auto-syncs managed local skill changes and commits/pushes them to the sync repo.
|
|
34
|
+
- Pulls remote changes from another machine and applies missing or updated skills locally.
|
|
35
|
+
- Shows skill states such as in sync, local only, repo only, local changed, repo changed, and conflict.
|
|
36
|
+
- Lets you compare versions and choose which copy should win when a skill conflicts.
|
|
37
|
+
- Records skill usage from local Codex session traces and shows last-used time.
|
|
38
|
+
- Includes a Codex Archive browser for archived sessions, with soft delete, restore, and unarchive.
|
|
39
|
+
- Keeps machine-specific config and cache out of Git.
|
|
40
|
+
|
|
41
|
+
## What It Does Not Do
|
|
42
|
+
|
|
43
|
+
- It does not host your skills on a SaaS service.
|
|
44
|
+
- It does not require a central backend beyond your own Git remote.
|
|
45
|
+
- It does not delete local installed skill copies when you stop syncing a skill.
|
|
46
|
+
- It does not change how Codex loads skills; Codex still reads local skill directories.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
Prerequisites:
|
|
51
|
+
|
|
52
|
+
- Node.js `>=20`
|
|
53
|
+
- Git
|
|
54
|
+
- macOS for the native directory picker
|
|
55
|
+
- A local folder or Git clone to use as the sync repository
|
|
56
|
+
|
|
57
|
+
Install from npm:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install -g @nameczz/skill-sync
|
|
61
|
+
skill-sync serve
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or run from source:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
yarn install
|
|
68
|
+
npm run dev -- serve
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Open [http://127.0.0.1:3017](http://127.0.0.1:3017).
|
|
72
|
+
|
|
73
|
+
On first launch:
|
|
74
|
+
|
|
75
|
+
1. Choose the Git sync repository directory.
|
|
76
|
+
2. Initialize the manager.
|
|
77
|
+
3. Add local-only skills to sync.
|
|
78
|
+
4. Let auto-sync watch managed skills, or use manual actions when you want explicit control.
|
|
79
|
+
|
|
80
|
+
## Common CLI Commands
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
skill-sync status
|
|
84
|
+
skill-sync pull
|
|
85
|
+
skill-sync sync <skill-id>
|
|
86
|
+
skill-sync update-local <skill-id>
|
|
87
|
+
skill-sync stop-syncing <skill-id>
|
|
88
|
+
skill-sync serve --port 4100
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Sync Model
|
|
92
|
+
|
|
93
|
+
The sync repository stores shared state:
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
skills/ # tracked skill folders
|
|
97
|
+
metadata/skills.json # tracked skill metadata
|
|
98
|
+
metadata/usage-events.jsonl
|
|
99
|
+
.gitignore
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Your machine keeps local-only state outside Git:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
~/.skill-sync/config.json
|
|
106
|
+
~/.skill-sync/cache/
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
At a high level:
|
|
110
|
+
|
|
111
|
+
```mermaid
|
|
112
|
+
flowchart LR
|
|
113
|
+
Codex["~/.codex/skills"] --> Manager["Skill Sync"]
|
|
114
|
+
Agents["~/.agents/skills"] --> Manager
|
|
115
|
+
Manager --> Repo["Git sync repo"]
|
|
116
|
+
Repo --> GitHub["Your Git remote"]
|
|
117
|
+
Manager --> Archive["~/.codex/archived_sessions"]
|
|
118
|
+
Manager --> Cache["Local config/cache"]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Typical Workflows
|
|
122
|
+
|
|
123
|
+
**Add a local skill to sync**
|
|
124
|
+
|
|
125
|
+
1. Open the Skills page.
|
|
126
|
+
2. Find a `Local only` skill.
|
|
127
|
+
3. Click `Add to sync`.
|
|
128
|
+
4. The app copies it into the sync repo, updates metadata, commits, and pushes.
|
|
129
|
+
|
|
130
|
+
**Use another machine**
|
|
131
|
+
|
|
132
|
+
1. Clone or choose the same sync repository.
|
|
133
|
+
2. Initialize Skill Sync with that repo path.
|
|
134
|
+
3. Click `Pull`.
|
|
135
|
+
4. Apply repo changes to install missing local copies.
|
|
136
|
+
|
|
137
|
+
**Resolve a conflict**
|
|
138
|
+
|
|
139
|
+
1. Open the compare dialog for the skill.
|
|
140
|
+
2. Review the repo, Codex, and Agents versions.
|
|
141
|
+
3. Accept the version you want to keep.
|
|
142
|
+
4. The app updates metadata, commits, and pushes the resolution.
|
|
143
|
+
|
|
144
|
+
## Codex Archive
|
|
145
|
+
|
|
146
|
+
Codex App can archive sessions into `~/.codex/archived_sessions`. Skill Sync exposes those sessions in the web UI so you can:
|
|
147
|
+
|
|
148
|
+
- Search archived sessions.
|
|
149
|
+
- Preview metadata without loading the full file by default.
|
|
150
|
+
- Move an archived session to Trash.
|
|
151
|
+
- Restore from Trash.
|
|
152
|
+
- Unarchive a session back into Codex sessions.
|
|
153
|
+
|
|
154
|
+
## Documentation
|
|
155
|
+
|
|
156
|
+
Local docs site:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm run docs:dev
|
|
160
|
+
npm run docs:build
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Useful docs:
|
|
164
|
+
|
|
165
|
+
- [Getting Started](./docs/guide/getting-started.md)
|
|
166
|
+
- [Sync Model](./docs/guide/sync-model.md)
|
|
167
|
+
- [API Reference](./docs/reference/api.md)
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
Run checks:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm run typecheck
|
|
175
|
+
npm test
|
|
176
|
+
npm run build
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Run with isolated test paths:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
SKILL_SYNC_REPO=/tmp/skill-sync-repo \
|
|
183
|
+
SKILL_SYNC_CODEX_SKILLS_DIR=/tmp/skill-sync-codex \
|
|
184
|
+
SKILL_SYNC_AGENTS_SKILLS_DIR=/tmp/skill-sync-agents \
|
|
185
|
+
SKILL_SYNC_CONFIG_DIR=/tmp/skill-sync-config \
|
|
186
|
+
SKILL_SYNC_CACHE_DIR=/tmp/skill-sync-cache \
|
|
187
|
+
npm run dev -- serve
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Legacy `CSM_*` environment variables and `~/.codex-skill-manager` config are still read for compatibility.
|
|
191
|
+
|
|
192
|
+
## Publishing
|
|
193
|
+
|
|
194
|
+
The npm package is published by `.github/workflows/publish-npm.yml` using npm Trusted Publishing, so no long-lived npm publish token is needed after the package exists on npm.
|
|
195
|
+
|
|
196
|
+
1. Rename or create the GitHub repository that matches `package.json`'s `repository.url`.
|
|
197
|
+
2. Publish `@nameczz/skill-sync@0.1.0` once manually if the package does not exist yet:
|
|
198
|
+
```bash
|
|
199
|
+
npm login
|
|
200
|
+
npm publish --access=public
|
|
201
|
+
```
|
|
202
|
+
3. On npmjs.com, open the `@nameczz/skill-sync` package settings and add a Trusted Publisher:
|
|
203
|
+
- Provider: GitHub Actions
|
|
204
|
+
- Organization/user: `nameczz`
|
|
205
|
+
- Repository: `skill-sync`
|
|
206
|
+
- Workflow filename: `publish-npm.yml`
|
|
207
|
+
- Allowed action: `npm publish`
|
|
208
|
+
4. Publish future versions with a GitHub Release, or run the `Publish npm` workflow manually with `dry-run` set to `false`.
|
|
209
|
+
|
|
210
|
+
You can also configure the trusted publisher from the CLI after the first publish:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npm trust github @nameczz/skill-sync --repo nameczz/skill-sync --file publish-npm.yml --allow-publish
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The workflow installs dependencies, runs typecheck, tests, build, `npm pack --dry-run`, then publishes with OIDC-backed npm provenance.
|
|
217
|
+
|
|
218
|
+
## Project Structure
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
skill-sync/
|
|
222
|
+
├── src/ # CLI, sync, git, archive, usage scanner, server
|
|
223
|
+
├── web/ # local management UI
|
|
224
|
+
├── tests/ # Vitest coverage
|
|
225
|
+
├── docs/ # docs and screenshot placeholders
|
|
226
|
+
├── .github/ # CI workflows and GitHub metadata
|
|
227
|
+
└── README.md
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Contributing
|
|
231
|
+
|
|
232
|
+
Issues and pull requests are welcome. No special template is required; a clear description, reproduction steps for bugs, and screenshots or logs when relevant are enough.
|
|
233
|
+
|
|
234
|
+
Before opening a PR, please run:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
npm run typecheck
|
|
238
|
+
npm test
|
|
239
|
+
npm run build
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md), [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), and [SECURITY.md](./SECURITY.md) for project expectations.
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type FSWatcher } from "node:fs";
|
|
2
|
+
import type { LocalConfig, SkillRecord } from "./types.js";
|
|
3
|
+
import { type SyncSelection } from "./sync.js";
|
|
4
|
+
import type { SyncResult } from "./sync.js";
|
|
5
|
+
type AutoSyncMode = "disabled" | "watching" | "polling";
|
|
6
|
+
export type AutoSyncStatus = {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
mode: AutoSyncMode;
|
|
9
|
+
running: boolean;
|
|
10
|
+
pending: boolean;
|
|
11
|
+
lastRunStartedAt: string | null;
|
|
12
|
+
lastRunCompletedAt: string | null;
|
|
13
|
+
lastSyncedSkillIds: string[];
|
|
14
|
+
lastError: string | null;
|
|
15
|
+
watchersSupported: boolean;
|
|
16
|
+
};
|
|
17
|
+
type AutoSyncController = {
|
|
18
|
+
start: (config: LocalConfig) => Promise<void>;
|
|
19
|
+
stop: () => Promise<void>;
|
|
20
|
+
trigger: () => void;
|
|
21
|
+
getStatus: () => AutoSyncStatus;
|
|
22
|
+
};
|
|
23
|
+
type AutoSyncOptions = {
|
|
24
|
+
debounceMs?: number;
|
|
25
|
+
pollingIntervalMs?: number;
|
|
26
|
+
statusBuilder?: (config: LocalConfig) => Promise<ManagedStatusReport>;
|
|
27
|
+
sync?: (config: LocalConfig, selections: SyncSelection[]) => Promise<SyncResult>;
|
|
28
|
+
pollSignatureBuilder?: (config: LocalConfig) => Promise<string>;
|
|
29
|
+
watchProvider?: (path: string, onChange: () => void, onError: (error: unknown) => void) => FSWatcher;
|
|
30
|
+
now?: () => string;
|
|
31
|
+
};
|
|
32
|
+
type ManagedStatusReport = {
|
|
33
|
+
managed: Array<Pick<SkillRecord, "id" | "syncState" | "localSource">>;
|
|
34
|
+
};
|
|
35
|
+
export declare function createAutoSyncController(options?: AutoSyncOptions): AutoSyncController;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { existsSync, watch } from "node:fs";
|
|
2
|
+
import { scanSkills } from "./scanner.js";
|
|
3
|
+
import { buildStatusReport } from "./status.js";
|
|
4
|
+
import { syncSelectedSkills } from "./sync.js";
|
|
5
|
+
export function createAutoSyncController(options = {}) {
|
|
6
|
+
const debounceMs = options.debounceMs ?? 2000;
|
|
7
|
+
const pollingIntervalMs = options.pollingIntervalMs ?? 4000;
|
|
8
|
+
const sync = options.sync ?? syncSelectedSkills;
|
|
9
|
+
const statusBuilder = options.statusBuilder ?? buildStatusReport;
|
|
10
|
+
const pollSignatureBuilder = options.pollSignatureBuilder ?? defaultPollSignatureBuilder;
|
|
11
|
+
const watchProvider = options.watchProvider ?? ((targetPath, onChange, onError) => {
|
|
12
|
+
const watcher = watch(targetPath, { recursive: true }, () => onChange());
|
|
13
|
+
watcher.on("error", onError);
|
|
14
|
+
return watcher;
|
|
15
|
+
});
|
|
16
|
+
const now = options.now ?? (() => new Date().toISOString());
|
|
17
|
+
let currentConfig = null;
|
|
18
|
+
let state = {
|
|
19
|
+
enabled: false,
|
|
20
|
+
mode: "disabled",
|
|
21
|
+
running: false,
|
|
22
|
+
pending: false,
|
|
23
|
+
lastRunStartedAt: null,
|
|
24
|
+
lastRunCompletedAt: null,
|
|
25
|
+
lastSyncedSkillIds: [],
|
|
26
|
+
lastError: null,
|
|
27
|
+
watchersSupported: true
|
|
28
|
+
};
|
|
29
|
+
const watchers = [];
|
|
30
|
+
let pollingTimer = null;
|
|
31
|
+
let debounceTimer = null;
|
|
32
|
+
let pollingPreviousSignature = "";
|
|
33
|
+
let running = false;
|
|
34
|
+
async function start(config) {
|
|
35
|
+
if (currentConfig !== null &&
|
|
36
|
+
currentConfig.syncRepo === config.syncRepo &&
|
|
37
|
+
currentConfig.codexSkillsDir === config.codexSkillsDir &&
|
|
38
|
+
currentConfig.agentsSkillsDir === config.agentsSkillsDir) {
|
|
39
|
+
state.enabled = true;
|
|
40
|
+
state.mode = state.mode === "disabled" ? (state.watchersSupported ? "watching" : "polling") : state.mode;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await stop();
|
|
44
|
+
currentConfig = config;
|
|
45
|
+
state.enabled = true;
|
|
46
|
+
state.lastError = null;
|
|
47
|
+
state.pending = false;
|
|
48
|
+
state.lastSyncedSkillIds = [];
|
|
49
|
+
state.mode = "watching";
|
|
50
|
+
const watchersStarted = await startWatchers(config);
|
|
51
|
+
state.watchersSupported = watchersStarted;
|
|
52
|
+
state.mode = watchersStarted ? "watching" : "polling";
|
|
53
|
+
try {
|
|
54
|
+
pollingPreviousSignature = await pollSignatureBuilder(config);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
state.lastError = normalizeError(error);
|
|
58
|
+
state.watchersSupported = false;
|
|
59
|
+
state.mode = "polling";
|
|
60
|
+
startPolling(config);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!watchersStarted) {
|
|
64
|
+
startPolling(config);
|
|
65
|
+
}
|
|
66
|
+
queueSync("startup");
|
|
67
|
+
}
|
|
68
|
+
async function stop() {
|
|
69
|
+
state.enabled = false;
|
|
70
|
+
state.mode = "disabled";
|
|
71
|
+
currentConfig = null;
|
|
72
|
+
for (const watcher of watchers) {
|
|
73
|
+
watcher.close();
|
|
74
|
+
}
|
|
75
|
+
watchers.length = 0;
|
|
76
|
+
if (pollingTimer !== null) {
|
|
77
|
+
clearInterval(pollingTimer);
|
|
78
|
+
pollingTimer = null;
|
|
79
|
+
}
|
|
80
|
+
if (debounceTimer !== null) {
|
|
81
|
+
clearTimeout(debounceTimer);
|
|
82
|
+
debounceTimer = null;
|
|
83
|
+
}
|
|
84
|
+
state.pending = false;
|
|
85
|
+
state.running = false;
|
|
86
|
+
state.lastRunStartedAt = null;
|
|
87
|
+
}
|
|
88
|
+
async function startWatchers(config) {
|
|
89
|
+
const roots = [config.codexSkillsDir, config.agentsSkillsDir];
|
|
90
|
+
let startedAny = false;
|
|
91
|
+
try {
|
|
92
|
+
for (const root of roots) {
|
|
93
|
+
if (!existsSync(root)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const watcher = watchProvider(root, () => {
|
|
97
|
+
queueSync("local-change");
|
|
98
|
+
}, (error) => {
|
|
99
|
+
void fallbackToPolling(config, error);
|
|
100
|
+
});
|
|
101
|
+
watchers.push(watcher);
|
|
102
|
+
startedAny = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
await cleanupWatchers();
|
|
107
|
+
state.lastError = normalizeError(error);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (startedAny) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
async function cleanupWatchers() {
|
|
116
|
+
for (const watcher of watchers) {
|
|
117
|
+
watcher.close();
|
|
118
|
+
}
|
|
119
|
+
watchers.length = 0;
|
|
120
|
+
}
|
|
121
|
+
function startPolling(config) {
|
|
122
|
+
state.mode = "polling";
|
|
123
|
+
if (pollingTimer !== null) {
|
|
124
|
+
clearInterval(pollingTimer);
|
|
125
|
+
}
|
|
126
|
+
pollingTimer = setInterval(() => {
|
|
127
|
+
void runPoll(config);
|
|
128
|
+
}, pollingIntervalMs);
|
|
129
|
+
}
|
|
130
|
+
async function runPoll(config) {
|
|
131
|
+
if (!state.enabled || running) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const signature = await pollSignatureBuilder(config);
|
|
136
|
+
if (signature !== pollingPreviousSignature) {
|
|
137
|
+
pollingPreviousSignature = signature;
|
|
138
|
+
queueSync("poll-change");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
state.lastError = normalizeError(error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function fallbackToPolling(config, error) {
|
|
146
|
+
await cleanupWatchers();
|
|
147
|
+
pollingPreviousSignature = "";
|
|
148
|
+
state.watchersSupported = false;
|
|
149
|
+
state.mode = "polling";
|
|
150
|
+
state.lastError = normalizeError(error);
|
|
151
|
+
startPolling(config);
|
|
152
|
+
}
|
|
153
|
+
function queueSync(_reason) {
|
|
154
|
+
if (!currentConfig || !state.enabled) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
state.pending = true;
|
|
158
|
+
if (debounceTimer !== null) {
|
|
159
|
+
clearTimeout(debounceTimer);
|
|
160
|
+
}
|
|
161
|
+
debounceTimer = setTimeout(() => {
|
|
162
|
+
void runAutoSync();
|
|
163
|
+
}, debounceMs);
|
|
164
|
+
}
|
|
165
|
+
async function runAutoSync() {
|
|
166
|
+
if (!state.enabled || running || !currentConfig) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
running = true;
|
|
170
|
+
state.running = true;
|
|
171
|
+
state.pending = false;
|
|
172
|
+
state.lastError = null;
|
|
173
|
+
state.lastRunStartedAt = now();
|
|
174
|
+
try {
|
|
175
|
+
const report = await statusBuilder(currentConfig);
|
|
176
|
+
const managed = report.managed
|
|
177
|
+
.map((skill) => ({
|
|
178
|
+
id: skill.id,
|
|
179
|
+
syncState: skill.syncState,
|
|
180
|
+
localSource: skill.localSource ?? null
|
|
181
|
+
}))
|
|
182
|
+
.filter((skill) => skill.syncState === "local_modified")
|
|
183
|
+
.map((skill) => ({
|
|
184
|
+
skillId: skill.id,
|
|
185
|
+
source: normalizeSource(skill.localSource)
|
|
186
|
+
}));
|
|
187
|
+
if (managed.length === 0) {
|
|
188
|
+
state.lastSyncedSkillIds = [];
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const result = await sync(currentConfig, managed);
|
|
192
|
+
state.lastSyncedSkillIds = result.skillIds;
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
state.lastError = normalizeError(error);
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
state.running = false;
|
|
199
|
+
state.lastRunCompletedAt = now();
|
|
200
|
+
running = false;
|
|
201
|
+
if (state.pending) {
|
|
202
|
+
queueSync("coalesced");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function normalizeSource(source) {
|
|
207
|
+
if (source === "codex" || source === "agents") {
|
|
208
|
+
return source;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
function getStatus() {
|
|
213
|
+
return { ...state };
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
start,
|
|
217
|
+
stop,
|
|
218
|
+
trigger: () => queueSync("manual"),
|
|
219
|
+
getStatus
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async function defaultPollSignatureBuilder(config) {
|
|
223
|
+
const [codex, agents] = await Promise.all([scanSkills(config.codexSkillsDir, "codex"), scanSkills(config.agentsSkillsDir, "agents")]);
|
|
224
|
+
const signature = [...codex, ...agents]
|
|
225
|
+
.map((skill) => `${skill.source}:${skill.id}:${skill.hash}:${skill.modifiedAt}`)
|
|
226
|
+
.sort();
|
|
227
|
+
return signature.join("\u0000");
|
|
228
|
+
}
|
|
229
|
+
function normalizeError(error) {
|
|
230
|
+
if (error instanceof Error) {
|
|
231
|
+
return error.message;
|
|
232
|
+
}
|
|
233
|
+
return String(error);
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=autoSync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoSync.js","sourceRoot":"","sources":["../../src/autoSync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkB,KAAK,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAsB,MAAM,WAAW,CAAC;AA4CnE,MAAM,UAAU,wBAAwB,CAAC,UAA2B,EAAE;IACpE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAC9C,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,kBAAkB,CAAC;IAChD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAK,iBAAmD,CAAC;IACpG,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,IAAI,2BAA2B,CAAC;IACzF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QAChF,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7B,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAE5D,IAAI,aAAa,GAAuB,IAAI,CAAC;IAC7C,IAAI,KAAK,GAAmB;QAC1B,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,KAAK;QACd,gBAAgB,EAAE,IAAI;QACtB,kBAAkB,EAAE,IAAI;QACxB,kBAAkB,EAAE,EAAE;QACtB,SAAS,EAAE,IAAI;QACf,iBAAiB,EAAE,IAAI;KACxB,CAAC;IAEF,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,IAAI,YAAY,GAA0C,IAAI,CAAC;IAC/D,IAAI,aAAa,GAAyC,IAAI,CAAC;IAC/D,IAAI,wBAAwB,GAAG,EAAE,CAAC;IAClC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,UAAU,KAAK,CAAC,MAAmB;QACtC,IACE,aAAa,KAAK,IAAI;YACtB,aAAa,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ;YAC1C,aAAa,CAAC,cAAc,KAAK,MAAM,CAAC,cAAc;YACtD,aAAa,CAAC,eAAe,KAAK,MAAM,CAAC,eAAe,EACxD,CAAC;YACD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACrB,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YACzG,OAAO;QACT,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;QACb,aAAa,GAAG,MAAM,CAAC;QACvB,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;QAExB,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QACpD,KAAK,CAAC,iBAAiB,GAAG,eAAe,CAAC;QAC1C,KAAK,CAAC,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QAEtD,IAAI,CAAC;YACH,wBAAwB,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;YACxC,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAChC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;YACvB,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,YAAY,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAED,SAAS,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,UAAU,IAAI;QACjB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;QACxB,aAAa,GAAG,IAAI,CAAC;QAErB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;QACD,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAEpB,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,aAAa,CAAC,YAAY,CAAC,CAAC;YAC5B,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,YAAY,CAAC,aAAa,CAAC,CAAC;YAC5B,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QAED,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAChC,CAAC;IAED,KAAK,UAAU,aAAa,CAAC,MAAmB;QAC9C,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QAC9D,IAAI,UAAU,GAAG,KAAK,CAAC;QAEvB,IAAI,CAAC;YACH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtB,SAAS;gBACX,CAAC;gBAED,MAAM,OAAO,GAAG,aAAa,CAC3B,IAAI,EACJ,GAAG,EAAE;oBACH,SAAS,CAAC,cAAc,CAAC,CAAC;gBAC5B,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;oBACR,KAAK,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACxC,CAAC,CACF,CAAC;gBAEF,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,eAAe,EAAE,CAAC;YACxB,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,UAAU,eAAe;QAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;QACD,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,SAAS,YAAY,CAAC,MAAmB;QACvC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;QACvB,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,aAAa,CAAC,YAAY,CAAC,CAAC;QAC9B,CAAC;QAED,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YAC9B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,MAAmB;QACxC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;YACrD,IAAI,SAAS,KAAK,wBAAwB,EAAE,CAAC;gBAC3C,wBAAwB,GAAG,SAAS,CAAC;gBACrC,SAAS,CAAC,aAAa,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,KAAK,UAAU,iBAAiB,CAAC,MAAmB,EAAE,KAAc;QAClE,MAAM,eAAe,EAAE,CAAC;QACxB,wBAAwB,GAAG,EAAE,CAAC;QAC9B,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAChC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;QACvB,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,SAAS,SAAS,CAAC,OAAe;QAChC,IAAI,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAErB,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,KAAK,WAAW,EAAE,CAAC;QACrB,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,UAAU,WAAW;QACxB,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;YAChD,OAAO;QACT,CAAC;QAED,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,KAAK,CAAC,gBAAgB,GAAG,GAAG,EAAE,CAAC;QAE/B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO;iBAC3B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACf,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;aACvC,CAAC,CAAC;iBACF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,gBAAgB,CAAC;iBACvD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACf,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC;aAC3C,CAAC,CAAC,CAAC;YAEN,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,CAAC,kBAAkB,GAAG,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAClD,KAAK,CAAC,kBAAkB,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YACtB,KAAK,CAAC,kBAAkB,GAAG,GAAG,EAAE,CAAC;YACjC,OAAO,GAAG,KAAK,CAAC;YAEhB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,SAAS,CAAC,WAAW,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,eAAe,CAAC,MAA2C;QAClE,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC9C,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,SAAS,SAAS;QAChB,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,OAAO;QACL,KAAK;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC;QAClC,SAAS;KACV,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,2BAA2B,CAAC,MAAmB;IAC5D,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtI,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC;SACpC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;SAC/E,IAAI,EAAE,CAAC;IAEV,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
|