@clauderecallhq/cli 0.0.1 → 0.12.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 +33 -0
- package/README.md +543 -3
- package/README.public.md +523 -0
- package/dist/cli.js +362 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/activate.js +69 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/audit-secrets.js +103 -0
- package/dist/commands/audit-secrets.js.map +1 -0
- package/dist/commands/blame.js +35 -0
- package/dist/commands/blame.js.map +1 -0
- package/dist/commands/config-verification.js +18 -0
- package/dist/commands/config-verification.js.map +1 -0
- package/dist/commands/context.js +144 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/correlate.js +70 -0
- package/dist/commands/correlate.js.map +1 -0
- package/dist/commands/digest.js +78 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/health.js +62 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/index.js +247 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/install-extension.js +138 -0
- package/dist/commands/install-extension.js.map +1 -0
- package/dist/commands/installs.js +128 -0
- package/dist/commands/installs.js.map +1 -0
- package/dist/commands/license.js +39 -0
- package/dist/commands/license.js.map +1 -0
- package/dist/commands/list.js +47 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.js +29 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/open.js +28 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/paste.js +154 -0
- package/dist/commands/paste.js.map +1 -0
- package/dist/commands/projects.js +36 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/search.js +67 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/semantic.js +173 -0
- package/dist/commands/semantic.js.map +1 -0
- package/dist/commands/show.js +121 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/start.js +47 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/stats.js +133 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/status.js +45 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.js +29 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/thread.js +396 -0
- package/dist/commands/thread.js.map +1 -0
- package/dist/context/formatter.js +103 -0
- package/dist/context/formatter.js.map +1 -0
- package/dist/daemon/auto-tag-config.js +103 -0
- package/dist/daemon/auto-tag-config.js.map +1 -0
- package/dist/daemon/auto-tag-config.test.js +72 -0
- package/dist/daemon/auto-tag-config.test.js.map +1 -0
- package/dist/daemon/auto-title-config.js +70 -0
- package/dist/daemon/auto-title-config.js.map +1 -0
- package/dist/daemon/bulk-title-jobs.js +170 -0
- package/dist/daemon/bulk-title-jobs.js.map +1 -0
- package/dist/daemon/correlator.js +320 -0
- package/dist/daemon/correlator.js.map +1 -0
- package/dist/daemon/discover.js +316 -0
- package/dist/daemon/discover.js.map +1 -0
- package/dist/daemon/editor-detection.js +186 -0
- package/dist/daemon/editor-detection.js.map +1 -0
- package/dist/daemon/entrypoint.js +55 -0
- package/dist/daemon/entrypoint.js.map +1 -0
- package/dist/daemon/git-correlator.js +256 -0
- package/dist/daemon/git-correlator.js.map +1 -0
- package/dist/daemon/mcp-installer.js +108 -0
- package/dist/daemon/mcp-installer.js.map +1 -0
- package/dist/daemon/onboarding-state.js +140 -0
- package/dist/daemon/onboarding-state.js.map +1 -0
- package/dist/daemon/pidfile.js +57 -0
- package/dist/daemon/pidfile.js.map +1 -0
- package/dist/daemon/ports.js +48 -0
- package/dist/daemon/ports.js.map +1 -0
- package/dist/daemon/scanProgressRegistry.js +62 -0
- package/dist/daemon/scanProgressRegistry.js.map +1 -0
- package/dist/daemon/server.js +2010 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/tag-scanner/anthropic-client.js +40 -0
- package/dist/daemon/tag-scanner/anthropic-client.js.map +1 -0
- package/dist/daemon/tag-scanner/autopilot.js +131 -0
- package/dist/daemon/tag-scanner/autopilot.js.map +1 -0
- package/dist/daemon/tag-scanner/claude-cli-driver.js +250 -0
- package/dist/daemon/tag-scanner/claude-cli-driver.js.map +1 -0
- package/dist/daemon/tag-scanner/orchestrator.js +88 -0
- package/dist/daemon/tag-scanner/orchestrator.js.map +1 -0
- package/dist/daemon/tag-scanner/prompt.js +46 -0
- package/dist/daemon/tag-scanner/prompt.js.map +1 -0
- package/dist/daemon/tag-scanner/prompt.test.js +48 -0
- package/dist/daemon/tag-scanner/prompt.test.js.map +1 -0
- package/dist/daemon/tag-scanner/scan-state.js +49 -0
- package/dist/daemon/tag-scanner/scan-state.js.map +1 -0
- package/dist/daemon/tag-scanner/session-fetcher.js +82 -0
- package/dist/daemon/tag-scanner/session-fetcher.js.map +1 -0
- package/dist/daemon/tag-scanner/session-fetcher.test.js +34 -0
- package/dist/daemon/tag-scanner/session-fetcher.test.js.map +1 -0
- package/dist/daemon/tag-scanner/validator.js +50 -0
- package/dist/daemon/tag-scanner/validator.js.map +1 -0
- package/dist/daemon/tag-scanner/validator.test.js +41 -0
- package/dist/daemon/tag-scanner/validator.test.js.map +1 -0
- package/dist/daemon/terminal-registry.js +443 -0
- package/dist/daemon/terminal-registry.js.map +1 -0
- package/dist/daemon/ui.js +64 -0
- package/dist/daemon/ui.js.map +1 -0
- package/dist/daemon/watcher.js +256 -0
- package/dist/daemon/watcher.js.map +1 -0
- package/dist/db/client.js +22 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/schema.js +496 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/license/api-base.js +13 -0
- package/dist/license/api-base.js.map +1 -0
- package/dist/license/manager.js +43 -0
- package/dist/license/manager.js.map +1 -0
- package/dist/license/public-key.js +19 -0
- package/dist/license/public-key.js.map +1 -0
- package/dist/license/storage.js +27 -0
- package/dist/license/storage.js.map +1 -0
- package/dist/license/verify.js +23 -0
- package/dist/license/verify.js.map +1 -0
- package/dist/mcp/audit.js +126 -0
- package/dist/mcp/audit.js.map +1 -0
- package/dist/mcp/prompts.js +180 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/server.js +502 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/thread-tools.js +363 -0
- package/dist/mcp/thread-tools.js.map +1 -0
- package/dist/mcp/write-tools.js +239 -0
- package/dist/mcp/write-tools.js.map +1 -0
- package/dist/parser/jsonl.js +150 -0
- package/dist/parser/jsonl.js.map +1 -0
- package/dist/semantic/chunker.js +47 -0
- package/dist/semantic/chunker.js.map +1 -0
- package/dist/semantic/config.js +74 -0
- package/dist/semantic/config.js.map +1 -0
- package/dist/semantic/embedder.js +54 -0
- package/dist/semantic/embedder.js.map +1 -0
- package/dist/semantic/fusion.js +38 -0
- package/dist/semantic/fusion.js.map +1 -0
- package/dist/semantic/model-download.js +69 -0
- package/dist/semantic/model-download.js.map +1 -0
- package/dist/semantic/pipeline.js +375 -0
- package/dist/semantic/pipeline.js.map +1 -0
- package/dist/semantic/query.js +42 -0
- package/dist/semantic/query.js.map +1 -0
- package/dist/semantic/worker.js +78 -0
- package/dist/semantic/worker.js.map +1 -0
- package/dist/stats/backfill.js +151 -0
- package/dist/stats/backfill.js.map +1 -0
- package/dist/stats/health.js +102 -0
- package/dist/stats/health.js.map +1 -0
- package/dist/stats/query.js +385 -0
- package/dist/stats/query.js.map +1 -0
- package/dist/utils/aliases.js +107 -0
- package/dist/utils/aliases.js.map +1 -0
- package/dist/utils/autoCollections.js +635 -0
- package/dist/utils/autoCollections.js.map +1 -0
- package/dist/utils/autoTitle.js +348 -0
- package/dist/utils/autoTitle.js.map +1 -0
- package/dist/utils/collections.js +446 -0
- package/dist/utils/collections.js.map +1 -0
- package/dist/utils/format.js +46 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/notes.js +270 -0
- package/dist/utils/notes.js.map +1 -0
- package/dist/utils/paths.js +50 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/pricing.js +257 -0
- package/dist/utils/pricing.js.map +1 -0
- package/dist/utils/secret-scanner.js +166 -0
- package/dist/utils/secret-scanner.js.map +1 -0
- package/dist/utils/sessionLabel.js +64 -0
- package/dist/utils/sessionLabel.js.map +1 -0
- package/dist/utils/tags.js +97 -0
- package/dist/utils/tags.js.map +1 -0
- package/dist/utils/thread-context.js +129 -0
- package/dist/utils/thread-context.js.map +1 -0
- package/dist/utils/threadFilter.js +18 -0
- package/dist/utils/threadFilter.js.map +1 -0
- package/dist/utils/threads-titler.js +298 -0
- package/dist/utils/threads-titler.js.map +1 -0
- package/dist/utils/threads.js +383 -0
- package/dist/utils/threads.js.map +1 -0
- package/dist/utils/usage.js +76 -0
- package/dist/utils/usage.js.map +1 -0
- package/dist/verification/compute.js +88 -0
- package/dist/verification/compute.js.map +1 -0
- package/dist/verification/config.js +34 -0
- package/dist/verification/config.js.map +1 -0
- package/dist/web/assets/index-CIr6J4Fw.js +1201 -0
- package/dist/web/assets/index-Ctc8g9Jw.css +1 -0
- package/dist/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/dist/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/dist/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
- package/dist/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-700-normal-CjBOestx.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-700-normal-DrXBdSj3.woff +0 -0
- package/dist/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/dist/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/dist/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
- package/dist/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-ext-700-normal-BjwYoWNd.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-ext-700-normal-LO58E6JB.woff +0 -0
- package/dist/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/dist/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/dist/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/dist/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/dist/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
- package/dist/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
- package/dist/web/assets/inter-greek-700-normal-BUv2fZ6O.woff +0 -0
- package/dist/web/assets/inter-greek-700-normal-C3JjAnD8.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/dist/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/dist/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
- package/dist/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-700-normal-BoQ6DsYi.woff +0 -0
- package/dist/web/assets/inter-greek-ext-700-normal-qfdV9bQt.woff2 +0 -0
- package/dist/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/dist/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/dist/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/dist/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/dist/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/dist/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/dist/web/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
- package/dist/web/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/dist/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/dist/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
- package/dist/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-700-normal-Ca8adRJv.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-700-normal-TidjK2hL.woff +0 -0
- package/dist/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/dist/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/dist/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
- package/dist/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-700-normal-BZaoP0fm.woff +0 -0
- package/dist/web/assets/inter-vietnamese-700-normal-DlLaEgI2.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/web/favicon.svg +9 -0
- package/dist/web/index.html +15 -0
- package/package.json +80 -9
- package/bin/cli.js +0 -12
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
/**
|
|
6
|
+
* Auto-tag scanner config. Persisted as `{ autoTag: {...} }` inside
|
|
7
|
+
* `$RECALL_HOME/config.json` (default `~/.recall/config.json`).
|
|
8
|
+
*
|
|
9
|
+
* The file is written with mode 0600 because it may contain an Anthropic
|
|
10
|
+
* API key. Reads are tolerant: a missing or malformed file yields defaults
|
|
11
|
+
* rather than throwing — the daemon must start even if config is broken.
|
|
12
|
+
*
|
|
13
|
+
* Path resolution is lazy (re-reads `process.env.RECALL_HOME` on every call)
|
|
14
|
+
* so tests can redirect the data root per-test without a module-cache dance.
|
|
15
|
+
* `RECALL_HOME` in `src/utils/paths.ts` is captured at module-load and stays
|
|
16
|
+
* authoritative for every other consumer; this module intentionally uses its
|
|
17
|
+
* own lazy lookup so it stays independent and test-friendly.
|
|
18
|
+
*/
|
|
19
|
+
function recallHome() {
|
|
20
|
+
return process.env.RECALL_HOME ?? join(homedir(), '.recall');
|
|
21
|
+
}
|
|
22
|
+
function ensureHome() {
|
|
23
|
+
const home = recallHome();
|
|
24
|
+
if (!existsSync(home))
|
|
25
|
+
mkdirSync(home, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
function configFile() {
|
|
28
|
+
return join(recallHome(), 'config.json');
|
|
29
|
+
}
|
|
30
|
+
export const AutoTagConfigSchema = z.object({
|
|
31
|
+
enabled: z.boolean().default(false),
|
|
32
|
+
backend: z.enum(['api', 'mcp']).default('api'),
|
|
33
|
+
apiKey: z.string().optional(),
|
|
34
|
+
model: z.string().default('claude-opus-4-7'),
|
|
35
|
+
maxTagsPerSession: z.number().int().min(1).max(10).default(4),
|
|
36
|
+
minTagsPerSession: z.number().int().min(1).max(10).default(2),
|
|
37
|
+
/**
|
|
38
|
+
* When true, daemon background-scans untagged sessions (existing + new)
|
|
39
|
+
* and applies tags automatically — no modal, no review. Only takes effect
|
|
40
|
+
* when backend='api' and an apiKey is set; MCP mode can't be driven by
|
|
41
|
+
* the daemon because it needs the user's agent to make tool calls.
|
|
42
|
+
*/
|
|
43
|
+
autopilot: z.boolean().default(false),
|
|
44
|
+
});
|
|
45
|
+
const DEFAULTS = {
|
|
46
|
+
enabled: false,
|
|
47
|
+
backend: 'api',
|
|
48
|
+
model: 'claude-opus-4-7',
|
|
49
|
+
maxTagsPerSession: 4,
|
|
50
|
+
minTagsPerSession: 2,
|
|
51
|
+
autopilot: false,
|
|
52
|
+
};
|
|
53
|
+
function readFileShape() {
|
|
54
|
+
const path = configFile();
|
|
55
|
+
if (!existsSync(path))
|
|
56
|
+
return {};
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error('[auto-tag-config] failed to parse config.json, using defaults:', err);
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function readAutoTagConfig() {
|
|
66
|
+
const raw = readFileShape().autoTag;
|
|
67
|
+
if (!raw)
|
|
68
|
+
return { ...DEFAULTS };
|
|
69
|
+
const parsed = AutoTagConfigSchema.safeParse({ ...DEFAULTS, ...raw });
|
|
70
|
+
return parsed.success ? parsed.data : { ...DEFAULTS };
|
|
71
|
+
}
|
|
72
|
+
export function writeAutoTagConfig(patch) {
|
|
73
|
+
ensureHome();
|
|
74
|
+
const existing = readFileShape();
|
|
75
|
+
const merged = AutoTagConfigSchema.parse({
|
|
76
|
+
...DEFAULTS,
|
|
77
|
+
...(existing.autoTag ?? {}),
|
|
78
|
+
...patch,
|
|
79
|
+
});
|
|
80
|
+
const next = { ...existing, autoTag: merged };
|
|
81
|
+
const path = configFile();
|
|
82
|
+
writeFileSync(path, JSON.stringify(next, null, 2));
|
|
83
|
+
try {
|
|
84
|
+
chmodSync(path, 0o600);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error('[auto-tag-config] chmod 0600 failed (continuing):', err);
|
|
88
|
+
}
|
|
89
|
+
return merged;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Shape for HTTP `GET /api/config/auto-tag`. Never exposes the raw apiKey over
|
|
93
|
+
* the wire; instead emits a marker and a `hasApiKey` boolean.
|
|
94
|
+
*/
|
|
95
|
+
export function redactForApi(cfg) {
|
|
96
|
+
const { apiKey, ...rest } = cfg;
|
|
97
|
+
return {
|
|
98
|
+
...rest,
|
|
99
|
+
apiKey: apiKey ? 'sk-ant-…' : null,
|
|
100
|
+
hasApiKey: Boolean(apiKey),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=auto-tag-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-tag-config.js","sourceRoot":"","sources":["../../src/daemon/auto-tag-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, rmSync, existsSync, statSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
describe('auto-tag-config', () => {
|
|
7
|
+
let tmpHome;
|
|
8
|
+
let prevHome;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tmpHome = mkdtempSync(join(tmpdir(), 'recall-cfg-'));
|
|
11
|
+
prevHome = process.env.RECALL_HOME;
|
|
12
|
+
process.env.RECALL_HOME = tmpHome;
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
if (prevHome)
|
|
16
|
+
process.env.RECALL_HOME = prevHome;
|
|
17
|
+
else
|
|
18
|
+
delete process.env.RECALL_HOME;
|
|
19
|
+
rmSync(tmpHome, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
it('returns defaults when config file does not exist', async () => {
|
|
22
|
+
const { readAutoTagConfig } = await import('./auto-tag-config.js');
|
|
23
|
+
const cfg = readAutoTagConfig();
|
|
24
|
+
assert.equal(cfg.enabled, false);
|
|
25
|
+
assert.equal(cfg.backend, 'api');
|
|
26
|
+
assert.equal(cfg.model, 'claude-opus-4-7');
|
|
27
|
+
});
|
|
28
|
+
it('round-trips writes', async () => {
|
|
29
|
+
const { readAutoTagConfig, writeAutoTagConfig } = await import('./auto-tag-config.js');
|
|
30
|
+
writeAutoTagConfig({
|
|
31
|
+
enabled: true,
|
|
32
|
+
backend: 'api',
|
|
33
|
+
apiKey: 'sk-ant-test',
|
|
34
|
+
model: 'claude-opus-4-7',
|
|
35
|
+
maxTagsPerSession: 4,
|
|
36
|
+
minTagsPerSession: 2,
|
|
37
|
+
});
|
|
38
|
+
const got = readAutoTagConfig();
|
|
39
|
+
assert.equal(got.enabled, true);
|
|
40
|
+
assert.equal(got.apiKey, 'sk-ant-test');
|
|
41
|
+
});
|
|
42
|
+
it('writes with 0600 file mode', async () => {
|
|
43
|
+
const { writeAutoTagConfig } = await import('./auto-tag-config.js');
|
|
44
|
+
writeAutoTagConfig({
|
|
45
|
+
enabled: true,
|
|
46
|
+
backend: 'api',
|
|
47
|
+
apiKey: 'secret',
|
|
48
|
+
model: 'claude-opus-4-7',
|
|
49
|
+
maxTagsPerSession: 4,
|
|
50
|
+
minTagsPerSession: 2,
|
|
51
|
+
});
|
|
52
|
+
const path = join(tmpHome, 'config.json');
|
|
53
|
+
assert.equal(existsSync(path), true);
|
|
54
|
+
const mode = statSync(path).mode & 0o777;
|
|
55
|
+
assert.equal(mode, 0o600);
|
|
56
|
+
});
|
|
57
|
+
it('redacts apiKey via redactForApi', async () => {
|
|
58
|
+
const { redactForApi } = await import('./auto-tag-config.js');
|
|
59
|
+
const redacted = redactForApi({
|
|
60
|
+
enabled: true,
|
|
61
|
+
backend: 'api',
|
|
62
|
+
apiKey: 'sk-ant-XXXXX',
|
|
63
|
+
model: 'claude-opus-4-7',
|
|
64
|
+
maxTagsPerSession: 4,
|
|
65
|
+
minTagsPerSession: 2,
|
|
66
|
+
autopilot: false,
|
|
67
|
+
});
|
|
68
|
+
assert.equal(redacted.apiKey, 'sk-ant-…');
|
|
69
|
+
assert.equal(redacted.hasApiKey, true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=auto-tag-config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-tag-config.test.js","sourceRoot":"","sources":["../../src/daemon/auto-tag-config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
/**
|
|
6
|
+
* v0.14b (T5) — auto-title config, persisted as `{ autoTitle: {...} }` inside
|
|
7
|
+
* `$RECALL_HOME/config.json` alongside the existing `autoTag` block.
|
|
8
|
+
*
|
|
9
|
+
* Two booleans:
|
|
10
|
+
* - `heuristicEnabled` (default true) — indexer-time heuristic pass.
|
|
11
|
+
* - `agentEnabled` (default false) — `claude -p` summarisation path.
|
|
12
|
+
* OFF by default because it spawns a subprocess and uses the user's
|
|
13
|
+
* subscription. The UI's ✨ Generate title button flips this per-click
|
|
14
|
+
* after an explicit confirmation, identical to the auto-tag opt-in.
|
|
15
|
+
*
|
|
16
|
+
* Path resolution mirrors `auto-tag-config.ts`: lazy re-read of
|
|
17
|
+
* `process.env.RECALL_HOME` on every call so tests can redirect the data
|
|
18
|
+
* root per-test without a module-cache dance.
|
|
19
|
+
*/
|
|
20
|
+
function recallHome() {
|
|
21
|
+
return process.env.RECALL_HOME ?? join(homedir(), '.recall');
|
|
22
|
+
}
|
|
23
|
+
function ensureHome() {
|
|
24
|
+
const home = recallHome();
|
|
25
|
+
if (!existsSync(home))
|
|
26
|
+
mkdirSync(home, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
function configFile() {
|
|
29
|
+
return join(recallHome(), 'config.json');
|
|
30
|
+
}
|
|
31
|
+
export const AutoTitleConfigSchema = z.object({
|
|
32
|
+
heuristicEnabled: z.boolean().default(true),
|
|
33
|
+
agentEnabled: z.boolean().default(false),
|
|
34
|
+
});
|
|
35
|
+
const DEFAULTS = {
|
|
36
|
+
heuristicEnabled: true,
|
|
37
|
+
agentEnabled: false,
|
|
38
|
+
};
|
|
39
|
+
function readFileShape() {
|
|
40
|
+
const path = configFile();
|
|
41
|
+
if (!existsSync(path))
|
|
42
|
+
return {};
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
console.error('[auto-title-config] failed to parse config.json, using defaults:', err);
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function readAutoTitleConfig() {
|
|
52
|
+
const raw = readFileShape().autoTitle;
|
|
53
|
+
if (!raw)
|
|
54
|
+
return { ...DEFAULTS };
|
|
55
|
+
const parsed = AutoTitleConfigSchema.safeParse({ ...DEFAULTS, ...raw });
|
|
56
|
+
return parsed.success ? parsed.data : { ...DEFAULTS };
|
|
57
|
+
}
|
|
58
|
+
export function writeAutoTitleConfig(patch) {
|
|
59
|
+
ensureHome();
|
|
60
|
+
const existing = readFileShape();
|
|
61
|
+
const merged = AutoTitleConfigSchema.parse({
|
|
62
|
+
...DEFAULTS,
|
|
63
|
+
...(existing.autoTitle ?? {}),
|
|
64
|
+
...patch,
|
|
65
|
+
});
|
|
66
|
+
const next = { ...existing, autoTitle: merged };
|
|
67
|
+
writeFileSync(configFile(), JSON.stringify(next, null, 2));
|
|
68
|
+
return merged;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=auto-title-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-title-config.js","sourceRoot":"","sources":["../../src/daemon/auto-title-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;GAcG;AAEH,SAAS,UAAU;IACjB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACzC,CAAC,CAAC;AAIH,MAAM,QAAQ,GAAoB;IAChC,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,KAAK;CACpB,CAAC;AAOF,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAc,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,kEAAkE,EAAE,GAAG,CAAC,CAAC;QACvF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,SAAS,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAA+B;IAClE,UAAU,EAAE,CAAC;IACb,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC;QACzC,GAAG,QAAQ;QACX,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;QAC7B,GAAG,KAAK;KACT,CAAC,CAAC;IACH,MAAM,IAAI,GAAc,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAC3D,aAAa,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { generateThreadTitles, } from '../utils/threads-titler.js';
|
|
3
|
+
const REGISTRY = new Map();
|
|
4
|
+
const TTL_AFTER_DONE_MS = 5 * 60 * 1000;
|
|
5
|
+
function pushEvent(state, kind, data) {
|
|
6
|
+
const id = state.events.length + 1;
|
|
7
|
+
state.events.push({ id, kind, data });
|
|
8
|
+
for (const w of state.waiters)
|
|
9
|
+
w.resolve();
|
|
10
|
+
state.waiters.clear();
|
|
11
|
+
}
|
|
12
|
+
function scheduleCleanup(state) {
|
|
13
|
+
if (state.cleanupTimer)
|
|
14
|
+
clearTimeout(state.cleanupTimer);
|
|
15
|
+
state.cleanupTimer = setTimeout(() => {
|
|
16
|
+
REGISTRY.delete(state.jobId);
|
|
17
|
+
}, TTL_AFTER_DONE_MS);
|
|
18
|
+
}
|
|
19
|
+
function snapshot(state) {
|
|
20
|
+
let done = 0;
|
|
21
|
+
let skipped = 0;
|
|
22
|
+
let failed = 0;
|
|
23
|
+
for (const e of state.events) {
|
|
24
|
+
if (e.kind === 'progress')
|
|
25
|
+
done += 1;
|
|
26
|
+
if (e.kind === 'skipped')
|
|
27
|
+
skipped += 1;
|
|
28
|
+
if (e.kind === 'error')
|
|
29
|
+
failed += 1;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
jobId: state.jobId,
|
|
33
|
+
threadId: state.threadId,
|
|
34
|
+
status: state.status,
|
|
35
|
+
startedAt: state.startedAt,
|
|
36
|
+
endedAt: state.endedAt,
|
|
37
|
+
total: state.total,
|
|
38
|
+
done,
|
|
39
|
+
skipped,
|
|
40
|
+
failed,
|
|
41
|
+
result: state.result,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Spawn a detached bulk-title walk. Returns the jobId immediately. The walk
|
|
46
|
+
* runs on the daemon's event loop and pushes progress events into the
|
|
47
|
+
* registry; SSE subscribers pull from there.
|
|
48
|
+
*/
|
|
49
|
+
export function startBulkTitleJob(input) {
|
|
50
|
+
const jobId = randomUUID();
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const state = {
|
|
53
|
+
jobId,
|
|
54
|
+
threadId: input.threadId,
|
|
55
|
+
status: 'running',
|
|
56
|
+
startedAt: new Date().toISOString(),
|
|
57
|
+
endedAt: null,
|
|
58
|
+
total: 0,
|
|
59
|
+
events: [],
|
|
60
|
+
waiters: new Set(),
|
|
61
|
+
controller,
|
|
62
|
+
result: null,
|
|
63
|
+
cleanupTimer: null,
|
|
64
|
+
};
|
|
65
|
+
REGISTRY.set(jobId, state);
|
|
66
|
+
// Detached: the walk runs to completion or until cancelled. Errors thrown
|
|
67
|
+
// by generateThreadTitles itself (thread not found, fatal db error) end up
|
|
68
|
+
// as a synthetic 'done' event with failed entries so subscribers always
|
|
69
|
+
// get a terminal event.
|
|
70
|
+
//
|
|
71
|
+
// The leading `await Promise.resolve()` yields once so the caller has time
|
|
72
|
+
// to bind the returned jobId before the walk starts. Without it, the spawn
|
|
73
|
+
// override could fire (and call cancelJob with a stale jobId) inside the
|
|
74
|
+
// same sync stack as `startBulkTitleJob(...)`, which is what the cancel
|
|
75
|
+
// test exercises.
|
|
76
|
+
void (async () => {
|
|
77
|
+
await Promise.resolve();
|
|
78
|
+
try {
|
|
79
|
+
const result = await generateThreadTitles(input.threadId, {
|
|
80
|
+
force: input.force ?? false,
|
|
81
|
+
signal: controller.signal,
|
|
82
|
+
model: input.model,
|
|
83
|
+
onProgress: (ev) => {
|
|
84
|
+
state.total = ev.total;
|
|
85
|
+
pushEvent(state, 'progress', ev);
|
|
86
|
+
},
|
|
87
|
+
onSkipped: (ev) => {
|
|
88
|
+
pushEvent(state, 'skipped', ev);
|
|
89
|
+
},
|
|
90
|
+
onFailed: (ev) => {
|
|
91
|
+
pushEvent(state, 'error', ev);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
state.result = result;
|
|
95
|
+
state.status = controller.signal.aborted ? 'cancelled' : 'done';
|
|
96
|
+
state.endedAt = new Date().toISOString();
|
|
97
|
+
pushEvent(state, 'done', result);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const message = err instanceof Error ? err.message : String(err ?? 'unknown error');
|
|
101
|
+
state.result = {
|
|
102
|
+
generated: [],
|
|
103
|
+
skipped: [],
|
|
104
|
+
failed: [{ sessionId: input.threadId, error: message }],
|
|
105
|
+
};
|
|
106
|
+
state.status = 'failed';
|
|
107
|
+
state.endedAt = new Date().toISOString();
|
|
108
|
+
pushEvent(state, 'done', state.result);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
scheduleCleanup(state);
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
return jobId;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Async iterator over a job's events. Resumes from `afterId` if provided
|
|
118
|
+
* (use `Last-Event-ID` from the SSE protocol). Yields buffered events first,
|
|
119
|
+
* then awaits new ones until the job emits `done`. The consumer is expected
|
|
120
|
+
* to break on the `done` event; the iterator itself does not exit early so
|
|
121
|
+
* heartbeats can interleave.
|
|
122
|
+
*/
|
|
123
|
+
export async function* subscribeJob(jobId, afterId = 0) {
|
|
124
|
+
const state = REGISTRY.get(jobId);
|
|
125
|
+
if (!state)
|
|
126
|
+
return;
|
|
127
|
+
let cursor = afterId;
|
|
128
|
+
while (true) {
|
|
129
|
+
while (cursor < state.events.length) {
|
|
130
|
+
const ev = state.events[cursor];
|
|
131
|
+
cursor += 1;
|
|
132
|
+
yield ev;
|
|
133
|
+
if (ev.kind === 'done')
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (state.endedAt)
|
|
137
|
+
return;
|
|
138
|
+
await new Promise((resolve) => {
|
|
139
|
+
state.waiters.add({ resolve });
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export function cancelJob(jobId) {
|
|
144
|
+
const state = REGISTRY.get(jobId);
|
|
145
|
+
if (!state)
|
|
146
|
+
return false;
|
|
147
|
+
if (state.status !== 'running')
|
|
148
|
+
return false;
|
|
149
|
+
state.controller.abort();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
export function getJobSnapshot(jobId) {
|
|
153
|
+
const state = REGISTRY.get(jobId);
|
|
154
|
+
if (!state)
|
|
155
|
+
return null;
|
|
156
|
+
return snapshot(state);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Test-only helper: clear every job (useful between tests so registry
|
|
160
|
+
* residue from one test does not leak into the next). Not exported from a
|
|
161
|
+
* module index; tests reach in directly.
|
|
162
|
+
*/
|
|
163
|
+
export function __resetForTests() {
|
|
164
|
+
for (const state of REGISTRY.values()) {
|
|
165
|
+
if (state.cleanupTimer)
|
|
166
|
+
clearTimeout(state.cleanupTimer);
|
|
167
|
+
}
|
|
168
|
+
REGISTRY.clear();
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=bulk-title-jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-title-jobs.js","sourceRoot":"","sources":["../../src/daemon/bulk-title-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,oBAAoB,GAKrB,MAAM,4BAA4B,CAAC;AAsEpC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;AAC7C,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAExC,SAAS,SAAS,CAAC,KAAe,EAAE,IAAwB,EAAE,IAAa;IACzE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACnC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO;QAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,eAAe,CAAC,KAAe;IACtC,IAAI,KAAK,CAAC,YAAY;QAAE,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACzD,KAAK,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;QACnC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAe;IAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;YAAE,IAAI,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI;QACJ,OAAO;QACP,MAAM;QACN,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC;AACJ,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA6B;IAC7D,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAa;QACtB,KAAK;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,UAAU;QACV,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI;KACnB,CAAC;IACF,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAE3B,0EAA0E;IAC1E,2EAA2E;IAC3E,wEAAwE;IACxE,wBAAwB;IACxB,EAAE;IACF,2EAA2E;IAC3E,2EAA2E;IAC3E,yEAAyE;IACzE,wEAAwE;IACxE,kBAAkB;IAClB,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,QAAQ,EAAE;gBACxD,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK;gBAC3B,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU,EAAE,CAAC,EAAiB,EAAE,EAAE;oBAChC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;oBACvB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;gBACnC,CAAC;gBACD,SAAS,EAAE,CAAC,EAAgB,EAAE,EAAE;oBAC9B,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;gBAClC,CAAC;gBACD,QAAQ,EAAE,CAAC,EAAe,EAAE,EAAE;oBAC5B,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;gBAChC,CAAC;aACF,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;YACtB,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;YAChE,KAAK,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACzC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GACX,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,CAAC;YACtE,KAAK,CAAC,MAAM,GAAG;gBACb,SAAS,EAAE,EAAE;gBACb,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;aACxD,CAAC;YACF,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;YACxB,KAAK,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACzC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;gBAAS,CAAC;YACT,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY,CACjC,KAAa,EACb,OAAO,GAAG,CAAC;IAEX,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,IAAI,MAAM,GAAG,OAAO,CAAC;IACrB,OAAO,IAAI,EAAE,CAAC;QACZ,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACpC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,IAAI,CAAC,CAAC;YACZ,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM;gBAAE,OAAO;QACjC,CAAC;QACD,IAAI,KAAK,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,YAAY;YAAE,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC;IACD,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
|