109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
|
|
/**
|
||
|
|
* Alice — the file CLIENT. Demonstrates the typed `@shade/files` API:
|
||
|
|
*
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts list /
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts mkdir /docs
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts upload ./README.md /docs/readme.md
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts download /docs/readme.md ./out.md
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts stat /docs/readme.md
|
||
|
|
* bun run examples/08-files-browser/alice-cli.ts delete /docs/readme.md
|
||
|
|
*/
|
||
|
|
import { readFile, writeFile, stat as fsstat } from 'node:fs/promises';
|
||
|
|
import { createShade } from '@shade/sdk';
|
||
|
|
|
||
|
|
const PREKEY = 'http://localhost:9992';
|
||
|
|
const ALICE_BASE_URL = 'http://localhost:9993';
|
||
|
|
const BOB_BASE_URL = 'http://localhost:9994';
|
||
|
|
|
||
|
|
const alice = await createShade({ prekeyServer: PREKEY, address: 'alice' });
|
||
|
|
alice.configureTransfers({
|
||
|
|
resolveBaseUrl: async (peer) => {
|
||
|
|
if (peer === 'bob') return BOB_BASE_URL;
|
||
|
|
throw new Error(`alice: unknown peer ${peer}`);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
const app = await alice.transferRoute();
|
||
|
|
Bun.serve({ port: 9993, fetch: app.fetch });
|
||
|
|
|
||
|
|
const fs = await alice.files.client('bob', { defaultTimeoutMs: 30_000 });
|
||
|
|
|
||
|
|
const [cmd, ...args] = process.argv.slice(2);
|
||
|
|
try {
|
||
|
|
switch (cmd) {
|
||
|
|
case 'list': {
|
||
|
|
const path = args[0] ?? '/';
|
||
|
|
const result = await fs.list(path);
|
||
|
|
for (const e of result.entries) {
|
||
|
|
console.log(`${e.kind === 'dir' ? 'd' : '-'} ${String(e.size).padStart(10)} ${e.name}`);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 'stat': {
|
||
|
|
const path = args[0]!;
|
||
|
|
const e = await fs.stat(path);
|
||
|
|
console.log(JSON.stringify(e, null, 2));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 'mkdir': {
|
||
|
|
const path = args[0]!;
|
||
|
|
const r = await fs.mkdir(path, { recursive: true });
|
||
|
|
console.log(`created ${r.entry.name}`);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 'delete': {
|
||
|
|
const path = args[0]!;
|
||
|
|
const r = await fs.delete(path, { recursive: true });
|
||
|
|
console.log(`deleted ${r.deletedCount} entrie(s)`);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 'upload': {
|
||
|
|
const localPath = args[0]!;
|
||
|
|
const remotePath = args[1]!;
|
||
|
|
const bytes = new Uint8Array(await readFile(localPath));
|
||
|
|
const s = await fsstat(localPath);
|
||
|
|
console.log(`uploading ${s.size} bytes → ${remotePath}`);
|
||
|
|
const r = await fs.write(remotePath, bytes, { overwrite: true });
|
||
|
|
console.log(`done — remote size: ${r.entry.size}`);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 'download': {
|
||
|
|
const remotePath = args[0]!;
|
||
|
|
const localPath = args[1]!;
|
||
|
|
const result = await fs.read(remotePath);
|
||
|
|
if (result.kind === 'inline') {
|
||
|
|
await writeFile(localPath, result.bytes);
|
||
|
|
console.log(`downloaded ${result.bytes.byteLength} bytes (inline) sha256=${result.sha256}`);
|
||
|
|
} else {
|
||
|
|
const reader = result.stream.getReader();
|
||
|
|
const chunks: Uint8Array[] = [];
|
||
|
|
let total = 0;
|
||
|
|
while (true) {
|
||
|
|
const { value, done } = await reader.read();
|
||
|
|
if (done) break;
|
||
|
|
if (value !== undefined) {
|
||
|
|
chunks.push(value);
|
||
|
|
total += value.byteLength;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
reader.releaseLock();
|
||
|
|
const out = new Uint8Array(total);
|
||
|
|
let offset = 0;
|
||
|
|
for (const c of chunks) {
|
||
|
|
out.set(c, offset);
|
||
|
|
offset += c.byteLength;
|
||
|
|
}
|
||
|
|
await writeFile(localPath, out);
|
||
|
|
await result.done();
|
||
|
|
console.log(`downloaded ${total} bytes (streamed) sha256=${result.sha256}`);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
console.error('Usage: alice-cli.ts <list|stat|mkdir|delete|upload|download> [args...]');
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
await alice.shutdown();
|
||
|
|
process.exit(0);
|
||
|
|
}
|