@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.
Files changed (81) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.d.ts.map +1 -0
  3. package/dist/bin.js +5 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +365 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/collectors/applications.d.ts +19 -0
  10. package/dist/collectors/applications.d.ts.map +1 -0
  11. package/dist/collectors/applications.js +51 -0
  12. package/dist/collectors/applications.js.map +1 -0
  13. package/dist/collectors/base.d.ts +52 -0
  14. package/dist/collectors/base.d.ts.map +1 -0
  15. package/dist/collectors/base.js +186 -0
  16. package/dist/collectors/base.js.map +1 -0
  17. package/dist/collectors/claude-config.d.ts +39 -0
  18. package/dist/collectors/claude-config.d.ts.map +1 -0
  19. package/dist/collectors/claude-config.js +124 -0
  20. package/dist/collectors/claude-config.js.map +1 -0
  21. package/dist/collectors/homebrew.d.ts +16 -0
  22. package/dist/collectors/homebrew.d.ts.map +1 -0
  23. package/dist/collectors/homebrew.js +43 -0
  24. package/dist/collectors/homebrew.js.map +1 -0
  25. package/dist/collectors/index.d.ts +21 -0
  26. package/dist/collectors/index.d.ts.map +1 -0
  27. package/dist/collectors/index.js +28 -0
  28. package/dist/collectors/index.js.map +1 -0
  29. package/dist/collectors/opencode-config.d.ts +21 -0
  30. package/dist/collectors/opencode-config.d.ts.map +1 -0
  31. package/dist/collectors/opencode-config.js +88 -0
  32. package/dist/collectors/opencode-config.js.map +1 -0
  33. package/dist/collectors/shell-config.d.ts +24 -0
  34. package/dist/collectors/shell-config.d.ts.map +1 -0
  35. package/dist/collectors/shell-config.js +133 -0
  36. package/dist/collectors/shell-config.js.map +1 -0
  37. package/dist/commands/config.d.ts +17 -0
  38. package/dist/commands/config.d.ts.map +1 -0
  39. package/dist/commands/config.js +21 -0
  40. package/dist/commands/config.js.map +1 -0
  41. package/dist/commands/scan.d.ts +11 -0
  42. package/dist/commands/scan.d.ts.map +1 -0
  43. package/dist/commands/scan.js +36 -0
  44. package/dist/commands/scan.js.map +1 -0
  45. package/dist/commands/snapshot.d.ts +52 -0
  46. package/dist/commands/snapshot.d.ts.map +1 -0
  47. package/dist/commands/snapshot.js +203 -0
  48. package/dist/commands/snapshot.js.map +1 -0
  49. package/dist/config/manager.d.ts +13 -0
  50. package/dist/config/manager.d.ts.map +1 -0
  51. package/dist/config/manager.js +28 -0
  52. package/dist/config/manager.js.map +1 -0
  53. package/dist/index.d.ts +8 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +8 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/snapshot/builder.d.ts +7 -0
  58. package/dist/snapshot/builder.d.ts.map +1 -0
  59. package/dist/snapshot/builder.js +68 -0
  60. package/dist/snapshot/builder.js.map +1 -0
  61. package/dist/storage/local.d.ts +44 -0
  62. package/dist/storage/local.d.ts.map +1 -0
  63. package/dist/storage/local.js +107 -0
  64. package/dist/storage/local.js.map +1 -0
  65. package/dist/uploader/icons.d.ts +41 -0
  66. package/dist/uploader/icons.d.ts.map +1 -0
  67. package/dist/uploader/icons.js +80 -0
  68. package/dist/uploader/icons.js.map +1 -0
  69. package/dist/uploader/webhook.d.ts +7 -0
  70. package/dist/uploader/webhook.d.ts.map +1 -0
  71. package/dist/uploader/webhook.js +54 -0
  72. package/dist/uploader/webhook.js.map +1 -0
  73. package/dist/utils/icons.d.ts +34 -0
  74. package/dist/utils/icons.d.ts.map +1 -0
  75. package/dist/utils/icons.js +113 -0
  76. package/dist/utils/icons.js.map +1 -0
  77. package/dist/utils/redact.d.ts +41 -0
  78. package/dist/utils/redact.d.ts.map +1 -0
  79. package/dist/utils/redact.js +317 -0
  80. package/dist/utils/redact.js.map +1 -0
  81. package/package.json +45 -0
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runMain } from "citty";
3
+ import { main } from "./cli.js";
4
+ runMain(main);
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,OAAO,CAAC,IAAI,CAAC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const main: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA2ZA,eAAO,MAAM,IAAI,qDAcf,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,365 @@
1
+ import { defineCommand } from "citty";
2
+ import { consola } from "consola";
3
+ import pc from "picocolors";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { createDefaultCollectors } from "./collectors/index.js";
7
+ import { executeScan } from "./commands/scan.js";
8
+ import { executeConfig } from "./commands/config.js";
9
+ import { formatSnapshotList, formatSnapshotDetail, diffSnapshots, formatSnapshotDiff } from "./commands/snapshot.js";
10
+ import { ConfigManager } from "./config/manager.js";
11
+ import { SnapshotStore } from "./storage/local.js";
12
+ import { uploadSnapshot } from "./uploader/webhook.js";
13
+ import { uploadIcons } from "./uploader/icons.js";
14
+ import { exportIcons } from "./utils/icons.js";
15
+ const otterConfigDir = join(homedir(), ".config", "otter");
16
+ const configManager = new ConfigManager(otterConfigDir);
17
+ const snapshotStore = new SnapshotStore(join(otterConfigDir, "snapshots"));
18
+ const scanCommand = defineCommand({
19
+ meta: {
20
+ name: "scan",
21
+ description: "Scan and collect configuration files and environment data",
22
+ },
23
+ args: {
24
+ json: {
25
+ type: "boolean",
26
+ description: "Output snapshot as JSON to stdout",
27
+ default: false,
28
+ },
29
+ slim: {
30
+ type: "boolean",
31
+ description: "Exclude behavior data (history.jsonl, session summaries) for a smaller snapshot",
32
+ default: false,
33
+ },
34
+ save: {
35
+ type: "boolean",
36
+ description: "Save the snapshot locally after scanning",
37
+ default: false,
38
+ },
39
+ },
40
+ async run({ args }) {
41
+ // When --json is set, redirect all progress output to stderr
42
+ // so that stdout contains only valid JSON
43
+ if (args.json) {
44
+ consola.options.stdout = process.stderr;
45
+ }
46
+ consola.start("Scanning your Mac environment...\n");
47
+ const collectors = createDefaultCollectors(homedir(), {
48
+ slim: args.slim,
49
+ });
50
+ const snapshot = await executeScan(collectors, {
51
+ onProgress: (_id, result) => {
52
+ const fileCount = result.files.length;
53
+ const listCount = result.lists.length;
54
+ const errorCount = result.errors.length;
55
+ const status = errorCount > 0 ? pc.yellow("⚠") : pc.green("✓");
56
+ consola.log(` ${status} ${pc.bold(result.label)}: ${fileCount} files, ${listCount} list items${errorCount > 0 ? `, ${pc.yellow(`${errorCount} errors`)}` : ""} ${pc.dim(`(${result.durationMs}ms)`)}`);
57
+ },
58
+ });
59
+ if (args.save) {
60
+ const filename = await snapshotStore.save(snapshot);
61
+ consola.success(`Snapshot saved locally: ${pc.dim(filename)}`);
62
+ }
63
+ if (args.json) {
64
+ process.stdout.write(JSON.stringify(snapshot, null, 2) + "\n");
65
+ }
66
+ else {
67
+ const totalFiles = snapshot.collectors.reduce((sum, c) => sum + c.files.length, 0);
68
+ const totalLists = snapshot.collectors.reduce((sum, c) => sum + c.lists.length, 0);
69
+ consola.success(`\nScan complete: ${pc.bold(String(totalFiles))} files, ${pc.bold(String(totalLists))} list items from ${pc.bold(String(snapshot.collectors.length))} collectors`);
70
+ consola.info(`Snapshot ID: ${pc.dim(snapshot.id)}`);
71
+ }
72
+ },
73
+ });
74
+ const backupCommand = defineCommand({
75
+ meta: {
76
+ name: "backup",
77
+ description: "Scan, build snapshot, and upload to webhook",
78
+ },
79
+ args: {
80
+ slim: {
81
+ type: "boolean",
82
+ description: "Exclude behavior data (history.jsonl, session summaries) for a smaller snapshot",
83
+ default: false,
84
+ },
85
+ },
86
+ async run({ args }) {
87
+ const config = await configManager.load();
88
+ if (!config.webhookUrl) {
89
+ consola.error("No webhook URL configured. Run " +
90
+ pc.bold("otter config set webhookUrl <url>") +
91
+ " first.");
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ consola.start("Scanning your Mac environment...\n");
96
+ const collectors = createDefaultCollectors(homedir(), {
97
+ slim: args.slim,
98
+ });
99
+ const snapshot = await executeScan(collectors, {
100
+ onProgress: (_id, result) => {
101
+ const status = result.errors.length > 0 ? pc.yellow("⚠") : pc.green("✓");
102
+ consola.log(` ${status} ${pc.bold(result.label)} ${pc.dim(`(${result.durationMs}ms)`)}`);
103
+ },
104
+ });
105
+ consola.start("\nUploading snapshot...");
106
+ const uploadResult = await uploadSnapshot(snapshot, {
107
+ webhookUrl: config.webhookUrl,
108
+ });
109
+ if (uploadResult.success) {
110
+ consola.success(`Backup uploaded successfully ${pc.dim(`(${uploadResult.durationMs}ms)`)}`);
111
+ // Auto-save locally after successful upload
112
+ const filename = await snapshotStore.save(snapshot);
113
+ consola.success(`Snapshot saved locally: ${pc.dim(filename)}`);
114
+ consola.info(`Snapshot ID: ${pc.dim(snapshot.id)}`);
115
+ }
116
+ else {
117
+ consola.error(`Upload failed: ${uploadResult.error}`);
118
+ process.exitCode = 1;
119
+ }
120
+ },
121
+ });
122
+ const configCommand = defineCommand({
123
+ meta: {
124
+ name: "config",
125
+ description: "Manage CLI configuration",
126
+ },
127
+ args: {
128
+ action: {
129
+ type: "positional",
130
+ description: "Action: get, set, or show",
131
+ required: false,
132
+ },
133
+ key: {
134
+ type: "positional",
135
+ description: "Config key (e.g., webhookUrl)",
136
+ required: false,
137
+ },
138
+ value: {
139
+ type: "positional",
140
+ description: "Value to set",
141
+ required: false,
142
+ },
143
+ },
144
+ async run({ args }) {
145
+ const action = args.action || "show";
146
+ if (action === "show") {
147
+ const result = await executeConfig(configManager, { action: "show" });
148
+ consola.info("Current configuration:");
149
+ consola.log(JSON.stringify(result, null, 2));
150
+ consola.log(pc.dim(`\nConfig file: ${configManager.configPath}`));
151
+ return;
152
+ }
153
+ if (action === "get") {
154
+ if (!args.key) {
155
+ consola.error("Usage: otter config get <key>");
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+ const result = await executeConfig(configManager, {
160
+ action: "get",
161
+ key: args.key,
162
+ });
163
+ if (result !== undefined) {
164
+ consola.log(result);
165
+ }
166
+ else {
167
+ consola.warn(`Key '${args.key}' is not set`);
168
+ }
169
+ return;
170
+ }
171
+ if (action === "set") {
172
+ if (!args.key || !args.value) {
173
+ consola.error("Usage: otter config set <key> <value>");
174
+ process.exitCode = 1;
175
+ return;
176
+ }
177
+ await executeConfig(configManager, {
178
+ action: "set",
179
+ key: args.key,
180
+ value: args.value,
181
+ });
182
+ consola.success(`Set ${pc.bold(args.key)} = ${args.value}`);
183
+ return;
184
+ }
185
+ consola.error(`Unknown action: ${action}. Use get, set, or show.`);
186
+ process.exitCode = 1;
187
+ },
188
+ });
189
+ const snapshotListCommand = defineCommand({
190
+ meta: {
191
+ name: "list",
192
+ description: "List locally-saved snapshots",
193
+ },
194
+ async run() {
195
+ const metas = await snapshotStore.list();
196
+ consola.log(formatSnapshotList(metas));
197
+ },
198
+ });
199
+ const snapshotShowCommand = defineCommand({
200
+ meta: {
201
+ name: "show",
202
+ description: "Show details of a saved snapshot",
203
+ },
204
+ args: {
205
+ id: {
206
+ type: "positional",
207
+ description: "Short ID (first 8 chars) or full UUID of the snapshot",
208
+ required: true,
209
+ },
210
+ },
211
+ async run({ args }) {
212
+ const snapshot = await snapshotStore.load(args.id);
213
+ if (!snapshot) {
214
+ consola.error(`Snapshot not found: ${pc.bold(args.id)}`);
215
+ process.exitCode = 1;
216
+ return;
217
+ }
218
+ consola.log(formatSnapshotDetail(snapshot));
219
+ },
220
+ });
221
+ const snapshotDiffCommand = defineCommand({
222
+ meta: {
223
+ name: "diff",
224
+ description: "Compare two snapshots (file + list level)",
225
+ },
226
+ args: {
227
+ id1: {
228
+ type: "positional",
229
+ description: "Short ID of the older snapshot",
230
+ required: true,
231
+ },
232
+ id2: {
233
+ type: "positional",
234
+ description: "Short ID of the newer snapshot",
235
+ required: true,
236
+ },
237
+ },
238
+ async run({ args }) {
239
+ const oldSnap = await snapshotStore.load(args.id1);
240
+ if (!oldSnap) {
241
+ consola.error(`Snapshot not found: ${pc.bold(args.id1)}`);
242
+ process.exitCode = 1;
243
+ return;
244
+ }
245
+ const newSnap = await snapshotStore.load(args.id2);
246
+ if (!newSnap) {
247
+ consola.error(`Snapshot not found: ${pc.bold(args.id2)}`);
248
+ process.exitCode = 1;
249
+ return;
250
+ }
251
+ const diff = diffSnapshots(oldSnap, newSnap);
252
+ consola.log(formatSnapshotDiff(diff));
253
+ },
254
+ });
255
+ const snapshotCommand = defineCommand({
256
+ meta: {
257
+ name: "snapshot",
258
+ description: "Manage locally-saved snapshots",
259
+ },
260
+ subCommands: {
261
+ list: snapshotListCommand,
262
+ show: snapshotShowCommand,
263
+ diff: snapshotDiffCommand,
264
+ },
265
+ });
266
+ const exportIconsCommand = defineCommand({
267
+ meta: {
268
+ name: "export-icons",
269
+ description: "Export application icons as PNG files",
270
+ },
271
+ args: {
272
+ output: {
273
+ type: "string",
274
+ description: "Output directory for PNG icons",
275
+ alias: "o",
276
+ required: false,
277
+ },
278
+ size: {
279
+ type: "string",
280
+ description: "Icon size in pixels (default: 128)",
281
+ alias: "s",
282
+ required: false,
283
+ },
284
+ upload: {
285
+ type: "boolean",
286
+ description: "Upload icons to R2 after export",
287
+ alias: "u",
288
+ required: false,
289
+ },
290
+ },
291
+ async run({ args }) {
292
+ const outputDir = args.output || join(homedir(), ".otter", "icons");
293
+ const size = parseInt(args.size || "128", 10);
294
+ consola.start(`Exporting app icons to ${pc.bold(outputDir)}...\n`);
295
+ const results = await exportIcons({
296
+ outputDir,
297
+ size,
298
+ onProgress: (result) => {
299
+ if (result.success) {
300
+ consola.log(` ${pc.green("✓")} ${result.appName}`);
301
+ }
302
+ else {
303
+ consola.log(` ${pc.yellow("⚠")} ${result.appName}: ${pc.dim(result.error ?? "unknown error")}`);
304
+ }
305
+ },
306
+ });
307
+ const succeeded = results.filter((r) => r.success).length;
308
+ const failed = results.filter((r) => !r.success).length;
309
+ consola.success(`\nExported ${pc.bold(String(succeeded))} icons` +
310
+ (failed > 0 ? ` (${pc.yellow(String(failed))} skipped)` : ""));
311
+ consola.info(`Output: ${pc.dim(outputDir)}`);
312
+ // Upload to R2 if requested
313
+ if (args.upload) {
314
+ const config = await configManager.load();
315
+ const r2Endpoint = config.iconR2Endpoint;
316
+ const r2AccessKeyId = config.iconR2AccessKeyId;
317
+ const r2SecretAccessKey = config.iconR2SecretAccessKey;
318
+ const r2Bucket = config.iconR2Bucket;
319
+ const r2PublicDomain = config.iconR2PublicDomain;
320
+ if (!r2Endpoint || !r2AccessKeyId || !r2SecretAccessKey || !r2Bucket || !r2PublicDomain) {
321
+ consola.error("R2 icon upload config not set. Run:\n" +
322
+ ` otter config set iconR2Endpoint <endpoint>\n` +
323
+ ` otter config set iconR2AccessKeyId <key>\n` +
324
+ ` otter config set iconR2SecretAccessKey <secret>\n` +
325
+ ` otter config set iconR2Bucket <bucket>\n` +
326
+ ` otter config set iconR2PublicDomain <domain>`);
327
+ process.exit(1);
328
+ }
329
+ const uploadConfig = {
330
+ r2Endpoint,
331
+ r2AccessKeyId,
332
+ r2SecretAccessKey,
333
+ r2Bucket,
334
+ r2PublicDomain,
335
+ };
336
+ const exportedIcons = results
337
+ .filter((r) => r.success && r.outputPath)
338
+ .map((r) => ({ appName: r.appName, pngPath: r.outputPath }));
339
+ consola.start(`\nUploading ${pc.bold(String(exportedIcons.length))} icons to R2...\n`);
340
+ const uploadResults = await uploadIcons(exportedIcons, uploadConfig, (result) => {
341
+ const status = result.uploaded ? pc.green("↑") : pc.dim("=");
342
+ consola.log(` ${status} ${result.appName} ${pc.dim(result.publicUrl)}`);
343
+ });
344
+ const uploaded = uploadResults.filter((r) => r.uploaded).length;
345
+ const skipped = uploadResults.filter((r) => !r.uploaded).length;
346
+ consola.success(`Uploaded ${pc.bold(String(uploaded))} icons` +
347
+ (skipped > 0 ? ` (${pc.dim(String(skipped))} unchanged)` : ""));
348
+ }
349
+ },
350
+ });
351
+ export const main = defineCommand({
352
+ meta: {
353
+ name: "otter",
354
+ version: "1.0.0",
355
+ description: "Backup and restore your Mac development environment configuration",
356
+ },
357
+ subCommands: {
358
+ scan: scanCommand,
359
+ backup: backupCommand,
360
+ config: configCommand,
361
+ snapshot: snapshotCommand,
362
+ "export-icons": exportIconsCommand,
363
+ },
364
+ });
365
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { BaseCollector } from "./base.js";
2
+ import type { CollectorCategory, CollectorResult } from "@otter/core";
3
+ /**
4
+ * Collects a list of installed applications from the Applications directory.
5
+ * List-only: no binary content is collected.
6
+ *
7
+ * When `iconBaseUrl` is provided, each item includes `meta.iconUrl` pointing
8
+ * to a deterministic R2 URL (SHA-256 hash of app name).
9
+ */
10
+ export declare class ApplicationsCollector extends BaseCollector {
11
+ private readonly appsDir;
12
+ private readonly iconBaseUrl?;
13
+ constructor(homeDir: string, appsDir?: string, iconBaseUrl?: string | undefined);
14
+ readonly id = "applications";
15
+ readonly label = "Installed Applications";
16
+ readonly category: CollectorCategory;
17
+ collect(): Promise<CollectorResult>;
18
+ }
19
+ //# sourceMappingURL=applications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applications.d.ts","sourceRoot":"","sources":["../../src/collectors/applications.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAQrB;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;IAGpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAF7B,OAAO,EAAE,MAAM,EACE,OAAO,GAAE,MAAwB,EACjC,WAAW,CAAC,EAAE,MAAM,YAAA;IAKvC,QAAQ,CAAC,EAAE,kBAAkB;IAC7B,QAAQ,CAAC,KAAK,4BAA4B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAiB;IAE/C,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;CA0B1C"}
@@ -0,0 +1,51 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readdir } from "node:fs/promises";
3
+ import { BaseCollector } from "./base.js";
4
+ /** Generate a deterministic icon URL from an app name and base URL */
5
+ function iconUrl(appName, baseUrl) {
6
+ const hash = createHash("sha256").update(appName).digest("hex").slice(0, 12);
7
+ return `${baseUrl}/${hash}.png`;
8
+ }
9
+ /**
10
+ * Collects a list of installed applications from the Applications directory.
11
+ * List-only: no binary content is collected.
12
+ *
13
+ * When `iconBaseUrl` is provided, each item includes `meta.iconUrl` pointing
14
+ * to a deterministic R2 URL (SHA-256 hash of app name).
15
+ */
16
+ export class ApplicationsCollector extends BaseCollector {
17
+ appsDir;
18
+ iconBaseUrl;
19
+ constructor(homeDir, appsDir = "/Applications", iconBaseUrl) {
20
+ super(homeDir);
21
+ this.appsDir = appsDir;
22
+ this.iconBaseUrl = iconBaseUrl;
23
+ }
24
+ id = "applications";
25
+ label = "Installed Applications";
26
+ category = "environment";
27
+ async collect() {
28
+ return this.timed(async (result) => {
29
+ try {
30
+ const entries = await readdir(this.appsDir, { withFileTypes: true });
31
+ const apps = entries
32
+ .filter((entry) => entry.isDirectory() && entry.name.endsWith(".app"))
33
+ .map((entry) => {
34
+ const name = entry.name.replace(/\.app$/, "");
35
+ const item = { name };
36
+ if (this.iconBaseUrl) {
37
+ item.meta = { iconUrl: iconUrl(name, this.iconBaseUrl) };
38
+ }
39
+ return item;
40
+ });
41
+ result.lists.push(...apps);
42
+ }
43
+ catch (err) {
44
+ if (err.code !== "ENOENT") {
45
+ result.errors.push(`Failed to read applications directory: ${err.message}`);
46
+ }
47
+ }
48
+ });
49
+ }
50
+ }
51
+ //# sourceMappingURL=applications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applications.js","sourceRoot":"","sources":["../../src/collectors/applications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAO1C,sEAAsE;AACtE,SAAS,OAAO,CAAC,OAAe,EAAE,OAAe;IAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7E,OAAO,GAAG,OAAO,IAAI,IAAI,MAAM,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,qBAAsB,SAAQ,aAAa;IAGnC;IACA;IAHnB,YACE,OAAe,EACE,UAAkB,eAAe,EACjC,WAAoB;QAErC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHE,YAAO,GAAP,OAAO,CAA0B;QACjC,gBAAW,GAAX,WAAW,CAAS;IAGvC,CAAC;IAEQ,EAAE,GAAG,cAAc,CAAC;IACpB,KAAK,GAAG,wBAAwB,CAAC;IACjC,QAAQ,GAAsB,aAAa,CAAC;IAErD,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,MAAM,IAAI,GAAwB,OAAO;qBACtC,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC9D;qBACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;oBACb,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;oBAC9C,MAAM,IAAI,GAAsB,EAAE,IAAI,EAAE,CAAC;oBACzC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;wBACrB,IAAI,CAAC,IAAI,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC3D,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC,CAAC,CAAC;gBACL,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,0CAA2C,GAAa,CAAC,OAAO,EAAE,CACnE,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ import type { Collector, CollectorCategory, CollectorResult, CollectedFile } from "@otter/core";
2
+ /** Options for safeReadFile */
3
+ export interface SafeReadOptions {
4
+ /** Override the default max file size (bytes) */
5
+ maxSize?: number;
6
+ /** If true, apply credential redaction based on file type */
7
+ redact?: boolean;
8
+ }
9
+ /** Options for collectDir to allow per-call customization */
10
+ export interface CollectDirOptions {
11
+ /** Custom filter — return false to skip a file */
12
+ filter?: (filePath: string) => boolean;
13
+ /** Override the default max file size (bytes) */
14
+ maxFileSize?: number;
15
+ /** Additional directory names to exclude */
16
+ excludeDirs?: Set<string>;
17
+ /** If true, apply credential redaction to collected files */
18
+ redact?: boolean;
19
+ }
20
+ /**
21
+ * Base class for all collectors. Provides common file reading utilities
22
+ * and standardized result creation.
23
+ */
24
+ export declare abstract class BaseCollector implements Collector {
25
+ protected readonly homeDir: string;
26
+ abstract readonly id: string;
27
+ abstract readonly label: string;
28
+ abstract readonly category: CollectorCategory;
29
+ constructor(homeDir: string);
30
+ abstract collect(): Promise<CollectorResult>;
31
+ /** Create an empty result skeleton */
32
+ protected createResult(): CollectorResult;
33
+ /**
34
+ * Safely read a single file. Returns null if file doesn't exist,
35
+ * can't be read, exceeds size limit, or is binary.
36
+ *
37
+ * @param redact If true, apply credential redaction based on file type
38
+ */
39
+ protected safeReadFile(filePath: string, result: CollectorResult, { maxSize, redact }?: SafeReadOptions): Promise<CollectedFile | null>;
40
+ /**
41
+ * Recursively collect all files in a directory.
42
+ *
43
+ * Safety features:
44
+ * - Skips excluded directories (.git, node_modules, cache, build output, etc.)
45
+ * - Skips binary files (by extension and known basenames)
46
+ * - Skips files exceeding size limit (default 512 KB)
47
+ */
48
+ protected collectDir(dirPath: string, result: CollectorResult, opts?: CollectDirOptions): Promise<CollectedFile[]>;
49
+ /** Measure execution time of the collect operation */
50
+ protected timed(fn: (result: CollectorResult) => Promise<void>): Promise<CollectorResult>;
51
+ }
52
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/collectors/base.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,aAAa,EACd,MAAM,aAAa,CAAC;AAoErB,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAcD;;;GAGG;AACH,8BAAsB,aAAc,YAAW,SAAS;IAK1C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM;IAJ9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;gBAEf,OAAO,EAAE,MAAM;IAE9C,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAE5C,sCAAsC;IACtC,SAAS,CAAC,YAAY,IAAI,eAAe;IAYzC;;;;;OAKG;cACa,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,eAAe,EACvB,EAAE,OAA6B,EAAE,MAAc,EAAE,GAAE,eAAoB,GACtE,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAsChC;;;;;;;OAOG;cACa,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,aAAa,EAAE,CAAC;IAoC3B,sDAAsD;cACtC,KAAK,CACnB,EAAE,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C,OAAO,CAAC,eAAe,CAAC;CAO5B"}