@nocoo/otter 1.0.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/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +365 -0
- package/dist/cli.js.map +1 -0
- package/dist/collectors/applications.d.ts +19 -0
- package/dist/collectors/applications.d.ts.map +1 -0
- package/dist/collectors/applications.js +51 -0
- package/dist/collectors/applications.js.map +1 -0
- package/dist/collectors/base.d.ts +52 -0
- package/dist/collectors/base.d.ts.map +1 -0
- package/dist/collectors/base.js +186 -0
- package/dist/collectors/base.js.map +1 -0
- package/dist/collectors/claude-config.d.ts +39 -0
- package/dist/collectors/claude-config.d.ts.map +1 -0
- package/dist/collectors/claude-config.js +124 -0
- package/dist/collectors/claude-config.js.map +1 -0
- package/dist/collectors/homebrew.d.ts +16 -0
- package/dist/collectors/homebrew.d.ts.map +1 -0
- package/dist/collectors/homebrew.js +43 -0
- package/dist/collectors/homebrew.js.map +1 -0
- package/dist/collectors/index.d.ts +21 -0
- package/dist/collectors/index.d.ts.map +1 -0
- package/dist/collectors/index.js +28 -0
- package/dist/collectors/index.js.map +1 -0
- package/dist/collectors/opencode-config.d.ts +21 -0
- package/dist/collectors/opencode-config.d.ts.map +1 -0
- package/dist/collectors/opencode-config.js +88 -0
- package/dist/collectors/opencode-config.js.map +1 -0
- package/dist/collectors/shell-config.d.ts +24 -0
- package/dist/collectors/shell-config.d.ts.map +1 -0
- package/dist/collectors/shell-config.js +133 -0
- package/dist/collectors/shell-config.js.map +1 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +21 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/scan.d.ts +11 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +36 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/snapshot.d.ts +52 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +203 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/config/manager.d.ts +13 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +28 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/snapshot/builder.d.ts +7 -0
- package/dist/snapshot/builder.d.ts.map +1 -0
- package/dist/snapshot/builder.js +68 -0
- package/dist/snapshot/builder.js.map +1 -0
- package/dist/storage/local.d.ts +44 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +107 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/uploader/icons.d.ts +41 -0
- package/dist/uploader/icons.d.ts.map +1 -0
- package/dist/uploader/icons.js +80 -0
- package/dist/uploader/icons.js.map +1 -0
- package/dist/uploader/webhook.d.ts +7 -0
- package/dist/uploader/webhook.d.ts.map +1 -0
- package/dist/uploader/webhook.js +54 -0
- package/dist/uploader/webhook.js.map +1 -0
- package/dist/utils/icons.d.ts +34 -0
- package/dist/utils/icons.d.ts.map +1 -0
- package/dist/utils/icons.js +113 -0
- package/dist/utils/icons.js.map +1 -0
- package/dist/utils/redact.d.ts +41 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +317 -0
- package/dist/utils/redact.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, extname, basename } from "node:path";
|
|
3
|
+
import { redactSecrets } from "../utils/redact.js";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Constants for safety limits
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/** Maximum size (in bytes) for a single file to be collected (512 KB) */
|
|
8
|
+
const MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
9
|
+
/** Directory names that should always be skipped during recursive collection */
|
|
10
|
+
const EXCLUDED_DIRS = new Set([
|
|
11
|
+
".git",
|
|
12
|
+
"node_modules",
|
|
13
|
+
"__pycache__",
|
|
14
|
+
".cache",
|
|
15
|
+
"cache",
|
|
16
|
+
"target",
|
|
17
|
+
"build",
|
|
18
|
+
"dist",
|
|
19
|
+
".next",
|
|
20
|
+
".nuxt",
|
|
21
|
+
".turbo",
|
|
22
|
+
]);
|
|
23
|
+
/** File extensions that indicate binary / non-text content */
|
|
24
|
+
const BINARY_EXTENSIONS = new Set([
|
|
25
|
+
".ds_store",
|
|
26
|
+
".sqlite",
|
|
27
|
+
".sqlite3",
|
|
28
|
+
".db",
|
|
29
|
+
".wasm",
|
|
30
|
+
".dylib",
|
|
31
|
+
".so",
|
|
32
|
+
".dll",
|
|
33
|
+
".exe",
|
|
34
|
+
".o",
|
|
35
|
+
".a",
|
|
36
|
+
".ico",
|
|
37
|
+
".png",
|
|
38
|
+
".jpg",
|
|
39
|
+
".jpeg",
|
|
40
|
+
".gif",
|
|
41
|
+
".webp",
|
|
42
|
+
".bmp",
|
|
43
|
+
".tiff",
|
|
44
|
+
".mp3",
|
|
45
|
+
".mp4",
|
|
46
|
+
".mov",
|
|
47
|
+
".avi",
|
|
48
|
+
".zip",
|
|
49
|
+
".tar",
|
|
50
|
+
".gz",
|
|
51
|
+
".bz2",
|
|
52
|
+
".xz",
|
|
53
|
+
".7z",
|
|
54
|
+
".rar",
|
|
55
|
+
".pdf",
|
|
56
|
+
".ttf",
|
|
57
|
+
".otf",
|
|
58
|
+
".woff",
|
|
59
|
+
".woff2",
|
|
60
|
+
".eot",
|
|
61
|
+
]);
|
|
62
|
+
/** File basenames that are always binary regardless of extension */
|
|
63
|
+
const BINARY_BASENAMES = new Set([".ds_store", "thumbs.db", "desktop.ini"]);
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Helpers
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
/** Check if a file is likely binary based on its name / extension */
|
|
68
|
+
function isBinaryFile(fileName) {
|
|
69
|
+
const lower = fileName.toLowerCase();
|
|
70
|
+
if (BINARY_BASENAMES.has(lower))
|
|
71
|
+
return true;
|
|
72
|
+
const ext = extname(lower);
|
|
73
|
+
return ext !== "" && BINARY_EXTENSIONS.has(ext);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Base class for all collectors. Provides common file reading utilities
|
|
77
|
+
* and standardized result creation.
|
|
78
|
+
*/
|
|
79
|
+
export class BaseCollector {
|
|
80
|
+
homeDir;
|
|
81
|
+
constructor(homeDir) {
|
|
82
|
+
this.homeDir = homeDir;
|
|
83
|
+
}
|
|
84
|
+
/** Create an empty result skeleton */
|
|
85
|
+
createResult() {
|
|
86
|
+
return {
|
|
87
|
+
id: this.id,
|
|
88
|
+
label: this.label,
|
|
89
|
+
category: this.category,
|
|
90
|
+
files: [],
|
|
91
|
+
lists: [],
|
|
92
|
+
errors: [],
|
|
93
|
+
durationMs: 0,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Safely read a single file. Returns null if file doesn't exist,
|
|
98
|
+
* can't be read, exceeds size limit, or is binary.
|
|
99
|
+
*
|
|
100
|
+
* @param redact If true, apply credential redaction based on file type
|
|
101
|
+
*/
|
|
102
|
+
async safeReadFile(filePath, result, { maxSize = MAX_FILE_SIZE_BYTES, redact = false } = {}) {
|
|
103
|
+
try {
|
|
104
|
+
// Skip binary files
|
|
105
|
+
const fileName = basename(filePath);
|
|
106
|
+
if (isBinaryFile(fileName))
|
|
107
|
+
return null;
|
|
108
|
+
const info = await stat(filePath);
|
|
109
|
+
// Skip files exceeding size limit
|
|
110
|
+
if (info.size > maxSize) {
|
|
111
|
+
result.errors.push(`Skipped ${filePath}: exceeds size limit (${(info.size / 1024).toFixed(0)} KB > ${(maxSize / 1024).toFixed(0)} KB)`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
let content = await readFile(filePath, "utf-8");
|
|
115
|
+
// Apply credential redaction if requested
|
|
116
|
+
if (redact) {
|
|
117
|
+
content = redactSecrets(content, filePath);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
path: filePath,
|
|
121
|
+
content,
|
|
122
|
+
sizeBytes: info.size,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (err.code !== "ENOENT") {
|
|
127
|
+
result.errors.push(`Failed to read ${filePath}: ${err.message}`);
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Recursively collect all files in a directory.
|
|
134
|
+
*
|
|
135
|
+
* Safety features:
|
|
136
|
+
* - Skips excluded directories (.git, node_modules, cache, build output, etc.)
|
|
137
|
+
* - Skips binary files (by extension and known basenames)
|
|
138
|
+
* - Skips files exceeding size limit (default 512 KB)
|
|
139
|
+
*/
|
|
140
|
+
async collectDir(dirPath, result, opts = {}) {
|
|
141
|
+
const { filter, maxFileSize = MAX_FILE_SIZE_BYTES, excludeDirs, redact } = opts;
|
|
142
|
+
const files = [];
|
|
143
|
+
try {
|
|
144
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
const fullPath = join(dirPath, entry.name);
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
const dirName = entry.name.toLowerCase();
|
|
149
|
+
// Skip globally excluded directories
|
|
150
|
+
if (EXCLUDED_DIRS.has(dirName))
|
|
151
|
+
continue;
|
|
152
|
+
// Skip caller-specified excluded directories
|
|
153
|
+
if (excludeDirs?.has(dirName))
|
|
154
|
+
continue;
|
|
155
|
+
const subFiles = await this.collectDir(fullPath, result, opts);
|
|
156
|
+
files.push(...subFiles);
|
|
157
|
+
}
|
|
158
|
+
else if (entry.isFile()) {
|
|
159
|
+
if (filter && !filter(fullPath))
|
|
160
|
+
continue;
|
|
161
|
+
const file = await this.safeReadFile(fullPath, result, {
|
|
162
|
+
maxSize: maxFileSize,
|
|
163
|
+
redact,
|
|
164
|
+
});
|
|
165
|
+
if (file)
|
|
166
|
+
files.push(file);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
if (err.code !== "ENOENT") {
|
|
172
|
+
result.errors.push(`Failed to read directory ${dirPath}: ${err.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return files;
|
|
176
|
+
}
|
|
177
|
+
/** Measure execution time of the collect operation */
|
|
178
|
+
async timed(fn) {
|
|
179
|
+
const result = this.createResult();
|
|
180
|
+
const start = performance.now();
|
|
181
|
+
await fn(result);
|
|
182
|
+
result.durationMs = Math.round(performance.now() - start);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/collectors/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseCollector } from "./base.js";
|
|
2
|
+
import type { CollectorCategory, CollectorResult } from "@otter/core";
|
|
3
|
+
/**
|
|
4
|
+
* Collects Claude Code configuration files with targeted, safe collection.
|
|
5
|
+
*
|
|
6
|
+
* What we collect:
|
|
7
|
+
* - ~/CLAUDE.md (user-level instructions)
|
|
8
|
+
* - ~/.claude/CLAUDE.md, settings.json, stats-cache.json
|
|
9
|
+
* - ~/.claude/plugins/installed_plugins.json, plugins/blocklist.json
|
|
10
|
+
* - ~/.claude/history.jsonl
|
|
11
|
+
* - Conversation metadata summaries (title, timestamps, token counts)
|
|
12
|
+
* from projects/sessions-index.json files — NOT full conversation content
|
|
13
|
+
*
|
|
14
|
+
* What we skip:
|
|
15
|
+
* - debug/, telemetry/, transcripts/, cache/, paste-cache/
|
|
16
|
+
* - shell-snapshots/, session-env/, statsig/
|
|
17
|
+
* - All .jsonl session content files
|
|
18
|
+
* - .git/ directories inside plugins
|
|
19
|
+
* - Binary files, large files
|
|
20
|
+
*/
|
|
21
|
+
export declare class ClaudeConfigCollector extends BaseCollector {
|
|
22
|
+
readonly id = "claude-config";
|
|
23
|
+
readonly label = "Claude Code Configuration";
|
|
24
|
+
readonly category: CollectorCategory;
|
|
25
|
+
private readonly slim;
|
|
26
|
+
constructor(homeDir: string, options?: {
|
|
27
|
+
slim?: boolean;
|
|
28
|
+
});
|
|
29
|
+
collect(): Promise<CollectorResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Iterate over all projects/sessions-index.json files and extract
|
|
32
|
+
* lightweight metadata for each conversation session.
|
|
33
|
+
*
|
|
34
|
+
* Returns a synthetic CollectedFile containing JSON with all project
|
|
35
|
+
* summaries — no actual conversation content is included.
|
|
36
|
+
*/
|
|
37
|
+
private collectSessionSummaries;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=claude-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-config.d.ts","sourceRoot":"","sources":["../../src/collectors/claude-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,aAAa,CAAC;AA0CrB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;IACtD,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,KAAK,+BAA+B;IAC7C,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAEhD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;IAKnD,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAmCzC;;;;;;OAMG;YACW,uBAAuB;CA6DtC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { BaseCollector } from "./base.js";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Constants
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/** Files to collect as full content (small, valuable config files) */
|
|
8
|
+
const TARGETED_FILES = [
|
|
9
|
+
{ path: "CLAUDE.md" }, // user-level instructions
|
|
10
|
+
{ path: "settings.json", redact: true }, // settings (contains API tokens)
|
|
11
|
+
{ path: "stats-cache.json" }, // aggregate usage stats
|
|
12
|
+
{ path: "plugins/installed_plugins.json" }, // plugin inventory
|
|
13
|
+
{ path: "plugins/blocklist.json" }, // plugin blocklist
|
|
14
|
+
{ path: "history.jsonl", redact: true, maxSize: 2 * 1024 * 1024, slim: true }, // prompt history (excluded in slim mode)
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Collects Claude Code configuration files with targeted, safe collection.
|
|
18
|
+
*
|
|
19
|
+
* What we collect:
|
|
20
|
+
* - ~/CLAUDE.md (user-level instructions)
|
|
21
|
+
* - ~/.claude/CLAUDE.md, settings.json, stats-cache.json
|
|
22
|
+
* - ~/.claude/plugins/installed_plugins.json, plugins/blocklist.json
|
|
23
|
+
* - ~/.claude/history.jsonl
|
|
24
|
+
* - Conversation metadata summaries (title, timestamps, token counts)
|
|
25
|
+
* from projects/sessions-index.json files — NOT full conversation content
|
|
26
|
+
*
|
|
27
|
+
* What we skip:
|
|
28
|
+
* - debug/, telemetry/, transcripts/, cache/, paste-cache/
|
|
29
|
+
* - shell-snapshots/, session-env/, statsig/
|
|
30
|
+
* - All .jsonl session content files
|
|
31
|
+
* - .git/ directories inside plugins
|
|
32
|
+
* - Binary files, large files
|
|
33
|
+
*/
|
|
34
|
+
export class ClaudeConfigCollector extends BaseCollector {
|
|
35
|
+
id = "claude-config";
|
|
36
|
+
label = "Claude Code Configuration";
|
|
37
|
+
category = "config";
|
|
38
|
+
slim;
|
|
39
|
+
constructor(homeDir, options) {
|
|
40
|
+
super(homeDir);
|
|
41
|
+
this.slim = options?.slim ?? false;
|
|
42
|
+
}
|
|
43
|
+
async collect() {
|
|
44
|
+
return this.timed(async (result) => {
|
|
45
|
+
const claudeDir = join(this.homeDir, ".claude");
|
|
46
|
+
// 1. Collect ~/CLAUDE.md (home-level instructions)
|
|
47
|
+
const homeMd = await this.safeReadFile(join(this.homeDir, "CLAUDE.md"), result);
|
|
48
|
+
if (homeMd)
|
|
49
|
+
result.files.push(homeMd);
|
|
50
|
+
// 2. Collect targeted config files from ~/.claude/
|
|
51
|
+
for (const { path: relativePath, redact, maxSize, slim: slimExclude } of TARGETED_FILES) {
|
|
52
|
+
// Skip files marked as slim-excluded when in slim mode
|
|
53
|
+
if (this.slim && slimExclude)
|
|
54
|
+
continue;
|
|
55
|
+
const file = await this.safeReadFile(join(claudeDir, relativePath), result, { redact, maxSize });
|
|
56
|
+
if (file)
|
|
57
|
+
result.files.push(file);
|
|
58
|
+
}
|
|
59
|
+
// 3. Collect conversation metadata summaries (skip in slim mode)
|
|
60
|
+
if (!this.slim) {
|
|
61
|
+
const summaryFile = await this.collectSessionSummaries(claudeDir, result);
|
|
62
|
+
if (summaryFile)
|
|
63
|
+
result.files.push(summaryFile);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Iterate over all projects/sessions-index.json files and extract
|
|
69
|
+
* lightweight metadata for each conversation session.
|
|
70
|
+
*
|
|
71
|
+
* Returns a synthetic CollectedFile containing JSON with all project
|
|
72
|
+
* summaries — no actual conversation content is included.
|
|
73
|
+
*/
|
|
74
|
+
async collectSessionSummaries(claudeDir, result) {
|
|
75
|
+
const projectsDir = join(claudeDir, "projects");
|
|
76
|
+
const summaries = [];
|
|
77
|
+
try {
|
|
78
|
+
// Each subdirectory under projects/ is a hashed project path
|
|
79
|
+
const projectDirs = await readdir(projectsDir, { withFileTypes: true });
|
|
80
|
+
for (const entry of projectDirs) {
|
|
81
|
+
if (!entry.isDirectory())
|
|
82
|
+
continue;
|
|
83
|
+
const indexPath = join(projectsDir, entry.name, "sessions-index.json");
|
|
84
|
+
try {
|
|
85
|
+
const raw = await readFile(indexPath, "utf-8");
|
|
86
|
+
const index = JSON.parse(raw);
|
|
87
|
+
if (!index.entries?.length)
|
|
88
|
+
continue;
|
|
89
|
+
summaries.push({
|
|
90
|
+
projectPath: index.originalPath ?? entry.name,
|
|
91
|
+
sessions: index.entries.map((e) => ({
|
|
92
|
+
sessionId: e.sessionId,
|
|
93
|
+
firstPrompt: e.firstPrompt,
|
|
94
|
+
messageCount: e.messageCount,
|
|
95
|
+
created: e.created,
|
|
96
|
+
modified: e.modified,
|
|
97
|
+
gitBranch: e.gitBranch,
|
|
98
|
+
projectPath: e.projectPath,
|
|
99
|
+
isSidechain: e.isSidechain,
|
|
100
|
+
})),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// sessions-index.json missing or malformed — skip silently
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (err.code !== "ENOENT") {
|
|
110
|
+
result.errors.push(`Failed to read Claude projects directory: ${err.message}`);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (summaries.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
const content = JSON.stringify(summaries, null, 2);
|
|
117
|
+
return {
|
|
118
|
+
path: join(claudeDir, "projects", "__sessions-summary.json"),
|
|
119
|
+
content,
|
|
120
|
+
sizeBytes: Buffer.byteLength(content, "utf-8"),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=claude-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-config.js","sourceRoot":"","sources":["../../src/collectors/claude-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseCollector } from "./base.js";
|
|
2
|
+
import type { CollectorCategory, CollectorResult } from "@otter/core";
|
|
3
|
+
/**
|
|
4
|
+
* Collects installed Homebrew packages (formulae + casks).
|
|
5
|
+
* List-only: no binary content is collected.
|
|
6
|
+
*/
|
|
7
|
+
export declare class HomebrewCollector extends BaseCollector {
|
|
8
|
+
readonly id = "homebrew";
|
|
9
|
+
readonly label = "Homebrew Packages";
|
|
10
|
+
readonly category: CollectorCategory;
|
|
11
|
+
/** Overridable for testing — executes a shell command and returns stdout */
|
|
12
|
+
_execCommand: (cmd: string) => Promise<string>;
|
|
13
|
+
collect(): Promise<CollectorResult>;
|
|
14
|
+
private listPackages;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=homebrew.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"homebrew.d.ts","sourceRoot":"","sources":["../../src/collectors/homebrew.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAIrB;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,aAAa;IAClD,QAAQ,CAAC,EAAE,cAAc;IACzB,QAAQ,CAAC,KAAK,uBAAuB;IACrC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAiB;IAErD,4EAA4E;IAC5E,YAAY,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC,CAGjD;IAEI,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;YAoB3B,YAAY;CAmB3B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { BaseCollector } from "./base.js";
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
/**
|
|
6
|
+
* Collects installed Homebrew packages (formulae + casks).
|
|
7
|
+
* List-only: no binary content is collected.
|
|
8
|
+
*/
|
|
9
|
+
export class HomebrewCollector extends BaseCollector {
|
|
10
|
+
id = "homebrew";
|
|
11
|
+
label = "Homebrew Packages";
|
|
12
|
+
category = "environment";
|
|
13
|
+
/** Overridable for testing — executes a shell command and returns stdout */
|
|
14
|
+
_execCommand = async (cmd) => {
|
|
15
|
+
const { stdout } = await execAsync(cmd);
|
|
16
|
+
return stdout;
|
|
17
|
+
};
|
|
18
|
+
async collect() {
|
|
19
|
+
return this.timed(async (result) => {
|
|
20
|
+
// Collect formulae
|
|
21
|
+
const formulae = await this.listPackages("brew list --formula", "formula", result);
|
|
22
|
+
result.lists.push(...formulae);
|
|
23
|
+
// Collect casks
|
|
24
|
+
const casks = await this.listPackages("brew list --cask", "cask", result);
|
|
25
|
+
result.lists.push(...casks);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async listPackages(cmd, type, result) {
|
|
29
|
+
try {
|
|
30
|
+
const output = await this._execCommand(cmd);
|
|
31
|
+
return output
|
|
32
|
+
.split("\n")
|
|
33
|
+
.map((line) => line.trim())
|
|
34
|
+
.filter((line) => line.length > 0)
|
|
35
|
+
.map((name) => ({ name, meta: { type } }));
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
result.errors.push(`Failed to run '${cmd}': ${err.message}`);
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=homebrew.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"homebrew.js","sourceRoot":"","sources":["../../src/collectors/homebrew.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAO1C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IACzC,EAAE,GAAG,UAAU,CAAC;IAChB,KAAK,GAAG,mBAAmB,CAAC;IAC5B,QAAQ,GAAsB,aAAa,CAAC;IAErD,4EAA4E;IAC5E,YAAY,GAAG,KAAK,EAAE,GAAW,EAAmB,EAAE;QACpD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACjC,mBAAmB;YACnB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CACtC,qBAAqB,EACrB,SAAS,EACT,MAAM,CACP,CAAC;YACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;YAE/B,gBAAgB;YAChB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CACnC,kBAAkB,EAClB,MAAM,EACN,MAAM,CACP,CAAC;YACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,GAAW,EACX,IAAY,EACZ,MAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;iBACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,kBAAkB,GAAG,MAAO,GAAa,CAAC,OAAO,EAAE,CACpD,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { BaseCollector } from "./base.js";
|
|
2
|
+
export { ClaudeConfigCollector } from "./claude-config.js";
|
|
3
|
+
export { OpenCodeConfigCollector } from "./opencode-config.js";
|
|
4
|
+
export { ShellConfigCollector } from "./shell-config.js";
|
|
5
|
+
export { HomebrewCollector } from "./homebrew.js";
|
|
6
|
+
export { ApplicationsCollector } from "./applications.js";
|
|
7
|
+
import type { Collector } from "@otter/core";
|
|
8
|
+
/**
|
|
9
|
+
* Options for creating the default set of collectors.
|
|
10
|
+
*/
|
|
11
|
+
export interface CollectorOptions {
|
|
12
|
+
/** If true, exclude behavior data (history.jsonl, session summaries) */
|
|
13
|
+
slim?: boolean;
|
|
14
|
+
/** Base URL for deterministic icon URLs (default: s.zhe.to/apps/otter) */
|
|
15
|
+
iconBaseUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create all default collectors targeting the current system.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createDefaultCollectors(homeDir?: string, options?: CollectorOptions): Collector[];
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAW7C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,MAAkB,EAC3B,OAAO,GAAE,gBAAqB,GAC7B,SAAS,EAAE,CASb"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export { BaseCollector } from "./base.js";
|
|
2
|
+
export { ClaudeConfigCollector } from "./claude-config.js";
|
|
3
|
+
export { OpenCodeConfigCollector } from "./opencode-config.js";
|
|
4
|
+
export { ShellConfigCollector } from "./shell-config.js";
|
|
5
|
+
export { HomebrewCollector } from "./homebrew.js";
|
|
6
|
+
export { ApplicationsCollector } from "./applications.js";
|
|
7
|
+
import { ClaudeConfigCollector } from "./claude-config.js";
|
|
8
|
+
import { OpenCodeConfigCollector } from "./opencode-config.js";
|
|
9
|
+
import { ShellConfigCollector } from "./shell-config.js";
|
|
10
|
+
import { HomebrewCollector } from "./homebrew.js";
|
|
11
|
+
import { ApplicationsCollector } from "./applications.js";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
/** Default R2 public base URL for app icon assets */
|
|
14
|
+
const DEFAULT_ICON_BASE_URL = "https://s.zhe.to/apps/otter";
|
|
15
|
+
/**
|
|
16
|
+
* Create all default collectors targeting the current system.
|
|
17
|
+
*/
|
|
18
|
+
export function createDefaultCollectors(homeDir = homedir(), options = {}) {
|
|
19
|
+
const iconBaseUrl = options.iconBaseUrl ?? DEFAULT_ICON_BASE_URL;
|
|
20
|
+
return [
|
|
21
|
+
new ClaudeConfigCollector(homeDir, { slim: options.slim }),
|
|
22
|
+
new OpenCodeConfigCollector(homeDir),
|
|
23
|
+
new ShellConfigCollector(homeDir),
|
|
24
|
+
new HomebrewCollector(homeDir),
|
|
25
|
+
new ApplicationsCollector(homeDir, "/Applications", iconBaseUrl),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/collectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,6BAA6B,CAAC;AAY5D;;GAEG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAkB,OAAO,EAAE,EAC3B,UAA4B,EAAE;IAE9B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC;IACjE,OAAO;QACL,IAAI,qBAAqB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1D,IAAI,uBAAuB,CAAC,OAAO,CAAC;QACpC,IAAI,oBAAoB,CAAC,OAAO,CAAC;QACjC,IAAI,iBAAiB,CAAC,OAAO,CAAC;QAC9B,IAAI,qBAAqB,CAAC,OAAO,EAAE,eAAe,EAAE,WAAW,CAAC;KACjE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseCollector } from "./base.js";
|
|
2
|
+
import type { CollectorCategory, CollectorResult } from "@otter/core";
|
|
3
|
+
/**
|
|
4
|
+
* Parse YAML-like frontmatter from a SKILL.md file.
|
|
5
|
+
* Extracts simple `key: value` pairs between `---` delimiters.
|
|
6
|
+
* Exported for testing.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseSkillFrontmatter(content: string): Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Collects OpenCode configuration files:
|
|
11
|
+
* - ~/.config/opencode/ config files (excluding skills content)
|
|
12
|
+
* - Skill names from ~/.config/opencode/skills/ and ~/.agents/skills/
|
|
13
|
+
*/
|
|
14
|
+
export declare class OpenCodeConfigCollector extends BaseCollector {
|
|
15
|
+
readonly id = "opencode-config";
|
|
16
|
+
readonly label = "OpenCode Configuration";
|
|
17
|
+
readonly category: CollectorCategory;
|
|
18
|
+
collect(): Promise<CollectorResult>;
|
|
19
|
+
private collectSkillNames;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=opencode-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-config.d.ts","sourceRoot":"","sources":["../../src/collectors/opencode-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAQrB;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxB;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,aAAa;IACxD,QAAQ,CAAC,EAAE,qBAAqB;IAChC,QAAQ,CAAC,KAAK,4BAA4B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAE1C,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;YAsB3B,iBAAiB;CA2ChC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { BaseCollector } from "./base.js";
|
|
4
|
+
/** Directories that contain skills (list-only, not full content) */
|
|
5
|
+
const SKILLS_DIRS = [
|
|
6
|
+
{ relative: ".config/opencode/skills", source: ".config/opencode/skills" },
|
|
7
|
+
{ relative: ".agents/skills", source: ".agents/skills" },
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Parse YAML-like frontmatter from a SKILL.md file.
|
|
11
|
+
* Extracts simple `key: value` pairs between `---` delimiters.
|
|
12
|
+
* Exported for testing.
|
|
13
|
+
*/
|
|
14
|
+
export function parseSkillFrontmatter(content) {
|
|
15
|
+
const meta = {};
|
|
16
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
17
|
+
if (!match)
|
|
18
|
+
return meta;
|
|
19
|
+
for (const line of match[1].split("\n")) {
|
|
20
|
+
// Match simple key: value (not nested YAML)
|
|
21
|
+
const kv = line.match(/^(\w[\w-]*)\s*:\s*(.+)$/);
|
|
22
|
+
if (kv) {
|
|
23
|
+
meta[kv[1].trim()] = kv[2].trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return meta;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Collects OpenCode configuration files:
|
|
30
|
+
* - ~/.config/opencode/ config files (excluding skills content)
|
|
31
|
+
* - Skill names from ~/.config/opencode/skills/ and ~/.agents/skills/
|
|
32
|
+
*/
|
|
33
|
+
export class OpenCodeConfigCollector extends BaseCollector {
|
|
34
|
+
id = "opencode-config";
|
|
35
|
+
label = "OpenCode Configuration";
|
|
36
|
+
category = "config";
|
|
37
|
+
async collect() {
|
|
38
|
+
return this.timed(async (result) => {
|
|
39
|
+
// 1. Collect config files from ~/.config/opencode/ (excluding skills dir)
|
|
40
|
+
const configDir = join(this.homeDir, ".config", "opencode");
|
|
41
|
+
const files = await this.collectDir(configDir, result, {
|
|
42
|
+
filter: (path) => !path.includes("/skills/"),
|
|
43
|
+
redact: true,
|
|
44
|
+
});
|
|
45
|
+
result.files.push(...files);
|
|
46
|
+
// 2. Collect skill names as list items
|
|
47
|
+
for (const skillDir of SKILLS_DIRS) {
|
|
48
|
+
const skills = await this.collectSkillNames(join(this.homeDir, skillDir.relative), skillDir.source, result);
|
|
49
|
+
result.lists.push(...skills);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async collectSkillNames(dirPath, source, result) {
|
|
54
|
+
const items = [];
|
|
55
|
+
try {
|
|
56
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
if (!entry.isDirectory())
|
|
59
|
+
continue;
|
|
60
|
+
const meta = { source };
|
|
61
|
+
const location = `file://${join(dirPath, entry.name, "SKILL.md")}`;
|
|
62
|
+
meta.location = location;
|
|
63
|
+
// Try to parse SKILL.md frontmatter for description
|
|
64
|
+
try {
|
|
65
|
+
const skillMd = await readFile(join(dirPath, entry.name, "SKILL.md"), "utf-8");
|
|
66
|
+
const fm = parseSkillFrontmatter(skillMd);
|
|
67
|
+
if (fm.description) {
|
|
68
|
+
meta.description = fm.description;
|
|
69
|
+
}
|
|
70
|
+
if (fm.name) {
|
|
71
|
+
meta.skillName = fm.name;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// SKILL.md missing or unreadable — not an error, just skip enrichment
|
|
76
|
+
}
|
|
77
|
+
items.push({ name: entry.name, meta });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err.code !== "ENOENT") {
|
|
82
|
+
result.errors.push(`Failed to read skills directory ${dirPath}: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return items;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=opencode-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-config.js","sourceRoot":"","sources":["../../src/collectors/opencode-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseCollector } from "./base.js";
|
|
2
|
+
import type { CollectorCategory, CollectorResult } from "@otter/core";
|
|
3
|
+
/**
|
|
4
|
+
* Classify an SSH filename as a key type, or null if not a key.
|
|
5
|
+
* Exported for testing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function classifySshFile(name: string): "private-key" | "public-key" | null;
|
|
8
|
+
/**
|
|
9
|
+
* Collects shell and developer environment configuration files:
|
|
10
|
+
* - Common dotfiles (.zshrc, .bashrc, .gitconfig, etc.)
|
|
11
|
+
* - SSH config (but NOT private/public keys — only presence indicators)
|
|
12
|
+
*/
|
|
13
|
+
export declare class ShellConfigCollector extends BaseCollector {
|
|
14
|
+
readonly id = "shell-config";
|
|
15
|
+
readonly label = "Shell Configuration";
|
|
16
|
+
readonly category: CollectorCategory;
|
|
17
|
+
collect(): Promise<CollectorResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Scan ~/.ssh/ for key files and return presence indicators.
|
|
20
|
+
* Key content is NEVER read — only filenames and metadata.
|
|
21
|
+
*/
|
|
22
|
+
private detectSshKeys;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=shell-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-config.d.ts","sourceRoot":"","sources":["../../src/collectors/shell-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAgDrB;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GACX,aAAa,GAAG,YAAY,GAAG,IAAI,CAcrC;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,aAAa;IACrD,QAAQ,CAAC,EAAE,kBAAkB;IAC7B,QAAQ,CAAC,KAAK,yBAAyB;IACvC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAiB;IAE/C,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IA2BzC;;;OAGG;YACW,aAAa;CA0C5B"}
|