resurf 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/README.md +54 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Resurf CLI
|
|
2
|
+
|
|
3
|
+
Access Resurf vault data from the terminal.
|
|
4
|
+
|
|
5
|
+
## Install from npm
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g resurf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with Bun:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun install -g resurf
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
resurf vaults
|
|
21
|
+
resurf list --limit 10
|
|
22
|
+
resurf --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Run from repo
|
|
26
|
+
|
|
27
|
+
From the monorepo root (after building the CLI once):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun run resurf:build # build the CLI
|
|
31
|
+
bun run resurf -- vaults
|
|
32
|
+
bun run resurf -- list --limit 5
|
|
33
|
+
bun run resurf -- --help
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Install globally (use `resurf` anywhere)
|
|
37
|
+
|
|
38
|
+
From this package directory:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd packages/resurf-cli
|
|
42
|
+
bun run build
|
|
43
|
+
bun link
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then you can run `resurf` from any terminal:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
resurf vaults
|
|
50
|
+
resurf list --json
|
|
51
|
+
resurf --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To remove the global link: `bun unlink` (from `packages/resurf-cli`).
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/reader.ts
|
|
4
|
+
import * as fs2 from "fs/promises";
|
|
5
|
+
import * as path2 from "path";
|
|
6
|
+
|
|
7
|
+
// src/vault.ts
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as os from "os";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
var VAULT_NAMES = ["Resurf Vault", "Resurf Vault Dev"];
|
|
12
|
+
var APP_NAMES = ["Resurf", "Resurf Dev"];
|
|
13
|
+
var CONFIG_FILES = ["app.json", "app.dev.json"];
|
|
14
|
+
function getVaultStructure(vaultPath) {
|
|
15
|
+
const root = path.resolve(vaultPath);
|
|
16
|
+
return {
|
|
17
|
+
root,
|
|
18
|
+
captures: path.join(root, "captures"),
|
|
19
|
+
attachments: path.join(root, "attachments"),
|
|
20
|
+
index: path.join(root, ".index"),
|
|
21
|
+
config: path.join(root, ".config"),
|
|
22
|
+
trash: path.join(root, ".trash")
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function hasVaultConfig(dir) {
|
|
26
|
+
try {
|
|
27
|
+
const configPath = path.join(dir, ".config", "vault.json");
|
|
28
|
+
await fs.access(configPath);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function getVaultNameFromConfig(dir) {
|
|
35
|
+
try {
|
|
36
|
+
const configPath = path.join(dir, ".config", "vault.json");
|
|
37
|
+
const data = await fs.readFile(configPath, "utf-8");
|
|
38
|
+
const config = JSON.parse(data);
|
|
39
|
+
return config.name ?? path.basename(dir);
|
|
40
|
+
} catch {
|
|
41
|
+
return path.basename(dir);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function getCurrentVaultFromAppConfig() {
|
|
45
|
+
const home = os.homedir();
|
|
46
|
+
const base = path.join(home, "Library", "Application Support");
|
|
47
|
+
for (const appName of APP_NAMES) {
|
|
48
|
+
for (const configFile of CONFIG_FILES) {
|
|
49
|
+
const configPath = path.join(base, appName, configFile);
|
|
50
|
+
try {
|
|
51
|
+
const data = await fs.readFile(configPath, "utf-8");
|
|
52
|
+
const config = JSON.parse(data);
|
|
53
|
+
const vaultPath = config.vaultPath?.trim();
|
|
54
|
+
if (vaultPath) {
|
|
55
|
+
const valid = await hasVaultConfig(vaultPath);
|
|
56
|
+
return { path: vaultPath, valid };
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function getDefaultSearchDirs() {
|
|
65
|
+
const home = os.homedir();
|
|
66
|
+
const dirs = [];
|
|
67
|
+
const documents = path.join(home, "Documents");
|
|
68
|
+
dirs.push(documents);
|
|
69
|
+
for (const name of VAULT_NAMES) {
|
|
70
|
+
dirs.push(path.join(documents, name));
|
|
71
|
+
}
|
|
72
|
+
const iCloud = path.join(home, "Library", "Mobile Documents", "com~apple~CloudDocs");
|
|
73
|
+
dirs.push(iCloud);
|
|
74
|
+
for (const name of VAULT_NAMES) {
|
|
75
|
+
dirs.push(path.join(iCloud, name));
|
|
76
|
+
}
|
|
77
|
+
return dirs;
|
|
78
|
+
}
|
|
79
|
+
async function findVaults(options) {
|
|
80
|
+
const vaults = [];
|
|
81
|
+
const seen = /* @__PURE__ */ new Set();
|
|
82
|
+
const current = await getCurrentVaultFromAppConfig();
|
|
83
|
+
if (current?.path && current.valid) {
|
|
84
|
+
const name = await getVaultNameFromConfig(current.path);
|
|
85
|
+
vaults.push({ path: path.resolve(current.path), name, current: true });
|
|
86
|
+
seen.add(path.resolve(current.path));
|
|
87
|
+
}
|
|
88
|
+
const searchDirs = options?.scanDir ? [options.scanDir] : getDefaultSearchDirs();
|
|
89
|
+
for (const dir of searchDirs) {
|
|
90
|
+
try {
|
|
91
|
+
await fs.access(dir);
|
|
92
|
+
} catch {
|
|
93
|
+
if (options?.verbose) console.error("Skipping (inaccessible):", dir);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (await hasVaultConfig(dir)) {
|
|
97
|
+
const resolved = path.resolve(dir);
|
|
98
|
+
if (!seen.has(resolved)) {
|
|
99
|
+
const name = await getVaultNameFromConfig(dir);
|
|
100
|
+
vaults.push({
|
|
101
|
+
path: resolved,
|
|
102
|
+
name,
|
|
103
|
+
current: !!current?.path && path.resolve(current.path) === resolved
|
|
104
|
+
});
|
|
105
|
+
seen.add(resolved);
|
|
106
|
+
}
|
|
107
|
+
} else if (options?.scanDir) {
|
|
108
|
+
await findVaultsRecursive(dir, vaults, seen, current?.path ? path.resolve(current.path) : null);
|
|
109
|
+
} else {
|
|
110
|
+
const base = path.basename(dir);
|
|
111
|
+
if (base === "Documents" || base === "com~apple~CloudDocs") {
|
|
112
|
+
for (const vname of VAULT_NAMES) {
|
|
113
|
+
const sub = path.join(dir, vname);
|
|
114
|
+
try {
|
|
115
|
+
await fs.access(sub);
|
|
116
|
+
if (await hasVaultConfig(sub)) {
|
|
117
|
+
const resolved = path.resolve(sub);
|
|
118
|
+
if (!seen.has(resolved)) {
|
|
119
|
+
const name = await getVaultNameFromConfig(sub);
|
|
120
|
+
vaults.push({
|
|
121
|
+
path: resolved,
|
|
122
|
+
name,
|
|
123
|
+
current: !!current?.path && path.resolve(current.path) === resolved
|
|
124
|
+
});
|
|
125
|
+
seen.add(resolved);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const currentResolved = current?.path ? path.resolve(current.path) : null;
|
|
135
|
+
const normalized = vaults.map((v) => ({
|
|
136
|
+
...v,
|
|
137
|
+
path: path.resolve(v.path),
|
|
138
|
+
current: currentResolved === path.resolve(v.path)
|
|
139
|
+
}));
|
|
140
|
+
return Array.from(new Map(normalized.map((v) => [v.path, v])).values());
|
|
141
|
+
}
|
|
142
|
+
async function findVaultsRecursive(dir, results, seen, currentResolved) {
|
|
143
|
+
try {
|
|
144
|
+
const stat3 = await fs.stat(dir);
|
|
145
|
+
if (!stat3.isDirectory()) return;
|
|
146
|
+
if (seen.has(dir)) return;
|
|
147
|
+
seen.add(dir);
|
|
148
|
+
if (await hasVaultConfig(dir)) {
|
|
149
|
+
const name = await getVaultNameFromConfig(dir);
|
|
150
|
+
results.push({
|
|
151
|
+
path: path.resolve(dir),
|
|
152
|
+
name,
|
|
153
|
+
current: currentResolved === path.resolve(dir)
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
158
|
+
for (const ent of entries) {
|
|
159
|
+
if (ent.isDirectory() && !ent.name.startsWith(".")) {
|
|
160
|
+
const child = path.join(dir, ent.name);
|
|
161
|
+
await findVaultsRecursive(child, results, seen, currentResolved);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function discoverVaultPath(override) {
|
|
168
|
+
if (override?.trim()) {
|
|
169
|
+
const resolved = path.resolve(override);
|
|
170
|
+
const valid = await hasVaultConfig(resolved);
|
|
171
|
+
if (!valid) {
|
|
172
|
+
throw new Error(`Not a valid Resurf vault: ${resolved}`);
|
|
173
|
+
}
|
|
174
|
+
return resolved;
|
|
175
|
+
}
|
|
176
|
+
const current = await getCurrentVaultFromAppConfig();
|
|
177
|
+
if (current?.path && current.valid) {
|
|
178
|
+
return path.resolve(current.path);
|
|
179
|
+
}
|
|
180
|
+
const home = os.homedir();
|
|
181
|
+
const documents = path.join(home, "Documents");
|
|
182
|
+
const iCloud = path.join(home, "Library", "Mobile Documents", "com~apple~CloudDocs");
|
|
183
|
+
for (const name of VAULT_NAMES) {
|
|
184
|
+
for (const parent of [documents, iCloud]) {
|
|
185
|
+
const candidate = path.join(parent, name);
|
|
186
|
+
try {
|
|
187
|
+
await fs.access(candidate);
|
|
188
|
+
if (await hasVaultConfig(candidate)) {
|
|
189
|
+
return path.resolve(candidate);
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const fallback = path.join(documents, VAULT_NAMES[0]);
|
|
196
|
+
throw new Error(
|
|
197
|
+
`No Resurf vault found. Set one with --vault <path>, or create a vault at ${fallback}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/reader.ts
|
|
202
|
+
async function readJson(filePath) {
|
|
203
|
+
try {
|
|
204
|
+
const data = await fs2.readFile(filePath, "utf-8");
|
|
205
|
+
return JSON.parse(data);
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
var VaultReader = class {
|
|
211
|
+
structure;
|
|
212
|
+
constructor(vaultPath) {
|
|
213
|
+
this.structure = getVaultStructure(vaultPath);
|
|
214
|
+
}
|
|
215
|
+
get vaultPath() {
|
|
216
|
+
return this.structure.root;
|
|
217
|
+
}
|
|
218
|
+
async getCaptureIndex() {
|
|
219
|
+
const indexPath = path2.join(this.structure.index, "index.captures.json");
|
|
220
|
+
const data = await readJson(indexPath);
|
|
221
|
+
if (!data) return [];
|
|
222
|
+
return Object.values(data);
|
|
223
|
+
}
|
|
224
|
+
async getSpaces() {
|
|
225
|
+
const indexPath = path2.join(this.structure.index, "index.spaces.json");
|
|
226
|
+
const data = await readJson(indexPath);
|
|
227
|
+
if (!data) return [];
|
|
228
|
+
return Object.values(data);
|
|
229
|
+
}
|
|
230
|
+
async getAreas() {
|
|
231
|
+
const indexPath = path2.join(this.structure.index, "index.areas.json");
|
|
232
|
+
const data = await readJson(indexPath);
|
|
233
|
+
if (!data) return [];
|
|
234
|
+
return Object.values(data);
|
|
235
|
+
}
|
|
236
|
+
async getCanvases() {
|
|
237
|
+
const indexPath = path2.join(this.structure.index, "index.canvases.json");
|
|
238
|
+
const data = await readJson(indexPath);
|
|
239
|
+
if (!data) return [];
|
|
240
|
+
return Object.values(data);
|
|
241
|
+
}
|
|
242
|
+
async getTags() {
|
|
243
|
+
const indexPath = path2.join(this.structure.index, "index.tags.json");
|
|
244
|
+
const data = await readJson(indexPath);
|
|
245
|
+
return data ?? [];
|
|
246
|
+
}
|
|
247
|
+
async getCaptureById(id) {
|
|
248
|
+
const capturePath = path2.join(this.structure.captures, `${id}.json`);
|
|
249
|
+
return readJson(capturePath);
|
|
250
|
+
}
|
|
251
|
+
async listCaptures(options = {}) {
|
|
252
|
+
let captures = await this.getCaptureIndex();
|
|
253
|
+
captures = captures.filter((c) => !c.isHidden);
|
|
254
|
+
if (options.spaceKey) {
|
|
255
|
+
captures = captures.filter((c) => c.spaceKeys?.includes(options.spaceKey));
|
|
256
|
+
}
|
|
257
|
+
if (options.tag) {
|
|
258
|
+
captures = captures.filter((c) => c.tags?.includes(options.tag));
|
|
259
|
+
}
|
|
260
|
+
if (options.contentType) {
|
|
261
|
+
captures = captures.filter((c) => {
|
|
262
|
+
const ct = c.contentType;
|
|
263
|
+
if (Array.isArray(ct)) return ct.includes(options.contentType);
|
|
264
|
+
return ct === options.contentType;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (options.pinned !== void 0) {
|
|
268
|
+
captures = captures.filter((c) => c.isPinned === options.pinned);
|
|
269
|
+
}
|
|
270
|
+
if (options.search) {
|
|
271
|
+
const q = options.search.toLowerCase();
|
|
272
|
+
captures = captures.filter((c) => {
|
|
273
|
+
const fields = [c.title, c.note, c.tldr, c.source, ...c.tags ?? []].filter(Boolean);
|
|
274
|
+
return fields.some((f) => f.toLowerCase().includes(q));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
const sortField = options.sortBy === "updated" ? "updatedAt" : "createdAt";
|
|
278
|
+
const sortDir = options.sortOrder === "asc" ? 1 : -1;
|
|
279
|
+
captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);
|
|
280
|
+
const total = captures.length;
|
|
281
|
+
const offset = options.offset ?? 0;
|
|
282
|
+
const limit = options.limit ?? 20;
|
|
283
|
+
return {
|
|
284
|
+
captures: captures.slice(offset, offset + limit),
|
|
285
|
+
total
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async getStats() {
|
|
289
|
+
const [captures, spaces, areas, canvases, tags] = await Promise.all([
|
|
290
|
+
this.getCaptureIndex(),
|
|
291
|
+
this.getSpaces(),
|
|
292
|
+
this.getAreas(),
|
|
293
|
+
this.getCanvases(),
|
|
294
|
+
this.getTags()
|
|
295
|
+
]);
|
|
296
|
+
const byContentType = {};
|
|
297
|
+
const byTriageStatus = {};
|
|
298
|
+
for (const c of captures) {
|
|
299
|
+
const types = Array.isArray(c.contentType) ? c.contentType : c.contentType ? [c.contentType] : ["unknown"];
|
|
300
|
+
for (const t of types) {
|
|
301
|
+
byContentType[t] = (byContentType[t] ?? 0) + 1;
|
|
302
|
+
}
|
|
303
|
+
const status = c.triageStatus ?? "none";
|
|
304
|
+
byTriageStatus[status] = (byTriageStatus[status] ?? 0) + 1;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
totalCaptures: captures.length,
|
|
308
|
+
totalSpaces: spaces.length,
|
|
309
|
+
totalAreas: areas.length,
|
|
310
|
+
totalCanvases: canvases.length,
|
|
311
|
+
totalTags: tags.length,
|
|
312
|
+
byContentType,
|
|
313
|
+
byTriageStatus
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
async getVaultSize() {
|
|
317
|
+
let files = 0;
|
|
318
|
+
let bytes = 0;
|
|
319
|
+
const walk = async (dir) => {
|
|
320
|
+
try {
|
|
321
|
+
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
322
|
+
for (const entry of entries) {
|
|
323
|
+
const full = path2.join(dir, entry.name);
|
|
324
|
+
if (entry.isDirectory()) {
|
|
325
|
+
await walk(full);
|
|
326
|
+
} else {
|
|
327
|
+
files++;
|
|
328
|
+
try {
|
|
329
|
+
const stat3 = await fs2.stat(full);
|
|
330
|
+
bytes += stat3.size;
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
await walk(this.structure.root);
|
|
339
|
+
return { files, bytes };
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// src/format.ts
|
|
344
|
+
import * as path3 from "path";
|
|
345
|
+
function formatVaults(vaults, format) {
|
|
346
|
+
if (format === "json") return JSON.stringify(vaults, null, 2);
|
|
347
|
+
if (vaults.length === 0) return "No vaults found.";
|
|
348
|
+
if (format === "md") {
|
|
349
|
+
const lines = [];
|
|
350
|
+
lines.push(`| Path | Name | Current |`);
|
|
351
|
+
lines.push(`| --- | --- | --- |`);
|
|
352
|
+
for (const v of vaults) {
|
|
353
|
+
lines.push(`| \`${v.path}\` | ${v.name} | ${v.current ? "yes" : ""} |`);
|
|
354
|
+
}
|
|
355
|
+
return lines.join("\n");
|
|
356
|
+
}
|
|
357
|
+
if (format === "table") {
|
|
358
|
+
const pathW = 60;
|
|
359
|
+
const nameW = 24;
|
|
360
|
+
const header = `${padRight("PATH", pathW)} ${padRight("NAME", nameW)} CURRENT`;
|
|
361
|
+
const sep = "\u2500".repeat(header.length);
|
|
362
|
+
const rows = vaults.map((v) => {
|
|
363
|
+
const p = truncate(v.path, pathW);
|
|
364
|
+
const n = truncate(v.name, nameW);
|
|
365
|
+
return `${padRight(p, pathW)} ${padRight(n, nameW)} ${v.current ? "yes" : ""}`;
|
|
366
|
+
});
|
|
367
|
+
return [sep, header, sep, ...rows, sep].join("\n");
|
|
368
|
+
}
|
|
369
|
+
return vaults.map((v) => `${v.path}${v.current ? " (current)" : ""}`).join("\n");
|
|
370
|
+
}
|
|
371
|
+
function truncate(str, max) {
|
|
372
|
+
if (str.length <= max) return str;
|
|
373
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
374
|
+
}
|
|
375
|
+
function formatDate(ts) {
|
|
376
|
+
return new Date(ts).toLocaleString();
|
|
377
|
+
}
|
|
378
|
+
function relativeTime(ts) {
|
|
379
|
+
const diff = Date.now() - ts;
|
|
380
|
+
const seconds = Math.floor(diff / 1e3);
|
|
381
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
382
|
+
const minutes = Math.floor(seconds / 60);
|
|
383
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
384
|
+
const hours = Math.floor(minutes / 60);
|
|
385
|
+
if (hours < 24) return `${hours}h ago`;
|
|
386
|
+
const days = Math.floor(hours / 24);
|
|
387
|
+
if (days < 30) return `${days}d ago`;
|
|
388
|
+
const months = Math.floor(days / 30);
|
|
389
|
+
if (months < 12) return `${months}mo ago`;
|
|
390
|
+
return `${Math.floor(months / 12)}y ago`;
|
|
391
|
+
}
|
|
392
|
+
function padRight(str, len) {
|
|
393
|
+
return str + " ".repeat(Math.max(0, len - str.length));
|
|
394
|
+
}
|
|
395
|
+
function padLeft(str, len) {
|
|
396
|
+
return " ".repeat(Math.max(0, len - str.length)) + str;
|
|
397
|
+
}
|
|
398
|
+
function formatContentType(ct) {
|
|
399
|
+
if (!ct) return "\u2014";
|
|
400
|
+
if (Array.isArray(ct)) return ct.join(", ");
|
|
401
|
+
return ct;
|
|
402
|
+
}
|
|
403
|
+
function isEmbeddableImage(mime) {
|
|
404
|
+
return /^image\/(png|jpe?g|gif|webp|svg\+xml|bmp|avif)$/i.test(mime);
|
|
405
|
+
}
|
|
406
|
+
function isEmbeddableVideo(mime) {
|
|
407
|
+
return /^video\/(mp4|webm|mov|quicktime)$/i.test(mime);
|
|
408
|
+
}
|
|
409
|
+
function resolveAttachmentPath(vaultPath, relativePath) {
|
|
410
|
+
return path3.resolve(vaultPath, relativePath);
|
|
411
|
+
}
|
|
412
|
+
function formatSize(bytes) {
|
|
413
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
414
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
415
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
416
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
417
|
+
}
|
|
418
|
+
function formatCaptureList(captures, total, format) {
|
|
419
|
+
if (format === "json") return JSON.stringify({ captures, total }, null, 2);
|
|
420
|
+
if (captures.length === 0) return "No captures found.";
|
|
421
|
+
if (format === "md") {
|
|
422
|
+
const lines2 = [];
|
|
423
|
+
lines2.push(`| ID | Type | Title | Tags | Created |`);
|
|
424
|
+
lines2.push(`| --- | --- | --- | --- | --- |`);
|
|
425
|
+
for (const c of captures) {
|
|
426
|
+
const id = `\`${c.id.slice(0, 8)}\``;
|
|
427
|
+
const ct = formatContentType(c.contentType);
|
|
428
|
+
const title = c.title ? c.title.replace(/\|/g, "\\|") : "_(untitled)_";
|
|
429
|
+
const tags = c.tags?.length ? c.tags.map((t) => `\`${t}\``).join(", ") : "";
|
|
430
|
+
const date = relativeTime(c.createdAt);
|
|
431
|
+
lines2.push(`| ${id} | ${ct} | ${title} | ${tags} | ${date} |`);
|
|
432
|
+
}
|
|
433
|
+
lines2.push("");
|
|
434
|
+
lines2.push(`_Showing ${captures.length} of ${total} captures_`);
|
|
435
|
+
return lines2.join("\n");
|
|
436
|
+
}
|
|
437
|
+
if (format === "table") {
|
|
438
|
+
const idW = 10;
|
|
439
|
+
const typeW = 12;
|
|
440
|
+
const titleW = 40;
|
|
441
|
+
const tagsW = 20;
|
|
442
|
+
const dateW = 12;
|
|
443
|
+
const header = `${padRight("ID", idW)} ${padRight("TYPE", typeW)} ${padRight("TITLE", titleW)} ${padRight("TAGS", tagsW)} ${padRight("CREATED", dateW)}`;
|
|
444
|
+
const sep = "\u2500".repeat(header.length);
|
|
445
|
+
const rows = captures.map((c) => {
|
|
446
|
+
const id = truncate(c.id, idW);
|
|
447
|
+
const ct = truncate(formatContentType(c.contentType), typeW);
|
|
448
|
+
const title = truncate(c.title ?? "(untitled)", titleW);
|
|
449
|
+
const tags = truncate(c.tags?.join(", ") ?? "", tagsW);
|
|
450
|
+
const date = relativeTime(c.createdAt);
|
|
451
|
+
return `${padRight(id, idW)} ${padRight(ct, typeW)} ${padRight(title, titleW)} ${padRight(tags, tagsW)} ${padRight(date, dateW)}`;
|
|
452
|
+
});
|
|
453
|
+
return [`${sep}`, header, sep, ...rows, sep, `Showing ${captures.length} of ${total}`].join(
|
|
454
|
+
"\n"
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const lines = [];
|
|
458
|
+
for (const c of captures) {
|
|
459
|
+
const pin = c.isPinned ? " \u{1F4CC}" : "";
|
|
460
|
+
const title = c.title ?? "(untitled)";
|
|
461
|
+
const ct = formatContentType(c.contentType);
|
|
462
|
+
lines.push(` ${c.id.slice(0, 8)} ${ct.padEnd(10)} ${title}${pin}`);
|
|
463
|
+
if (c.tldr) lines.push(` ${truncate(c.tldr, 70)}`);
|
|
464
|
+
if (c.tags?.length) lines.push(` tags: ${c.tags.join(", ")}`);
|
|
465
|
+
lines.push("");
|
|
466
|
+
}
|
|
467
|
+
lines.push(`${captures.length} of ${total} captures`);
|
|
468
|
+
return lines.join("\n");
|
|
469
|
+
}
|
|
470
|
+
function formatCapture(capture, format, vaultPath) {
|
|
471
|
+
if (format === "json") return JSON.stringify(capture, null, 2);
|
|
472
|
+
if (format === "md") {
|
|
473
|
+
const lines2 = [];
|
|
474
|
+
const title = capture.title || "Untitled Capture";
|
|
475
|
+
lines2.push(`# ${title}`);
|
|
476
|
+
lines2.push("");
|
|
477
|
+
const content2 = capture.content;
|
|
478
|
+
if (content2?.type === "attachment" && content2.path) {
|
|
479
|
+
const fullPath = vaultPath ? resolveAttachmentPath(vaultPath, String(content2.path)) : String(content2.path);
|
|
480
|
+
const mime = String(content2.mimeType ?? "");
|
|
481
|
+
if (isEmbeddableImage(mime)) {
|
|
482
|
+
lines2.push(``);
|
|
483
|
+
lines2.push("");
|
|
484
|
+
} else if (isEmbeddableVideo(mime)) {
|
|
485
|
+
lines2.push(`[Video: ${title}](${fullPath})`);
|
|
486
|
+
lines2.push("");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
lines2.push(`- **ID:** \`${capture.id}\``);
|
|
490
|
+
lines2.push(`- **Type:** ${capture.type} (${formatContentType(capture.contentType)})`);
|
|
491
|
+
if (capture.source) lines2.push(`- **Source:** ${capture.source}`);
|
|
492
|
+
lines2.push(`- **Created:** ${formatDate(capture.createdAt)}`);
|
|
493
|
+
lines2.push(`- **Updated:** ${formatDate(capture.updatedAt)}`);
|
|
494
|
+
if (capture.isPinned) lines2.push(`- **Pinned:** yes`);
|
|
495
|
+
if (capture.tags?.length)
|
|
496
|
+
lines2.push(`- **Tags:** ${capture.tags.map((t) => `\`${t}\``).join(", ")}`);
|
|
497
|
+
if (capture.spaceKeys?.length)
|
|
498
|
+
lines2.push(`- **Spaces:** ${capture.spaceKeys.map((k) => `\`${k}\``).join(", ")}`);
|
|
499
|
+
if (capture.triageStatus) lines2.push(`- **Triage:** ${capture.triageStatus}`);
|
|
500
|
+
if (capture.tldr) {
|
|
501
|
+
lines2.push("");
|
|
502
|
+
lines2.push(`## TLDR`);
|
|
503
|
+
lines2.push(capture.tldr);
|
|
504
|
+
}
|
|
505
|
+
if (capture.note) {
|
|
506
|
+
lines2.push("");
|
|
507
|
+
lines2.push(`## Note`);
|
|
508
|
+
lines2.push(capture.note);
|
|
509
|
+
}
|
|
510
|
+
if (content2) {
|
|
511
|
+
if (content2.type === "note" && content2.textContent) {
|
|
512
|
+
lines2.push("");
|
|
513
|
+
lines2.push(`## Content`);
|
|
514
|
+
lines2.push(String(content2.textContent));
|
|
515
|
+
} else if (content2.type === "link") {
|
|
516
|
+
lines2.push("");
|
|
517
|
+
lines2.push(`## Content`);
|
|
518
|
+
if (content2.url) lines2.push(`**URL:** ${content2.url}`);
|
|
519
|
+
if (content2.ogTitle) lines2.push(`**OG Title:** ${content2.ogTitle}`);
|
|
520
|
+
if (content2.ogDescription)
|
|
521
|
+
lines2.push(`**OG Description:** ${truncate(String(content2.ogDescription), 200)}`);
|
|
522
|
+
} else if (content2.type === "attachment") {
|
|
523
|
+
const fullPath = vaultPath ? resolveAttachmentPath(vaultPath, String(content2.path)) : String(content2.path ?? "");
|
|
524
|
+
lines2.push("");
|
|
525
|
+
lines2.push(`## Attachment`);
|
|
526
|
+
if (content2.mimeType) lines2.push(`- **MIME:** ${content2.mimeType}`);
|
|
527
|
+
if (content2.size) lines2.push(`- **Size:** ${formatSize(Number(content2.size))}`);
|
|
528
|
+
lines2.push(`- **Path:** \`${fullPath}\``);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return lines2.join("\n");
|
|
532
|
+
}
|
|
533
|
+
const lines = [];
|
|
534
|
+
const sep = "\u2500".repeat(60);
|
|
535
|
+
lines.push(sep);
|
|
536
|
+
lines.push(` ID: ${capture.id}`);
|
|
537
|
+
lines.push(` Type: ${capture.type} (${formatContentType(capture.contentType)})`);
|
|
538
|
+
if (capture.title) lines.push(` Title: ${capture.title}`);
|
|
539
|
+
if (capture.source) lines.push(` Source: ${capture.source}`);
|
|
540
|
+
lines.push(` Created: ${formatDate(capture.createdAt)}`);
|
|
541
|
+
lines.push(` Updated: ${formatDate(capture.updatedAt)}`);
|
|
542
|
+
if (capture.isPinned) lines.push(` Pinned: yes`);
|
|
543
|
+
if (capture.tags?.length) lines.push(` Tags: ${capture.tags.join(", ")}`);
|
|
544
|
+
if (capture.spaceKeys?.length) lines.push(` Spaces: ${capture.spaceKeys.join(", ")}`);
|
|
545
|
+
if (capture.triageStatus) lines.push(` Triage: ${capture.triageStatus}`);
|
|
546
|
+
if (capture.processingStatus) lines.push(` Status: ${capture.processingStatus}`);
|
|
547
|
+
if (capture.tldr) {
|
|
548
|
+
lines.push(sep);
|
|
549
|
+
lines.push(" TLDR:");
|
|
550
|
+
lines.push(` ${capture.tldr}`);
|
|
551
|
+
}
|
|
552
|
+
if (capture.note) {
|
|
553
|
+
lines.push(sep);
|
|
554
|
+
lines.push(" Note:");
|
|
555
|
+
lines.push(` ${capture.note}`);
|
|
556
|
+
}
|
|
557
|
+
const content = capture.content;
|
|
558
|
+
if (content) {
|
|
559
|
+
lines.push(sep);
|
|
560
|
+
lines.push(" Content:");
|
|
561
|
+
if (content.type === "note" && content.textContent) {
|
|
562
|
+
const text = String(content.textContent);
|
|
563
|
+
lines.push(` ${truncate(text, 500)}`);
|
|
564
|
+
} else if (content.type === "link") {
|
|
565
|
+
if (content.url) lines.push(` URL: ${content.url}`);
|
|
566
|
+
if (content.ogTitle) lines.push(` OG Title: ${content.ogTitle}`);
|
|
567
|
+
if (content.ogDescription)
|
|
568
|
+
lines.push(` OG Desc: ${truncate(String(content.ogDescription), 200)}`);
|
|
569
|
+
} else if (content.type === "attachment") {
|
|
570
|
+
if (content.mimeType) lines.push(` MIME: ${content.mimeType}`);
|
|
571
|
+
if (content.size) lines.push(` Size: ${formatSize(Number(content.size))}`);
|
|
572
|
+
if (content.path) {
|
|
573
|
+
const fullPath = vaultPath ? resolveAttachmentPath(vaultPath, String(content.path)) : String(content.path);
|
|
574
|
+
lines.push(` Path: ${fullPath}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
lines.push(sep);
|
|
579
|
+
return lines.join("\n");
|
|
580
|
+
}
|
|
581
|
+
function formatSpaces(spaces, format) {
|
|
582
|
+
if (format === "json") return JSON.stringify(spaces, null, 2);
|
|
583
|
+
if (spaces.length === 0) return "No spaces found.";
|
|
584
|
+
if (format === "md") {
|
|
585
|
+
const lines = [];
|
|
586
|
+
lines.push(`| Key | Name | Captures | Pinned |`);
|
|
587
|
+
lines.push(`| --- | --- | ---: | --- |`);
|
|
588
|
+
for (const s of spaces) {
|
|
589
|
+
const pin = s.isPinned ? "yes" : "";
|
|
590
|
+
lines.push(`| \`${s.key}\` | ${s.name} | ${s.noteCount ?? 0} | ${pin} |`);
|
|
591
|
+
}
|
|
592
|
+
return lines.join("\n");
|
|
593
|
+
}
|
|
594
|
+
if (format === "table") {
|
|
595
|
+
const keyW = 20;
|
|
596
|
+
const nameW = 30;
|
|
597
|
+
const countW = 8;
|
|
598
|
+
const header = `${padRight("KEY", keyW)} ${padRight("NAME", nameW)} ${padLeft("COUNT", countW)}`;
|
|
599
|
+
const sep = "\u2500".repeat(header.length);
|
|
600
|
+
const rows = spaces.map((s) => {
|
|
601
|
+
return `${padRight(truncate(s.key, keyW), keyW)} ${padRight(truncate(s.name, nameW), nameW)} ${padLeft(String(s.noteCount ?? 0), countW)}`;
|
|
602
|
+
});
|
|
603
|
+
return [sep, header, sep, ...rows, sep].join("\n");
|
|
604
|
+
}
|
|
605
|
+
return spaces.map((s) => {
|
|
606
|
+
const pin = s.isPinned ? " \u{1F4CC}" : "";
|
|
607
|
+
return ` ${s.key.padEnd(20)} ${s.name}${pin} (${s.noteCount ?? 0} captures)`;
|
|
608
|
+
}).join("\n");
|
|
609
|
+
}
|
|
610
|
+
function formatAreas(areas, format) {
|
|
611
|
+
if (format === "json") return JSON.stringify(areas, null, 2);
|
|
612
|
+
if (areas.length === 0) return "No areas found.";
|
|
613
|
+
if (format === "md") {
|
|
614
|
+
const lines = [];
|
|
615
|
+
lines.push(`| Key | Name |`);
|
|
616
|
+
lines.push(`| --- | --- |`);
|
|
617
|
+
for (const a of areas) {
|
|
618
|
+
lines.push(`| \`${a.key}\` | ${a.name} |`);
|
|
619
|
+
}
|
|
620
|
+
return lines.join("\n");
|
|
621
|
+
}
|
|
622
|
+
return areas.map((a) => ` ${a.key.padEnd(20)} ${a.name}`).join("\n");
|
|
623
|
+
}
|
|
624
|
+
function formatTags(tags, format) {
|
|
625
|
+
if (format === "json") return JSON.stringify(tags, null, 2);
|
|
626
|
+
if (tags.length === 0) return "No tags found.";
|
|
627
|
+
if (format === "md") {
|
|
628
|
+
return tags.map((t) => `- \`${t}\``).join("\n");
|
|
629
|
+
}
|
|
630
|
+
return tags.map((t) => ` ${t}`).join("\n");
|
|
631
|
+
}
|
|
632
|
+
function formatCanvases(canvases, format) {
|
|
633
|
+
if (format === "json") return JSON.stringify(canvases, null, 2);
|
|
634
|
+
if (canvases.length === 0) return "No canvases found.";
|
|
635
|
+
if (format === "md") {
|
|
636
|
+
const lines = [];
|
|
637
|
+
lines.push(`| ID | Name | Nodes | Connections |`);
|
|
638
|
+
lines.push(`| --- | --- | ---: | ---: |`);
|
|
639
|
+
for (const c of canvases) {
|
|
640
|
+
lines.push(
|
|
641
|
+
`| \`${c.id.slice(0, 8)}\` | ${c.name} | ${c.nodes.length} | ${c.connections.length} |`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
return lines.join("\n");
|
|
645
|
+
}
|
|
646
|
+
return canvases.map(
|
|
647
|
+
(c) => ` ${c.id.slice(0, 8)} ${c.name} (${c.nodes.length} nodes, ${c.connections.length} connections)`
|
|
648
|
+
).join("\n");
|
|
649
|
+
}
|
|
650
|
+
function formatStats(stats, vaultPath, format) {
|
|
651
|
+
if (format === "json") return JSON.stringify({ vaultPath, ...stats }, null, 2);
|
|
652
|
+
if (format === "md") {
|
|
653
|
+
const lines2 = [];
|
|
654
|
+
lines2.push(`# Vault Stats`);
|
|
655
|
+
lines2.push("");
|
|
656
|
+
lines2.push(`**Path:** \`${vaultPath}\``);
|
|
657
|
+
lines2.push("");
|
|
658
|
+
lines2.push(`| Metric | Count |`);
|
|
659
|
+
lines2.push(`| --- | ---: |`);
|
|
660
|
+
lines2.push(`| Captures | ${stats.totalCaptures} |`);
|
|
661
|
+
lines2.push(`| Spaces | ${stats.totalSpaces} |`);
|
|
662
|
+
lines2.push(`| Areas | ${stats.totalAreas} |`);
|
|
663
|
+
lines2.push(`| Canvases | ${stats.totalCanvases} |`);
|
|
664
|
+
lines2.push(`| Tags | ${stats.totalTags} |`);
|
|
665
|
+
lines2.push("");
|
|
666
|
+
lines2.push(`### By Content Type`);
|
|
667
|
+
lines2.push("");
|
|
668
|
+
lines2.push(`| Type | Count |`);
|
|
669
|
+
lines2.push(`| --- | ---: |`);
|
|
670
|
+
const sortedTypes2 = Object.entries(stats.byContentType).sort((a, b) => b[1] - a[1]);
|
|
671
|
+
for (const [type, count] of sortedTypes2) {
|
|
672
|
+
lines2.push(`| ${type} | ${count} |`);
|
|
673
|
+
}
|
|
674
|
+
lines2.push("");
|
|
675
|
+
lines2.push(`### By Triage Status`);
|
|
676
|
+
lines2.push("");
|
|
677
|
+
lines2.push(`| Status | Count |`);
|
|
678
|
+
lines2.push(`| --- | ---: |`);
|
|
679
|
+
const sortedStatus2 = Object.entries(stats.byTriageStatus).sort((a, b) => b[1] - a[1]);
|
|
680
|
+
for (const [status, count] of sortedStatus2) {
|
|
681
|
+
lines2.push(`| ${status} | ${count} |`);
|
|
682
|
+
}
|
|
683
|
+
return lines2.join("\n");
|
|
684
|
+
}
|
|
685
|
+
const sep = "\u2500".repeat(40);
|
|
686
|
+
const lines = [];
|
|
687
|
+
lines.push(sep);
|
|
688
|
+
lines.push(` Vault: ${vaultPath}`);
|
|
689
|
+
lines.push(sep);
|
|
690
|
+
lines.push(` Captures: ${stats.totalCaptures}`);
|
|
691
|
+
lines.push(` Spaces: ${stats.totalSpaces}`);
|
|
692
|
+
lines.push(` Areas: ${stats.totalAreas}`);
|
|
693
|
+
lines.push(` Canvases: ${stats.totalCanvases}`);
|
|
694
|
+
lines.push(` Tags: ${stats.totalTags}`);
|
|
695
|
+
lines.push(sep);
|
|
696
|
+
lines.push(" By Content Type:");
|
|
697
|
+
const sortedTypes = Object.entries(stats.byContentType).sort((a, b) => b[1] - a[1]);
|
|
698
|
+
for (const [type, count] of sortedTypes) {
|
|
699
|
+
lines.push(` ${type.padEnd(14)} ${count}`);
|
|
700
|
+
}
|
|
701
|
+
lines.push(sep);
|
|
702
|
+
lines.push(" By Triage Status:");
|
|
703
|
+
const sortedStatus = Object.entries(stats.byTriageStatus).sort((a, b) => b[1] - a[1]);
|
|
704
|
+
for (const [status, count] of sortedStatus) {
|
|
705
|
+
lines.push(` ${status.padEnd(14)} ${count}`);
|
|
706
|
+
}
|
|
707
|
+
lines.push(sep);
|
|
708
|
+
return lines.join("\n");
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/index.ts
|
|
712
|
+
var VERSION = "1.0.0";
|
|
713
|
+
function parseGlobalFlags(args) {
|
|
714
|
+
const flags = { format: "pretty" };
|
|
715
|
+
const rest = [];
|
|
716
|
+
let i = 0;
|
|
717
|
+
while (i < args.length) {
|
|
718
|
+
const arg = args[i];
|
|
719
|
+
if (arg === "--vault" && i + 1 < args.length) {
|
|
720
|
+
flags.vault = args[i + 1];
|
|
721
|
+
i += 2;
|
|
722
|
+
} else if (arg === "--json") {
|
|
723
|
+
flags.format = "json";
|
|
724
|
+
i++;
|
|
725
|
+
} else if (arg === "--md" || arg === "--markdown") {
|
|
726
|
+
flags.format = "md";
|
|
727
|
+
i++;
|
|
728
|
+
} else if (arg === "--table") {
|
|
729
|
+
flags.format = "table";
|
|
730
|
+
i++;
|
|
731
|
+
} else if (arg === "--format" && i + 1 < args.length) {
|
|
732
|
+
flags.format = args[i + 1];
|
|
733
|
+
i += 2;
|
|
734
|
+
} else if (arg === "--verbose") {
|
|
735
|
+
flags.verbose = true;
|
|
736
|
+
i++;
|
|
737
|
+
} else {
|
|
738
|
+
rest.push(arg);
|
|
739
|
+
i++;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return { flags, rest };
|
|
743
|
+
}
|
|
744
|
+
function extractFlag(args, flag) {
|
|
745
|
+
const idx = args.indexOf(flag);
|
|
746
|
+
if (idx === -1 || idx + 1 >= args.length) return void 0;
|
|
747
|
+
return args[idx + 1];
|
|
748
|
+
}
|
|
749
|
+
function hasFlag(args, flag) {
|
|
750
|
+
return args.includes(flag);
|
|
751
|
+
}
|
|
752
|
+
function extractNumber(args, flag, defaultVal) {
|
|
753
|
+
const val = extractFlag(args, flag);
|
|
754
|
+
if (!val) return defaultVal;
|
|
755
|
+
const n = parseInt(val, 10);
|
|
756
|
+
return isNaN(n) ? defaultVal : n;
|
|
757
|
+
}
|
|
758
|
+
async function cmdList(reader, args, format) {
|
|
759
|
+
const limit = extractNumber(args, "--limit", 20);
|
|
760
|
+
const offset = extractNumber(args, "--offset", 0);
|
|
761
|
+
const spaceKey = extractFlag(args, "--space");
|
|
762
|
+
const tag = extractFlag(args, "--tag");
|
|
763
|
+
const contentType = extractFlag(args, "--type");
|
|
764
|
+
const search = extractFlag(args, "--search") ?? extractFlag(args, "-s");
|
|
765
|
+
const pinned = hasFlag(args, "--pinned") ? true : void 0;
|
|
766
|
+
const sortBy = extractFlag(args, "--sort") ?? "created";
|
|
767
|
+
const sortOrder = hasFlag(args, "--asc") ? "asc" : "desc";
|
|
768
|
+
const result = await reader.listCaptures({
|
|
769
|
+
limit,
|
|
770
|
+
offset,
|
|
771
|
+
spaceKey,
|
|
772
|
+
tag,
|
|
773
|
+
contentType,
|
|
774
|
+
search,
|
|
775
|
+
pinned,
|
|
776
|
+
sortBy,
|
|
777
|
+
sortOrder
|
|
778
|
+
});
|
|
779
|
+
console.log(formatCaptureList(result.captures, result.total, format));
|
|
780
|
+
}
|
|
781
|
+
async function cmdGet(reader, args, format) {
|
|
782
|
+
const id = args[0];
|
|
783
|
+
if (!id) {
|
|
784
|
+
console.error("Usage: resurf get <capture-id>");
|
|
785
|
+
process.exit(1);
|
|
786
|
+
}
|
|
787
|
+
let capture = await reader.getCaptureById(id);
|
|
788
|
+
if (!capture) {
|
|
789
|
+
const index = await reader.getCaptureIndex();
|
|
790
|
+
const match = index.find((c) => c.id.startsWith(id));
|
|
791
|
+
if (match) {
|
|
792
|
+
capture = await reader.getCaptureById(match.id);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (!capture) {
|
|
796
|
+
console.error(`Capture not found: ${id}`);
|
|
797
|
+
process.exit(1);
|
|
798
|
+
}
|
|
799
|
+
console.log(formatCapture(capture, format, reader.vaultPath));
|
|
800
|
+
}
|
|
801
|
+
async function cmdSearch(reader, args, format) {
|
|
802
|
+
const positional = [];
|
|
803
|
+
let i = 0;
|
|
804
|
+
while (i < args.length) {
|
|
805
|
+
if (args[i].startsWith("--") && i + 1 < args.length) {
|
|
806
|
+
i += 2;
|
|
807
|
+
} else if (args[i].startsWith("--")) {
|
|
808
|
+
i++;
|
|
809
|
+
} else {
|
|
810
|
+
positional.push(args[i]);
|
|
811
|
+
i++;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const query = positional.join(" ");
|
|
815
|
+
if (!query) {
|
|
816
|
+
console.error("Usage: resurf search <query>");
|
|
817
|
+
process.exit(1);
|
|
818
|
+
}
|
|
819
|
+
const limit = extractNumber(args, "--limit", 20);
|
|
820
|
+
const result = await reader.listCaptures({ search: query, limit });
|
|
821
|
+
console.log(formatCaptureList(result.captures, result.total, format));
|
|
822
|
+
}
|
|
823
|
+
async function cmdSpaces(reader, _args, format) {
|
|
824
|
+
const spaces = await reader.getSpaces();
|
|
825
|
+
console.log(formatSpaces(spaces, format));
|
|
826
|
+
}
|
|
827
|
+
async function cmdAreas(reader, _args, format) {
|
|
828
|
+
const areas = await reader.getAreas();
|
|
829
|
+
console.log(formatAreas(areas, format));
|
|
830
|
+
}
|
|
831
|
+
async function cmdTags(reader, _args, format) {
|
|
832
|
+
const tags = await reader.getTags();
|
|
833
|
+
console.log(formatTags(tags, format));
|
|
834
|
+
}
|
|
835
|
+
async function cmdCanvases(reader, _args, format) {
|
|
836
|
+
const canvases = await reader.getCanvases();
|
|
837
|
+
console.log(formatCanvases(canvases, format));
|
|
838
|
+
}
|
|
839
|
+
async function cmdStats(reader, _args, format) {
|
|
840
|
+
const stats = await reader.getStats();
|
|
841
|
+
console.log(formatStats(stats, reader.vaultPath, format));
|
|
842
|
+
}
|
|
843
|
+
async function cmdOpen(reader, args) {
|
|
844
|
+
const id = args[0];
|
|
845
|
+
if (!id) {
|
|
846
|
+
console.error("Usage: resurf open <capture-id>");
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
let captureId = id;
|
|
850
|
+
const capture = await reader.getCaptureById(id);
|
|
851
|
+
if (!capture) {
|
|
852
|
+
const index = await reader.getCaptureIndex();
|
|
853
|
+
const match = index.find((c) => c.id.startsWith(id));
|
|
854
|
+
if (match) captureId = match.id;
|
|
855
|
+
}
|
|
856
|
+
const { exec } = await import("child_process");
|
|
857
|
+
const url = `resurf://capture/${captureId}`;
|
|
858
|
+
exec(`open "${url}"`);
|
|
859
|
+
console.log(`Opening capture in Resurf: ${captureId}`);
|
|
860
|
+
}
|
|
861
|
+
async function cmdCreate(args) {
|
|
862
|
+
const content = extractFlag(args, "--content") ?? extractFlag(args, "--text");
|
|
863
|
+
const url = extractFlag(args, "--url");
|
|
864
|
+
const title = extractFlag(args, "--title");
|
|
865
|
+
const tags = extractFlag(args, "--tags");
|
|
866
|
+
const source = extractFlag(args, "--source");
|
|
867
|
+
const space = extractFlag(args, "--space");
|
|
868
|
+
if (!content && !url) {
|
|
869
|
+
console.error(
|
|
870
|
+
"Usage: resurf create --content <text> [--url <url>] [--title <title>] [--tags <tags>]"
|
|
871
|
+
);
|
|
872
|
+
process.exit(1);
|
|
873
|
+
}
|
|
874
|
+
const params = new URLSearchParams();
|
|
875
|
+
if (content) params.set("content", content);
|
|
876
|
+
if (url) params.set("url", url);
|
|
877
|
+
if (title) params.set("title", title);
|
|
878
|
+
if (tags) params.set("tags", tags);
|
|
879
|
+
if (source) params.set("source", source);
|
|
880
|
+
if (space) params.set("space", space);
|
|
881
|
+
const protocolUrl = `resurf://new?${params.toString()}`;
|
|
882
|
+
const { exec } = await import("child_process");
|
|
883
|
+
exec(`open "${protocolUrl}"`);
|
|
884
|
+
console.log("Creating capture via Resurf protocol...");
|
|
885
|
+
}
|
|
886
|
+
async function cmdExport(reader, args, format) {
|
|
887
|
+
const spaceKey = extractFlag(args, "--space");
|
|
888
|
+
const tag = extractFlag(args, "--tag");
|
|
889
|
+
const contentType = extractFlag(args, "--type");
|
|
890
|
+
const result = await reader.listCaptures({
|
|
891
|
+
limit: 999999,
|
|
892
|
+
spaceKey,
|
|
893
|
+
tag,
|
|
894
|
+
contentType
|
|
895
|
+
});
|
|
896
|
+
const captures = [];
|
|
897
|
+
for (const item of result.captures) {
|
|
898
|
+
const full = await reader.getCaptureById(item.id);
|
|
899
|
+
if (full) captures.push(full);
|
|
900
|
+
}
|
|
901
|
+
if (format === "json") {
|
|
902
|
+
console.log(JSON.stringify(captures, null, 2));
|
|
903
|
+
} else if (format === "md") {
|
|
904
|
+
const lines = [];
|
|
905
|
+
for (const c of captures) {
|
|
906
|
+
const cap = c;
|
|
907
|
+
const title = cap.title || "Untitled";
|
|
908
|
+
const id = cap.id;
|
|
909
|
+
lines.push(`## ${title}`);
|
|
910
|
+
lines.push(`- **ID:** \`${id}\``);
|
|
911
|
+
if (cap.contentType) lines.push(`- **Type:** ${cap.contentType}`);
|
|
912
|
+
if (cap.tldr) lines.push(`- **TLDR:** ${cap.tldr}`);
|
|
913
|
+
if (cap.note) lines.push(`- **Note:** ${cap.note}`);
|
|
914
|
+
lines.push("");
|
|
915
|
+
}
|
|
916
|
+
lines.push(`_Exported ${captures.length} captures_`);
|
|
917
|
+
console.log(lines.join("\n"));
|
|
918
|
+
} else {
|
|
919
|
+
console.log(JSON.stringify(captures, null, 2));
|
|
920
|
+
console.error(`
|
|
921
|
+
Exported ${captures.length} captures (use --json or --md for piping)`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function printHelp() {
|
|
925
|
+
const help = `
|
|
926
|
+
resurf - Access Resurf vault data from the command line
|
|
927
|
+
|
|
928
|
+
USAGE
|
|
929
|
+
resurf <command> [options]
|
|
930
|
+
|
|
931
|
+
COMMANDS
|
|
932
|
+
list List captures (default)
|
|
933
|
+
get <id> Get a capture by ID (supports partial IDs)
|
|
934
|
+
search <query> Search captures by title, note, or TLDR
|
|
935
|
+
spaces List all spaces
|
|
936
|
+
areas List all areas
|
|
937
|
+
tags List all tags
|
|
938
|
+
canvases List all canvases
|
|
939
|
+
stats Show vault statistics
|
|
940
|
+
vaults List available Resurf vaults on this computer
|
|
941
|
+
open <id> Open a capture in Resurf app
|
|
942
|
+
create Create a capture via Resurf protocol
|
|
943
|
+
export Export captures as JSON
|
|
944
|
+
|
|
945
|
+
GLOBAL OPTIONS
|
|
946
|
+
--vault <path> Path to Resurf vault (auto-detected if omitted)
|
|
947
|
+
--json Output as JSON
|
|
948
|
+
--md, --markdown Output as Markdown
|
|
949
|
+
--table Output as table
|
|
950
|
+
--format <fmt> Output format: json, md, table, pretty (default: pretty)
|
|
951
|
+
|
|
952
|
+
LIST OPTIONS
|
|
953
|
+
--limit <n> Max results (default: 20)
|
|
954
|
+
--offset <n> Skip first N results
|
|
955
|
+
--space <key> Filter by space key
|
|
956
|
+
--tag <tag> Filter by tag
|
|
957
|
+
--type <type> Filter by content type (note, link, image, video, etc.)
|
|
958
|
+
--search <query> Filter by text search
|
|
959
|
+
--pinned Show only pinned captures
|
|
960
|
+
--sort <field> Sort by: created, updated (default: created)
|
|
961
|
+
--asc Sort ascending (default: descending)
|
|
962
|
+
|
|
963
|
+
SEARCH OPTIONS
|
|
964
|
+
--limit <n> Max results (default: 20)
|
|
965
|
+
|
|
966
|
+
CREATE OPTIONS
|
|
967
|
+
--content <text> Text content for the capture
|
|
968
|
+
--url <url> URL to capture
|
|
969
|
+
--title <title> Title for the capture
|
|
970
|
+
--tags <tags> Comma-separated tags
|
|
971
|
+
--source <source> Source identifier
|
|
972
|
+
--space <key> Space key to assign
|
|
973
|
+
|
|
974
|
+
EXPORT OPTIONS
|
|
975
|
+
--space <key> Filter by space key
|
|
976
|
+
--tag <tag> Filter by tag
|
|
977
|
+
--type <type> Filter by content type
|
|
978
|
+
|
|
979
|
+
VAULTS OPTIONS
|
|
980
|
+
[directory] Scan this directory recursively for vaults
|
|
981
|
+
--verbose Log skipped inaccessible paths
|
|
982
|
+
|
|
983
|
+
EXAMPLES
|
|
984
|
+
resurf list --limit 10 --json
|
|
985
|
+
resurf list --space work --type note
|
|
986
|
+
resurf get abc123
|
|
987
|
+
resurf search "meeting notes"
|
|
988
|
+
resurf search "react hooks" --json | jq '.[].title'
|
|
989
|
+
resurf spaces --json
|
|
990
|
+
resurf tags
|
|
991
|
+
resurf stats
|
|
992
|
+
resurf vaults
|
|
993
|
+
resurf vaults --json
|
|
994
|
+
resurf vaults /path/to/folder
|
|
995
|
+
resurf open abc123
|
|
996
|
+
resurf create --content "Quick note" --tags "idea,work"
|
|
997
|
+
resurf create --url "https://example.com" --title "Example"
|
|
998
|
+
resurf export --space work --json > work-captures.json
|
|
999
|
+
resurf list --json | jq '.captures[].id'
|
|
1000
|
+
|
|
1001
|
+
VERSION
|
|
1002
|
+
${VERSION}
|
|
1003
|
+
`;
|
|
1004
|
+
console.log(help.trim());
|
|
1005
|
+
}
|
|
1006
|
+
async function main() {
|
|
1007
|
+
const rawArgs = process.argv.slice(2);
|
|
1008
|
+
if (rawArgs.length === 0 || rawArgs[0] === "--help" || rawArgs[0] === "-h") {
|
|
1009
|
+
printHelp();
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (rawArgs[0] === "--version" || rawArgs[0] === "-v") {
|
|
1013
|
+
console.log(VERSION);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const { flags, rest } = parseGlobalFlags(rawArgs);
|
|
1017
|
+
const command = rest[0] ?? "list";
|
|
1018
|
+
const commandArgs = rest.slice(1);
|
|
1019
|
+
if (command === "create") {
|
|
1020
|
+
await cmdCreate(commandArgs);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
if (command === "vaults" || command === "find-vaults") {
|
|
1024
|
+
const scanDir = commandArgs.find((a) => !a.startsWith("-"));
|
|
1025
|
+
const vaults = await findVaults({ scanDir, verbose: flags.verbose });
|
|
1026
|
+
console.log(formatVaults(vaults, flags.format));
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const vaultPath = await discoverVaultPath(flags.vault);
|
|
1030
|
+
const reader = new VaultReader(vaultPath);
|
|
1031
|
+
const commands = {
|
|
1032
|
+
list: cmdList,
|
|
1033
|
+
ls: cmdList,
|
|
1034
|
+
get: cmdGet,
|
|
1035
|
+
show: cmdGet,
|
|
1036
|
+
search: cmdSearch,
|
|
1037
|
+
find: cmdSearch,
|
|
1038
|
+
spaces: cmdSpaces,
|
|
1039
|
+
areas: cmdAreas,
|
|
1040
|
+
tags: cmdTags,
|
|
1041
|
+
canvases: cmdCanvases,
|
|
1042
|
+
stats: cmdStats,
|
|
1043
|
+
info: cmdStats,
|
|
1044
|
+
export: cmdExport
|
|
1045
|
+
};
|
|
1046
|
+
if (command === "open") {
|
|
1047
|
+
await cmdOpen(reader, commandArgs);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
const handler = commands[command];
|
|
1051
|
+
if (!handler) {
|
|
1052
|
+
console.error(`Unknown command: ${command}`);
|
|
1053
|
+
console.error(`Run 'resurf --help' for usage.`);
|
|
1054
|
+
process.exit(1);
|
|
1055
|
+
}
|
|
1056
|
+
await handler(reader, commandArgs, flags.format);
|
|
1057
|
+
}
|
|
1058
|
+
main().catch((err) => {
|
|
1059
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1060
|
+
console.error(`Error: ${message}`);
|
|
1061
|
+
process.exit(1);
|
|
1062
|
+
});
|
|
1063
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reader.ts","../src/vault.ts","../src/format.ts","../src/index.ts"],"sourcesContent":["import * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { type VaultStructure, getVaultStructure } from \"./vault.js\";\n\nexport type CaptureIndex = {\n id: string;\n type: string;\n filePath?: string;\n createdAt: number;\n updatedAt: number;\n contentType?: string | string[];\n title?: string;\n tags?: string[];\n spaceKeys?: string[];\n mimeTypes?: string[];\n note?: string;\n tldr?: string;\n source?: string;\n isPinned?: boolean;\n isHidden?: boolean;\n triageStatus?: string;\n processingStatus?: string;\n};\n\nexport type Space = {\n id: string;\n name: string;\n key: string;\n areaKey?: string;\n icon?: string;\n color?: string;\n createdAt: number;\n updatedAt: number;\n noteCount?: number;\n isPinned?: boolean;\n};\n\nexport type Area = {\n id: string;\n name: string;\n key: string;\n createdAt: number;\n updatedAt: number;\n};\n\nexport type Canvas = {\n id: string;\n name: string;\n createdAt: number;\n updatedAt: number;\n nodes: unknown[];\n connections: unknown[];\n annotations: unknown[];\n};\n\nexport type Capture = CaptureIndex & {\n content: Record<string, unknown>;\n author?: Record<string, unknown>;\n colorPalette?: unknown[];\n embedding?: number[];\n};\n\ntype IndexMap<T> = Record<string, T>;\n\nasync function readJson<T>(filePath: string): Promise<T | null> {\n try {\n const data = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n\nexport class VaultReader {\n private structure: VaultStructure;\n\n constructor(vaultPath: string) {\n this.structure = getVaultStructure(vaultPath);\n }\n\n get vaultPath(): string {\n return this.structure.root;\n }\n\n async getCaptureIndex(): Promise<CaptureIndex[]> {\n const indexPath = path.join(this.structure.index, \"index.captures.json\");\n const data = await readJson<IndexMap<CaptureIndex>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getSpaces(): Promise<Space[]> {\n const indexPath = path.join(this.structure.index, \"index.spaces.json\");\n const data = await readJson<IndexMap<Space>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getAreas(): Promise<Area[]> {\n const indexPath = path.join(this.structure.index, \"index.areas.json\");\n const data = await readJson<IndexMap<Area>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getCanvases(): Promise<Canvas[]> {\n const indexPath = path.join(this.structure.index, \"index.canvases.json\");\n const data = await readJson<IndexMap<Canvas>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getTags(): Promise<string[]> {\n const indexPath = path.join(this.structure.index, \"index.tags.json\");\n const data = await readJson<string[]>(indexPath);\n return data ?? [];\n }\n\n async getCaptureById(id: string): Promise<Capture | null> {\n const capturePath = path.join(this.structure.captures, `${id}.json`);\n return readJson<Capture>(capturePath);\n }\n\n async listCaptures(\n options: {\n limit?: number;\n offset?: number;\n spaceKey?: string;\n tag?: string;\n contentType?: string;\n search?: string;\n pinned?: boolean;\n sortBy?: \"created\" | \"updated\";\n sortOrder?: \"asc\" | \"desc\";\n } = {}\n ): Promise<{ captures: CaptureIndex[]; total: number }> {\n let captures = await this.getCaptureIndex();\n\n captures = captures.filter((c) => !c.isHidden);\n\n if (options.spaceKey) {\n captures = captures.filter((c) => c.spaceKeys?.includes(options.spaceKey!));\n }\n\n if (options.tag) {\n captures = captures.filter((c) => c.tags?.includes(options.tag!));\n }\n\n if (options.contentType) {\n captures = captures.filter((c) => {\n const ct = c.contentType;\n if (Array.isArray(ct)) return ct.includes(options.contentType!);\n return ct === options.contentType;\n });\n }\n\n if (options.pinned !== undefined) {\n captures = captures.filter((c) => c.isPinned === options.pinned);\n }\n\n if (options.search) {\n const q = options.search.toLowerCase();\n captures = captures.filter((c) => {\n const fields = [c.title, c.note, c.tldr, c.source, ...(c.tags ?? [])].filter(Boolean);\n return fields.some((f) => f!.toLowerCase().includes(q));\n });\n }\n\n const sortField = options.sortBy === \"updated\" ? \"updatedAt\" : \"createdAt\";\n const sortDir = options.sortOrder === \"asc\" ? 1 : -1;\n captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);\n\n const total = captures.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 20;\n\n return {\n captures: captures.slice(offset, offset + limit),\n total,\n };\n }\n\n async getStats(): Promise<{\n totalCaptures: number;\n totalSpaces: number;\n totalAreas: number;\n totalCanvases: number;\n totalTags: number;\n byContentType: Record<string, number>;\n byTriageStatus: Record<string, number>;\n }> {\n const [captures, spaces, areas, canvases, tags] = await Promise.all([\n this.getCaptureIndex(),\n this.getSpaces(),\n this.getAreas(),\n this.getCanvases(),\n this.getTags(),\n ]);\n\n const byContentType: Record<string, number> = {};\n const byTriageStatus: Record<string, number> = {};\n\n for (const c of captures) {\n const types = Array.isArray(c.contentType)\n ? c.contentType\n : c.contentType\n ? [c.contentType]\n : [\"unknown\"];\n for (const t of types) {\n byContentType[t] = (byContentType[t] ?? 0) + 1;\n }\n const status = c.triageStatus ?? \"none\";\n byTriageStatus[status] = (byTriageStatus[status] ?? 0) + 1;\n }\n\n return {\n totalCaptures: captures.length,\n totalSpaces: spaces.length,\n totalAreas: areas.length,\n totalCanvases: canvases.length,\n totalTags: tags.length,\n byContentType,\n byTriageStatus,\n };\n }\n\n async getVaultSize(): Promise<{ files: number; bytes: number }> {\n let files = 0;\n let bytes = 0;\n\n const walk = async (dir: string): Promise<void> => {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n } else {\n files++;\n try {\n const stat = await fs.stat(full);\n bytes += stat.size;\n } catch {}\n }\n }\n } catch {}\n };\n\n await walk(this.structure.root);\n return { files, bytes };\n }\n}\n","import * as fs from \"fs/promises\";\nimport * as os from \"os\";\nimport * as path from \"path\";\n\nconst VAULT_NAMES = [\"Resurf Vault\", \"Resurf Vault Dev\"];\nconst APP_NAMES = [\"Resurf\", \"Resurf Dev\"];\nconst CONFIG_FILES = [\"app.json\", \"app.dev.json\"];\n\nexport type VaultStructure = {\n root: string;\n captures: string;\n attachments: string;\n index: string;\n config: string;\n trash: string;\n};\n\nexport function getVaultStructure(vaultPath: string): VaultStructure {\n const root = path.resolve(vaultPath);\n return {\n root,\n captures: path.join(root, \"captures\"),\n attachments: path.join(root, \"attachments\"),\n index: path.join(root, \".index\"),\n config: path.join(root, \".config\"),\n trash: path.join(root, \".trash\"),\n };\n}\n\nexport async function hasVaultConfig(dir: string): Promise<boolean> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n await fs.access(configPath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function getVaultNameFromConfig(dir: string): Promise<string> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { name?: string };\n return config.name ?? path.basename(dir);\n } catch {\n return path.basename(dir);\n }\n}\n\nasync function getCurrentVaultFromAppConfig(): Promise<{\n path: string;\n valid: boolean;\n} | null> {\n const home = os.homedir();\n const base = path.join(home, \"Library\", \"Application Support\");\n for (const appName of APP_NAMES) {\n for (const configFile of CONFIG_FILES) {\n const configPath = path.join(base, appName, configFile);\n try {\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { vaultPath?: string };\n const vaultPath = config.vaultPath?.trim();\n if (vaultPath) {\n const valid = await hasVaultConfig(vaultPath);\n return { path: vaultPath, valid };\n }\n } catch {\n // ignore\n }\n }\n }\n return null;\n}\n\nfunction getDefaultSearchDirs(): string[] {\n const home = os.homedir();\n const dirs: string[] = [];\n const documents = path.join(home, \"Documents\");\n dirs.push(documents);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(documents, name));\n }\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n dirs.push(iCloud);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(iCloud, name));\n }\n return dirs;\n}\n\nexport type VaultInfo = {\n path: string;\n name: string;\n current: boolean;\n};\n\nexport async function findVaults(options?: {\n scanDir?: string;\n verbose?: boolean;\n}): Promise<VaultInfo[]> {\n const vaults: VaultInfo[] = [];\n const seen = new Set<string>();\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n const name = await getVaultNameFromConfig(current.path);\n vaults.push({ path: path.resolve(current.path), name, current: true });\n seen.add(path.resolve(current.path));\n }\n\n const searchDirs = options?.scanDir ? [options.scanDir] : getDefaultSearchDirs();\n for (const dir of searchDirs) {\n try {\n await fs.access(dir);\n } catch {\n if (options?.verbose) console.error(\"Skipping (inaccessible):\", dir);\n continue;\n }\n if (await hasVaultConfig(dir)) {\n const resolved = path.resolve(dir);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(dir);\n vaults.push({\n path: resolved,\n name,\n current: !!current?.path && path.resolve(current.path) === resolved,\n });\n seen.add(resolved);\n }\n } else if (options?.scanDir) {\n await findVaultsRecursive(dir, vaults, seen, current?.path ? path.resolve(current.path) : null);\n } else {\n const base = path.basename(dir);\n if (base === \"Documents\" || base === \"com~apple~CloudDocs\") {\n for (const vname of VAULT_NAMES) {\n const sub = path.join(dir, vname);\n try {\n await fs.access(sub);\n if (await hasVaultConfig(sub)) {\n const resolved = path.resolve(sub);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(sub);\n vaults.push({\n path: resolved,\n name,\n current: !!current?.path && path.resolve(current.path) === resolved,\n });\n seen.add(resolved);\n }\n }\n } catch {\n // skip\n }\n }\n }\n }\n }\n\n const currentResolved = current?.path ? path.resolve(current.path) : null;\n const normalized = vaults.map((v) => ({\n ...v,\n path: path.resolve(v.path),\n current: currentResolved === path.resolve(v.path),\n }));\n return Array.from(new Map(normalized.map((v) => [v.path, v])).values());\n}\n\nasync function findVaultsRecursive(\n dir: string,\n results: VaultInfo[],\n seen: Set<string>,\n currentResolved: string | null\n): Promise<void> {\n try {\n const stat = await fs.stat(dir);\n if (!stat.isDirectory()) return;\n if (seen.has(dir)) return;\n seen.add(dir);\n\n if (await hasVaultConfig(dir)) {\n const name = await getVaultNameFromConfig(dir);\n results.push({\n path: path.resolve(dir),\n name,\n current: currentResolved === path.resolve(dir),\n });\n return;\n }\n\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);\n for (const ent of entries) {\n if (ent.isDirectory() && !ent.name.startsWith(\".\")) {\n const child = path.join(dir, ent.name);\n await findVaultsRecursive(child, results, seen, currentResolved);\n }\n }\n } catch {\n // skip inaccessible dirs\n }\n}\n\nexport async function discoverVaultPath(override?: string): Promise<string> {\n if (override?.trim()) {\n const resolved = path.resolve(override);\n const valid = await hasVaultConfig(resolved);\n if (!valid) {\n throw new Error(`Not a valid Resurf vault: ${resolved}`);\n }\n return resolved;\n }\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n return path.resolve(current.path);\n }\n\n const home = os.homedir();\n const documents = path.join(home, \"Documents\");\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n\n for (const name of VAULT_NAMES) {\n for (const parent of [documents, iCloud]) {\n const candidate = path.join(parent, name);\n try {\n await fs.access(candidate);\n if (await hasVaultConfig(candidate)) {\n return path.resolve(candidate);\n }\n } catch {\n // skip\n }\n }\n }\n\n const fallback = path.join(documents, VAULT_NAMES[0]);\n throw new Error(\n `No Resurf vault found. Set one with --vault <path>, or create a vault at ${fallback}`\n );\n}\n","import * as path from \"path\";\nimport type { CaptureIndex, Capture, Space, Area, Canvas } from \"./reader.js\";\nimport type { VaultInfo } from \"./vault.js\";\n\nexport type OutputFormat = \"json\" | \"md\" | \"table\" | \"pretty\";\n\nexport function formatVaults(vaults: VaultInfo[], format: OutputFormat): string {\n if (format === \"json\") return JSON.stringify(vaults, null, 2);\n\n if (vaults.length === 0) return \"No vaults found.\";\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`| Path | Name | Current |`);\n lines.push(`| --- | --- | --- |`);\n for (const v of vaults) {\n lines.push(`| \\`${v.path}\\` | ${v.name} | ${v.current ? \"yes\" : \"\"} |`);\n }\n return lines.join(\"\\n\");\n }\n\n if (format === \"table\") {\n const pathW = 60;\n const nameW = 24;\n const header = `${padRight(\"PATH\", pathW)} ${padRight(\"NAME\", nameW)} CURRENT`;\n const sep = \"─\".repeat(header.length);\n const rows = vaults.map((v) => {\n const p = truncate(v.path, pathW);\n const n = truncate(v.name, nameW);\n return `${padRight(p, pathW)} ${padRight(n, nameW)} ${v.current ? \"yes\" : \"\"}`;\n });\n return [sep, header, sep, ...rows, sep].join(\"\\n\");\n }\n\n return vaults\n .map((v) => `${v.path}${v.current ? \" (current)\" : \"\"}`)\n .join(\"\\n\");\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return str.slice(0, max - 1) + \"…\";\n}\n\nfunction formatDate(ts: number): string {\n return new Date(ts).toLocaleString();\n}\n\nfunction relativeTime(ts: number): string {\n const diff = Date.now() - ts;\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo ago`;\n return `${Math.floor(months / 12)}y ago`;\n}\n\nfunction padRight(str: string, len: number): string {\n return str + \" \".repeat(Math.max(0, len - str.length));\n}\n\nfunction padLeft(str: string, len: number): string {\n return \" \".repeat(Math.max(0, len - str.length)) + str;\n}\n\nfunction formatContentType(ct: string | string[] | undefined): string {\n if (!ct) return \"—\";\n if (Array.isArray(ct)) return ct.join(\", \");\n return ct;\n}\n\nfunction isEmbeddableImage(mime: string): boolean {\n return /^image\\/(png|jpe?g|gif|webp|svg\\+xml|bmp|avif)$/i.test(mime);\n}\n\nfunction isEmbeddableVideo(mime: string): boolean {\n return /^video\\/(mp4|webm|mov|quicktime)$/i.test(mime);\n}\n\nfunction resolveAttachmentPath(vaultPath: string, relativePath: string): string {\n return path.resolve(vaultPath, relativePath);\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n}\n\nexport function formatCaptureList(\n captures: CaptureIndex[],\n total: number,\n format: OutputFormat\n): string {\n if (format === \"json\") return JSON.stringify({ captures, total }, null, 2);\n\n if (captures.length === 0) return \"No captures found.\";\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`| ID | Type | Title | Tags | Created |`);\n lines.push(`| --- | --- | --- | --- | --- |`);\n for (const c of captures) {\n const id = `\\`${c.id.slice(0, 8)}\\``;\n const ct = formatContentType(c.contentType);\n const title = c.title ? c.title.replace(/\\|/g, \"\\\\|\") : \"_(untitled)_\";\n const tags = c.tags?.length ? c.tags.map((t) => `\\`${t}\\``).join(\", \") : \"\";\n const date = relativeTime(c.createdAt);\n lines.push(`| ${id} | ${ct} | ${title} | ${tags} | ${date} |`);\n }\n lines.push(\"\");\n lines.push(`_Showing ${captures.length} of ${total} captures_`);\n return lines.join(\"\\n\");\n }\n\n if (format === \"table\") {\n const idW = 10;\n const typeW = 12;\n const titleW = 40;\n const tagsW = 20;\n const dateW = 12;\n\n const header = `${padRight(\"ID\", idW)} ${padRight(\"TYPE\", typeW)} ${padRight(\"TITLE\", titleW)} ${padRight(\"TAGS\", tagsW)} ${padRight(\"CREATED\", dateW)}`;\n const sep = \"─\".repeat(header.length);\n\n const rows = captures.map((c) => {\n const id = truncate(c.id, idW);\n const ct = truncate(formatContentType(c.contentType), typeW);\n const title = truncate(c.title ?? \"(untitled)\", titleW);\n const tags = truncate(c.tags?.join(\", \") ?? \"\", tagsW);\n const date = relativeTime(c.createdAt);\n return `${padRight(id, idW)} ${padRight(ct, typeW)} ${padRight(title, titleW)} ${padRight(tags, tagsW)} ${padRight(date, dateW)}`;\n });\n\n return [`${sep}`, header, sep, ...rows, sep, `Showing ${captures.length} of ${total}`].join(\n \"\\n\"\n );\n }\n\n const lines: string[] = [];\n for (const c of captures) {\n const pin = c.isPinned ? \" 📌\" : \"\";\n const title = c.title ?? \"(untitled)\";\n const ct = formatContentType(c.contentType);\n lines.push(` ${c.id.slice(0, 8)} ${ct.padEnd(10)} ${title}${pin}`);\n if (c.tldr) lines.push(` ${truncate(c.tldr, 70)}`);\n if (c.tags?.length) lines.push(` tags: ${c.tags.join(\", \")}`);\n lines.push(\"\");\n }\n lines.push(`${captures.length} of ${total} captures`);\n return lines.join(\"\\n\");\n}\n\nexport function formatCapture(capture: Capture, format: OutputFormat, vaultPath?: string): string {\n if (format === \"json\") return JSON.stringify(capture, null, 2);\n\n if (format === \"md\") {\n const lines: string[] = [];\n const title = capture.title || \"Untitled Capture\";\n lines.push(`# ${title}`);\n lines.push(\"\");\n\n const content = capture.content;\n if (content?.type === \"attachment\" && content.path) {\n const fullPath = vaultPath\n ? resolveAttachmentPath(vaultPath, String(content.path))\n : String(content.path);\n const mime = String(content.mimeType ?? \"\");\n\n if (isEmbeddableImage(mime)) {\n lines.push(``);\n lines.push(\"\");\n } else if (isEmbeddableVideo(mime)) {\n lines.push(`[Video: ${title}](${fullPath})`);\n lines.push(\"\");\n }\n }\n\n lines.push(`- **ID:** \\`${capture.id}\\``);\n lines.push(`- **Type:** ${capture.type} (${formatContentType(capture.contentType)})`);\n if (capture.source) lines.push(`- **Source:** ${capture.source}`);\n lines.push(`- **Created:** ${formatDate(capture.createdAt)}`);\n lines.push(`- **Updated:** ${formatDate(capture.updatedAt)}`);\n if (capture.isPinned) lines.push(`- **Pinned:** yes`);\n if (capture.tags?.length)\n lines.push(`- **Tags:** ${capture.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n if (capture.spaceKeys?.length)\n lines.push(`- **Spaces:** ${capture.spaceKeys.map((k) => `\\`${k}\\``).join(\", \")}`);\n if (capture.triageStatus) lines.push(`- **Triage:** ${capture.triageStatus}`);\n\n if (capture.tldr) {\n lines.push(\"\");\n lines.push(`## TLDR`);\n lines.push(capture.tldr);\n }\n\n if (capture.note) {\n lines.push(\"\");\n lines.push(`## Note`);\n lines.push(capture.note);\n }\n\n if (content) {\n if (content.type === \"note\" && content.textContent) {\n lines.push(\"\");\n lines.push(`## Content`);\n lines.push(String(content.textContent));\n } else if (content.type === \"link\") {\n lines.push(\"\");\n lines.push(`## Content`);\n if (content.url) lines.push(`**URL:** ${content.url}`);\n if (content.ogTitle) lines.push(`**OG Title:** ${content.ogTitle}`);\n if (content.ogDescription)\n lines.push(`**OG Description:** ${truncate(String(content.ogDescription), 200)}`);\n } else if (content.type === \"attachment\") {\n const fullPath = vaultPath\n ? resolveAttachmentPath(vaultPath, String(content.path))\n : String(content.path ?? \"\");\n lines.push(\"\");\n lines.push(`## Attachment`);\n if (content.mimeType) lines.push(`- **MIME:** ${content.mimeType}`);\n if (content.size) lines.push(`- **Size:** ${formatSize(Number(content.size))}`);\n lines.push(`- **Path:** \\`${fullPath}\\``);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n const lines: string[] = [];\n const sep = \"─\".repeat(60);\n\n lines.push(sep);\n lines.push(` ID: ${capture.id}`);\n lines.push(` Type: ${capture.type} (${formatContentType(capture.contentType)})`);\n if (capture.title) lines.push(` Title: ${capture.title}`);\n if (capture.source) lines.push(` Source: ${capture.source}`);\n lines.push(` Created: ${formatDate(capture.createdAt)}`);\n lines.push(` Updated: ${formatDate(capture.updatedAt)}`);\n if (capture.isPinned) lines.push(` Pinned: yes`);\n if (capture.tags?.length) lines.push(` Tags: ${capture.tags.join(\", \")}`);\n if (capture.spaceKeys?.length) lines.push(` Spaces: ${capture.spaceKeys.join(\", \")}`);\n if (capture.triageStatus) lines.push(` Triage: ${capture.triageStatus}`);\n if (capture.processingStatus) lines.push(` Status: ${capture.processingStatus}`);\n\n if (capture.tldr) {\n lines.push(sep);\n lines.push(\" TLDR:\");\n lines.push(` ${capture.tldr}`);\n }\n\n if (capture.note) {\n lines.push(sep);\n lines.push(\" Note:\");\n lines.push(` ${capture.note}`);\n }\n\n const content = capture.content;\n if (content) {\n lines.push(sep);\n lines.push(\" Content:\");\n if (content.type === \"note\" && content.textContent) {\n const text = String(content.textContent);\n lines.push(` ${truncate(text, 500)}`);\n } else if (content.type === \"link\") {\n if (content.url) lines.push(` URL: ${content.url}`);\n if (content.ogTitle) lines.push(` OG Title: ${content.ogTitle}`);\n if (content.ogDescription)\n lines.push(` OG Desc: ${truncate(String(content.ogDescription), 200)}`);\n } else if (content.type === \"attachment\") {\n if (content.mimeType) lines.push(` MIME: ${content.mimeType}`);\n if (content.size) lines.push(` Size: ${formatSize(Number(content.size))}`);\n if (content.path) {\n const fullPath = vaultPath\n ? resolveAttachmentPath(vaultPath, String(content.path))\n : String(content.path);\n lines.push(` Path: ${fullPath}`);\n }\n }\n }\n\n lines.push(sep);\n return lines.join(\"\\n\");\n}\n\nexport function formatSpaces(spaces: Space[], format: OutputFormat): string {\n if (format === \"json\") return JSON.stringify(spaces, null, 2);\n\n if (spaces.length === 0) return \"No spaces found.\";\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`| Key | Name | Captures | Pinned |`);\n lines.push(`| --- | --- | ---: | --- |`);\n for (const s of spaces) {\n const pin = s.isPinned ? \"yes\" : \"\";\n lines.push(`| \\`${s.key}\\` | ${s.name} | ${s.noteCount ?? 0} | ${pin} |`);\n }\n return lines.join(\"\\n\");\n }\n\n if (format === \"table\") {\n const keyW = 20;\n const nameW = 30;\n const countW = 8;\n const header = `${padRight(\"KEY\", keyW)} ${padRight(\"NAME\", nameW)} ${padLeft(\"COUNT\", countW)}`;\n const sep = \"─\".repeat(header.length);\n\n const rows = spaces.map((s) => {\n return `${padRight(truncate(s.key, keyW), keyW)} ${padRight(truncate(s.name, nameW), nameW)} ${padLeft(String(s.noteCount ?? 0), countW)}`;\n });\n\n return [sep, header, sep, ...rows, sep].join(\"\\n\");\n }\n\n return spaces\n .map((s) => {\n const pin = s.isPinned ? \" 📌\" : \"\";\n return ` ${s.key.padEnd(20)} ${s.name}${pin} (${s.noteCount ?? 0} captures)`;\n })\n .join(\"\\n\");\n}\n\nexport function formatAreas(areas: Area[], format: OutputFormat): string {\n if (format === \"json\") return JSON.stringify(areas, null, 2);\n if (areas.length === 0) return \"No areas found.\";\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`| Key | Name |`);\n lines.push(`| --- | --- |`);\n for (const a of areas) {\n lines.push(`| \\`${a.key}\\` | ${a.name} |`);\n }\n return lines.join(\"\\n\");\n }\n\n return areas.map((a) => ` ${a.key.padEnd(20)} ${a.name}`).join(\"\\n\");\n}\n\nexport function formatTags(tags: string[], format: OutputFormat): string {\n if (format === \"json\") return JSON.stringify(tags, null, 2);\n if (tags.length === 0) return \"No tags found.\";\n\n if (format === \"md\") {\n return tags.map((t) => `- \\`${t}\\``).join(\"\\n\");\n }\n\n return tags.map((t) => ` ${t}`).join(\"\\n\");\n}\n\nexport function formatCanvases(canvases: Canvas[], format: OutputFormat): string {\n if (format === \"json\") return JSON.stringify(canvases, null, 2);\n if (canvases.length === 0) return \"No canvases found.\";\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`| ID | Name | Nodes | Connections |`);\n lines.push(`| --- | --- | ---: | ---: |`);\n for (const c of canvases) {\n lines.push(\n `| \\`${c.id.slice(0, 8)}\\` | ${c.name} | ${c.nodes.length} | ${c.connections.length} |`\n );\n }\n return lines.join(\"\\n\");\n }\n\n return canvases\n .map(\n (c) =>\n ` ${c.id.slice(0, 8)} ${c.name} (${c.nodes.length} nodes, ${c.connections.length} connections)`\n )\n .join(\"\\n\");\n}\n\nexport function formatStats(\n stats: {\n totalCaptures: number;\n totalSpaces: number;\n totalAreas: number;\n totalCanvases: number;\n totalTags: number;\n byContentType: Record<string, number>;\n byTriageStatus: Record<string, number>;\n },\n vaultPath: string,\n format: OutputFormat\n): string {\n if (format === \"json\") return JSON.stringify({ vaultPath, ...stats }, null, 2);\n\n if (format === \"md\") {\n const lines: string[] = [];\n lines.push(`# Vault Stats`);\n lines.push(\"\");\n lines.push(`**Path:** \\`${vaultPath}\\``);\n lines.push(\"\");\n lines.push(`| Metric | Count |`);\n lines.push(`| --- | ---: |`);\n lines.push(`| Captures | ${stats.totalCaptures} |`);\n lines.push(`| Spaces | ${stats.totalSpaces} |`);\n lines.push(`| Areas | ${stats.totalAreas} |`);\n lines.push(`| Canvases | ${stats.totalCanvases} |`);\n lines.push(`| Tags | ${stats.totalTags} |`);\n lines.push(\"\");\n lines.push(`### By Content Type`);\n lines.push(\"\");\n lines.push(`| Type | Count |`);\n lines.push(`| --- | ---: |`);\n const sortedTypes = Object.entries(stats.byContentType).sort((a, b) => b[1] - a[1]);\n for (const [type, count] of sortedTypes) {\n lines.push(`| ${type} | ${count} |`);\n }\n lines.push(\"\");\n lines.push(`### By Triage Status`);\n lines.push(\"\");\n lines.push(`| Status | Count |`);\n lines.push(`| --- | ---: |`);\n const sortedStatus = Object.entries(stats.byTriageStatus).sort((a, b) => b[1] - a[1]);\n for (const [status, count] of sortedStatus) {\n lines.push(`| ${status} | ${count} |`);\n }\n return lines.join(\"\\n\");\n }\n\n const sep = \"─\".repeat(40);\n const lines: string[] = [];\n\n lines.push(sep);\n lines.push(` Vault: ${vaultPath}`);\n lines.push(sep);\n lines.push(` Captures: ${stats.totalCaptures}`);\n lines.push(` Spaces: ${stats.totalSpaces}`);\n lines.push(` Areas: ${stats.totalAreas}`);\n lines.push(` Canvases: ${stats.totalCanvases}`);\n lines.push(` Tags: ${stats.totalTags}`);\n lines.push(sep);\n\n lines.push(\" By Content Type:\");\n const sortedTypes = Object.entries(stats.byContentType).sort((a, b) => b[1] - a[1]);\n for (const [type, count] of sortedTypes) {\n lines.push(` ${type.padEnd(14)} ${count}`);\n }\n\n lines.push(sep);\n lines.push(\" By Triage Status:\");\n const sortedStatus = Object.entries(stats.byTriageStatus).sort((a, b) => b[1] - a[1]);\n for (const [status, count] of sortedStatus) {\n lines.push(` ${status.padEnd(14)} ${count}`);\n }\n\n lines.push(sep);\n return lines.join(\"\\n\");\n}\n","import { VaultReader } from \"./reader.js\";\nimport { discoverVaultPath, findVaults } from \"./vault.js\";\nimport {\n type OutputFormat,\n formatAreas,\n formatCanvases,\n formatCapture,\n formatCaptureList,\n formatSpaces,\n formatStats,\n formatTags,\n formatVaults,\n} from \"./format.js\";\n\nconst VERSION = \"1.0.0\";\n\ntype GlobalFlags = {\n vault?: string;\n format: OutputFormat;\n verbose?: boolean;\n};\n\nfunction parseGlobalFlags(args: string[]): { flags: GlobalFlags; rest: string[] } {\n const flags: GlobalFlags = { format: \"pretty\" };\n const rest: string[] = [];\n let i = 0;\n\n while (i < args.length) {\n const arg = args[i];\n if (arg === \"--vault\" && i + 1 < args.length) {\n flags.vault = args[i + 1];\n i += 2;\n } else if (arg === \"--json\") {\n flags.format = \"json\";\n i++;\n } else if (arg === \"--md\" || arg === \"--markdown\") {\n flags.format = \"md\";\n i++;\n } else if (arg === \"--table\") {\n flags.format = \"table\";\n i++;\n } else if (arg === \"--format\" && i + 1 < args.length) {\n flags.format = args[i + 1] as OutputFormat;\n i += 2;\n } else if (arg === \"--verbose\") {\n flags.verbose = true;\n i++;\n } else {\n rest.push(arg);\n i++;\n }\n }\n\n return { flags, rest };\n}\n\nfunction extractFlag(args: string[], flag: string): string | undefined {\n const idx = args.indexOf(flag);\n if (idx === -1 || idx + 1 >= args.length) return undefined;\n return args[idx + 1];\n}\n\nfunction hasFlag(args: string[], flag: string): boolean {\n return args.includes(flag);\n}\n\nfunction extractNumber(args: string[], flag: string, defaultVal: number): number {\n const val = extractFlag(args, flag);\n if (!val) return defaultVal;\n const n = parseInt(val, 10);\n return isNaN(n) ? defaultVal : n;\n}\n\nasync function cmdList(reader: VaultReader, args: string[], format: OutputFormat): Promise<void> {\n const limit = extractNumber(args, \"--limit\", 20);\n const offset = extractNumber(args, \"--offset\", 0);\n const spaceKey = extractFlag(args, \"--space\");\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n const search = extractFlag(args, \"--search\") ?? extractFlag(args, \"-s\");\n const pinned = hasFlag(args, \"--pinned\") ? true : undefined;\n const sortBy = (extractFlag(args, \"--sort\") as \"created\" | \"updated\") ?? \"created\";\n const sortOrder = hasFlag(args, \"--asc\") ? (\"asc\" as const) : (\"desc\" as const);\n\n const result = await reader.listCaptures({\n limit,\n offset,\n spaceKey,\n tag,\n contentType,\n search,\n pinned,\n sortBy,\n sortOrder,\n });\n\n console.log(formatCaptureList(result.captures, result.total, format));\n}\n\nasync function cmdGet(reader: VaultReader, args: string[], format: OutputFormat): Promise<void> {\n const id = args[0];\n if (!id) {\n console.error(\"Usage: resurf get <capture-id>\");\n process.exit(1);\n }\n\n let capture = await reader.getCaptureById(id);\n\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) {\n capture = await reader.getCaptureById(match.id);\n }\n }\n\n if (!capture) {\n console.error(`Capture not found: ${id}`);\n process.exit(1);\n }\n\n console.log(formatCapture(capture, format, reader.vaultPath));\n}\n\nasync function cmdSearch(reader: VaultReader, args: string[], format: OutputFormat): Promise<void> {\n const positional: string[] = [];\n let i = 0;\n while (i < args.length) {\n if (args[i].startsWith(\"--\") && i + 1 < args.length) {\n i += 2;\n } else if (args[i].startsWith(\"--\")) {\n i++;\n } else {\n positional.push(args[i]);\n i++;\n }\n }\n\n const query = positional.join(\" \");\n if (!query) {\n console.error(\"Usage: resurf search <query>\");\n process.exit(1);\n }\n\n const limit = extractNumber(args, \"--limit\", 20);\n\n const result = await reader.listCaptures({ search: query, limit });\n console.log(formatCaptureList(result.captures, result.total, format));\n}\n\nasync function cmdSpaces(\n reader: VaultReader,\n _args: string[],\n format: OutputFormat\n): Promise<void> {\n const spaces = await reader.getSpaces();\n console.log(formatSpaces(spaces, format));\n}\n\nasync function cmdAreas(reader: VaultReader, _args: string[], format: OutputFormat): Promise<void> {\n const areas = await reader.getAreas();\n console.log(formatAreas(areas, format));\n}\n\nasync function cmdTags(reader: VaultReader, _args: string[], format: OutputFormat): Promise<void> {\n const tags = await reader.getTags();\n console.log(formatTags(tags, format));\n}\n\nasync function cmdCanvases(\n reader: VaultReader,\n _args: string[],\n format: OutputFormat\n): Promise<void> {\n const canvases = await reader.getCanvases();\n console.log(formatCanvases(canvases, format));\n}\n\nasync function cmdStats(reader: VaultReader, _args: string[], format: OutputFormat): Promise<void> {\n const stats = await reader.getStats();\n console.log(formatStats(stats, reader.vaultPath, format));\n}\n\nasync function cmdOpen(reader: VaultReader, args: string[]): Promise<void> {\n const id = args[0];\n if (!id) {\n console.error(\"Usage: resurf open <capture-id>\");\n process.exit(1);\n }\n\n let captureId = id;\n const capture = await reader.getCaptureById(id);\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) captureId = match.id;\n }\n\n const { exec } = await import(\"child_process\");\n const url = `resurf://capture/${captureId}`;\n exec(`open \"${url}\"`);\n console.log(`Opening capture in Resurf: ${captureId}`);\n}\n\nasync function cmdCreate(args: string[]): Promise<void> {\n const content = extractFlag(args, \"--content\") ?? extractFlag(args, \"--text\");\n const url = extractFlag(args, \"--url\");\n const title = extractFlag(args, \"--title\");\n const tags = extractFlag(args, \"--tags\");\n const source = extractFlag(args, \"--source\");\n const space = extractFlag(args, \"--space\");\n\n if (!content && !url) {\n console.error(\n \"Usage: resurf create --content <text> [--url <url>] [--title <title>] [--tags <tags>]\"\n );\n process.exit(1);\n }\n\n const params = new URLSearchParams();\n if (content) params.set(\"content\", content);\n if (url) params.set(\"url\", url);\n if (title) params.set(\"title\", title);\n if (tags) params.set(\"tags\", tags);\n if (source) params.set(\"source\", source);\n if (space) params.set(\"space\", space);\n\n const protocolUrl = `resurf://new?${params.toString()}`;\n const { exec } = await import(\"child_process\");\n exec(`open \"${protocolUrl}\"`);\n console.log(\"Creating capture via Resurf protocol...\");\n}\n\nasync function cmdExport(reader: VaultReader, args: string[], format: OutputFormat): Promise<void> {\n const spaceKey = extractFlag(args, \"--space\");\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n\n const result = await reader.listCaptures({\n limit: 999999,\n spaceKey,\n tag,\n contentType,\n });\n\n const captures: Record<string, unknown>[] = [];\n for (const item of result.captures) {\n const full = await reader.getCaptureById(item.id);\n if (full) captures.push(full);\n }\n\n if (format === \"json\") {\n console.log(JSON.stringify(captures, null, 2));\n } else if (format === \"md\") {\n const lines: string[] = [];\n for (const c of captures) {\n const cap = c as Record<string, unknown>;\n const title = (cap.title as string) || \"Untitled\";\n const id = cap.id as string;\n lines.push(`## ${title}`);\n lines.push(`- **ID:** \\`${id}\\``);\n if (cap.contentType) lines.push(`- **Type:** ${cap.contentType}`);\n if (cap.tldr) lines.push(`- **TLDR:** ${cap.tldr}`);\n if (cap.note) lines.push(`- **Note:** ${cap.note}`);\n lines.push(\"\");\n }\n lines.push(`_Exported ${captures.length} captures_`);\n console.log(lines.join(\"\\n\"));\n } else {\n console.log(JSON.stringify(captures, null, 2));\n console.error(`\\nExported ${captures.length} captures (use --json or --md for piping)`);\n }\n}\n\nfunction printHelp(): void {\n const help = `\nresurf - Access Resurf vault data from the command line\n\nUSAGE\n resurf <command> [options]\n\nCOMMANDS\n list List captures (default)\n get <id> Get a capture by ID (supports partial IDs)\n search <query> Search captures by title, note, or TLDR\n spaces List all spaces\n areas List all areas\n tags List all tags\n canvases List all canvases\n stats Show vault statistics\n vaults List available Resurf vaults on this computer\n open <id> Open a capture in Resurf app\n create Create a capture via Resurf protocol\n export Export captures as JSON\n\nGLOBAL OPTIONS\n --vault <path> Path to Resurf vault (auto-detected if omitted)\n --json Output as JSON\n --md, --markdown Output as Markdown\n --table Output as table\n --format <fmt> Output format: json, md, table, pretty (default: pretty)\n\nLIST OPTIONS\n --limit <n> Max results (default: 20)\n --offset <n> Skip first N results\n --space <key> Filter by space key\n --tag <tag> Filter by tag\n --type <type> Filter by content type (note, link, image, video, etc.)\n --search <query> Filter by text search\n --pinned Show only pinned captures\n --sort <field> Sort by: created, updated (default: created)\n --asc Sort ascending (default: descending)\n\nSEARCH OPTIONS\n --limit <n> Max results (default: 20)\n\nCREATE OPTIONS\n --content <text> Text content for the capture\n --url <url> URL to capture\n --title <title> Title for the capture\n --tags <tags> Comma-separated tags\n --source <source> Source identifier\n --space <key> Space key to assign\n\nEXPORT OPTIONS\n --space <key> Filter by space key\n --tag <tag> Filter by tag\n --type <type> Filter by content type\n\nVAULTS OPTIONS\n [directory] Scan this directory recursively for vaults\n --verbose Log skipped inaccessible paths\n\nEXAMPLES\n resurf list --limit 10 --json\n resurf list --space work --type note\n resurf get abc123\n resurf search \"meeting notes\"\n resurf search \"react hooks\" --json | jq '.[].title'\n resurf spaces --json\n resurf tags\n resurf stats\n resurf vaults\n resurf vaults --json\n resurf vaults /path/to/folder\n resurf open abc123\n resurf create --content \"Quick note\" --tags \"idea,work\"\n resurf create --url \"https://example.com\" --title \"Example\"\n resurf export --space work --json > work-captures.json\n resurf list --json | jq '.captures[].id'\n\nVERSION\n ${VERSION}\n`;\n console.log(help.trim());\n}\n\nasync function main(): Promise<void> {\n const rawArgs = process.argv.slice(2);\n\n if (rawArgs.length === 0 || rawArgs[0] === \"--help\" || rawArgs[0] === \"-h\") {\n printHelp();\n return;\n }\n\n if (rawArgs[0] === \"--version\" || rawArgs[0] === \"-v\") {\n console.log(VERSION);\n return;\n }\n\n const { flags, rest } = parseGlobalFlags(rawArgs);\n const command = rest[0] ?? \"list\";\n const commandArgs = rest.slice(1);\n\n if (command === \"create\") {\n await cmdCreate(commandArgs);\n return;\n }\n\n if (command === \"vaults\" || command === \"find-vaults\") {\n const scanDir = commandArgs.find((a) => !a.startsWith(\"-\"));\n const vaults = await findVaults({ scanDir, verbose: flags.verbose });\n console.log(formatVaults(vaults, flags.format));\n return;\n }\n\n const vaultPath = await discoverVaultPath(flags.vault);\n const reader = new VaultReader(vaultPath);\n\n const commands: Record<string, (r: VaultReader, a: string[], f: OutputFormat) => Promise<void>> =\n {\n list: cmdList,\n ls: cmdList,\n get: cmdGet,\n show: cmdGet,\n search: cmdSearch,\n find: cmdSearch,\n spaces: cmdSpaces,\n areas: cmdAreas,\n tags: cmdTags,\n canvases: cmdCanvases,\n stats: cmdStats,\n info: cmdStats,\n export: cmdExport,\n };\n\n if (command === \"open\") {\n await cmdOpen(reader, commandArgs);\n return;\n }\n\n const handler = commands[command];\n if (!handler) {\n console.error(`Unknown command: ${command}`);\n console.error(`Run 'resurf --help' for usage.`);\n process.exit(1);\n }\n\n await handler(reader, commandArgs, flags.format);\n}\n\nmain().catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n"],"mappings":"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","names":["fs","path","stat","stat","path","lines","content","sortedTypes","sortedStatus"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "resurf",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI to access Resurf vault data from any app or terminal",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"resurf": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": ["dist"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"dev": "tsup --watch",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["resurf", "cli", "captures", "knowledge-base", "notes", "vault"],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"typescript": "^5.8.0"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/captureai/captureai.git",
|
|
29
|
+
"directory": "packages/resurf-cli"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/captureai/captureai#readme",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/captureai/captureai/issues"
|
|
34
|
+
}
|
|
35
|
+
}
|