mirror of
https://github.com/Xevion/dotfiles.git
synced 2026-01-31 00:24:06 -06:00
docs: enhance Question tool guidance and improve share script error handling
Add prominent Question tool guidance to AGENTS.md and expand coverage in CLAUDE.md with chezmoi-specific examples. Refactor share.ts error handling to show last 10 lines of stderr instead of full output, remove hardcoded env var docs now that credentials are templated.
This commit is contained in:
@@ -1,3 +1,23 @@
|
|||||||
|
## ⚠️ CRITICAL: Use the Question Tool for Planning and Decisions
|
||||||
|
|
||||||
|
**STRONGLY prefer using mcp_question when:**
|
||||||
|
- Planning how to approach a task with multiple valid options
|
||||||
|
- Facing design decisions or architectural choices
|
||||||
|
- Uncertain about user intent or requirements
|
||||||
|
- About to make assumptions that could lead to rework
|
||||||
|
- Working with ambiguous requests
|
||||||
|
|
||||||
|
**During planning phases, ASK before acting:**
|
||||||
|
- "I see 3 approaches to this. Which do you prefer: A, B, or C?"
|
||||||
|
- "Should I prioritize X or Y for this implementation?"
|
||||||
|
- "This could mean either A or B. Which did you intend?"
|
||||||
|
|
||||||
|
**It's better to ask and get it right than to guess and need to redo work.**
|
||||||
|
|
||||||
|
See detailed Question Tool guidance in CLAUDE.md below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚠️ CRITICAL: File Access in Chezmoi Repository
|
## ⚠️ CRITICAL: File Access in Chezmoi Repository
|
||||||
|
|
||||||
**When working in `/home/xevion/.local/share/chezmoi`, ONLY access files within this directory.**
|
**When working in `/home/xevion/.local/share/chezmoi`, ONLY access files within this directory.**
|
||||||
|
|||||||
@@ -322,6 +322,50 @@ These are very different operations - which did you mean?"
|
|||||||
4. **Prefer explanations** over automated actions
|
4. **Prefer explanations** over automated actions
|
||||||
5. **Present options** when requests are ambiguous (direction, location, scope)
|
5. **Present options** when requests are ambiguous (direction, location, scope)
|
||||||
6. **Show consequences** before destructive operations
|
6. **Show consequences** before destructive operations
|
||||||
|
7. **USE THE QUESTION TOOL** - When planning or facing design decisions, use mcp_question to present options and get user input proactively
|
||||||
|
|
||||||
|
## Question Tool Usage in Chezmoi Context
|
||||||
|
|
||||||
|
**STRONGLY prefer using the Question tool when working in this repository, especially for:**
|
||||||
|
|
||||||
|
**Planning & Ambiguity Resolution:**
|
||||||
|
- **Direction questions** - Source→Target vs Target→Source operations
|
||||||
|
- "Should I apply changes from source to target, or import from target to source?"
|
||||||
|
- **File location questions** - Which CLAUDE.md/AGENTS.md file to edit
|
||||||
|
- "Which file should I edit: project-level CLAUDE.md or user-level home/dot_claude/CLAUDE.md.tmpl?"
|
||||||
|
- **Platform targeting** - Which OS/environment to optimize for
|
||||||
|
- "Should this template prioritize Windows, Linux, or work equally on both?"
|
||||||
|
- **Template complexity** - How sophisticated the conditional logic should be
|
||||||
|
- "Should I add platform detection here, or keep it simple?"
|
||||||
|
|
||||||
|
**Before Making Changes:**
|
||||||
|
- **Encryption decisions** - Whether files should be encrypted
|
||||||
|
- "This file contains [type of data]. Should I encrypt it with age or leave it plain?"
|
||||||
|
- **Permission choices** - Using `private_` prefix or standard permissions
|
||||||
|
- "Should this config file use private_ prefix (600 permissions)?"
|
||||||
|
- **Template vs static** - Whether to use `.tmpl` suffix
|
||||||
|
- "Should this be a template file, or a static file?"
|
||||||
|
|
||||||
|
**Examples of good question tool usage:**
|
||||||
|
```
|
||||||
|
User: "Add my GitHub token to the config"
|
||||||
|
AI: *Uses Question tool* "How should I handle this token?
|
||||||
|
A. Store encrypted with age (encrypted_*.age)
|
||||||
|
B. Reference from Doppler ({{ dopplerProjectJson.GITHUB_TOKEN }})
|
||||||
|
C. Add to .chezmoiignore and manage manually"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
User: "Update the shell config"
|
||||||
|
AI: *Uses Question tool* "Which file should I edit?
|
||||||
|
A. home/dot_bashrc.tmpl (bash configuration)
|
||||||
|
B. home/dot_config/nushell/config.nu.tmpl (nushell configuration)
|
||||||
|
C. home/dot_config/fish/config.fish (fish configuration)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**When NOT to use:** Trivial clarifications that don't affect functionality or security.
|
||||||
|
|
||||||
|
**Remember:** In a templating/dotfiles context, wrong assumptions lead to broken deployments. Always confirm when uncertain.
|
||||||
|
|
||||||
# Extended Documentation
|
# Extended Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -26,3 +26,4 @@ delta = "latest"
|
|||||||
lazydocker = "latest"
|
lazydocker = "latest"
|
||||||
lazyssh = "latest"
|
lazyssh = "latest"
|
||||||
lazyjournal = "latest"
|
lazyjournal = "latest"
|
||||||
|
gradle-profiler = "latest"
|
||||||
|
|||||||
@@ -241,7 +241,8 @@
|
|||||||
"rust-analyzer-lsp@claude-plugins-official": true,
|
"rust-analyzer-lsp@claude-plugins-official": true,
|
||||||
"ralph-wiggum@claude-plugins-official": true,
|
"ralph-wiggum@claude-plugins-official": true,
|
||||||
"superpowers@superpowers-marketplace": true,
|
"superpowers@superpowers-marketplace": true,
|
||||||
"gopls-lsp@claude-plugins-official": true
|
"gopls-lsp@claude-plugins-official": true,
|
||||||
|
"code-review@claude-code-plugins": true
|
||||||
},
|
},
|
||||||
"alwaysThinkingEnabled": true
|
"alwaysThinkingEnabled": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@
|
|||||||
* share file.png # Upload specific file
|
* share file.png # Upload specific file
|
||||||
* cat file.txt | share # Upload from stdin
|
* cat file.txt | share # Upload from stdin
|
||||||
* share -c video.mov # Convert then upload
|
* share -c video.mov # Convert then upload
|
||||||
*
|
|
||||||
* Environment Variables:
|
|
||||||
* R2_ENDPOINT S3-compatible endpoint URL
|
|
||||||
* R2_ACCESS_KEY_ID Access key ID
|
|
||||||
* R2_SECRET_ACCESS_KEY Secret access key
|
|
||||||
* R2_BUCKET Bucket name
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { existsSync, fstatSync } from "fs";
|
import { existsSync, fstatSync } from "fs";
|
||||||
@@ -65,12 +59,6 @@ type IntervalHandle = ReturnType<typeof setInterval>;
|
|||||||
|
|
||||||
const DOMAIN = "https://i.xevion.dev";
|
const DOMAIN = "https://i.xevion.dev";
|
||||||
|
|
||||||
const REQUIRED_ENV = [
|
|
||||||
"R2_ENDPOINT",
|
|
||||||
"R2_ACCESS_KEY_ID",
|
|
||||||
"R2_SECRET_ACCESS_KEY",
|
|
||||||
"R2_BUCKET",
|
|
||||||
];
|
|
||||||
const ENV = {
|
const ENV = {
|
||||||
endpoint: "{{ dopplerProjectJson.R2_ENDPOINT }}",
|
endpoint: "{{ dopplerProjectJson.R2_ENDPOINT }}",
|
||||||
accessKeyId: "{{ dopplerProjectJson.R2_ACCESS_KEY_ID }}",
|
accessKeyId: "{{ dopplerProjectJson.R2_ACCESS_KEY_ID }}",
|
||||||
@@ -591,6 +579,33 @@ async function hasCommand(cmd: string): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runMediaCommand(cmd: string[]): Promise<void> {
|
||||||
|
try {
|
||||||
|
const proc = Bun.spawn(cmd, {
|
||||||
|
stdout: "ignore",
|
||||||
|
stderr: "pipe",
|
||||||
|
});
|
||||||
|
|
||||||
|
const exitCode = await proc.exited;
|
||||||
|
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
const stderr = await new Response(proc.stderr).text();
|
||||||
|
const errorLines = stderr
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => line.trim())
|
||||||
|
.slice(-10) // Last 10 non-empty lines
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
throw new Error(`Command failed with exit code ${exitCode}\n${errorLines}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
throw new Error(`Command failed: ${String(e)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function writeTempFile(buffer: Buffer, ext = ""): Promise<string> {
|
async function writeTempFile(buffer: Buffer, ext = ""): Promise<string> {
|
||||||
const path = join(tmpdir(), `share-probe-${nanoid(8)}${ext}`);
|
const path = join(tmpdir(), `share-probe-${nanoid(8)}${ext}`);
|
||||||
await Bun.write(path, buffer);
|
await Bun.write(path, buffer);
|
||||||
@@ -824,8 +839,11 @@ async function remuxVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
const outputPath = join(tmpdir(), `share-remux-${nanoid(8)}.webm`);
|
const outputPath = join(tmpdir(), `share-remux-${nanoid(8)}.webm`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Remux without re-encoding, adding faststart for MP4
|
await runMediaCommand([
|
||||||
await $`ffmpeg -y -i ${inputPath} -c copy -fflags +genpts ${outputPath}`.quiet();
|
"ffmpeg", "-y", "-i", inputPath,
|
||||||
|
"-c", "copy", "-fflags", "+genpts",
|
||||||
|
outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -833,9 +851,9 @@ async function remuxVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nRemux error:"));
|
||||||
`Video remux failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Video remux failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -849,7 +867,11 @@ async function addFaststart(buffer: Buffer): Promise<Buffer> {
|
|||||||
const outputPath = join(tmpdir(), `share-faststart-${nanoid(8)}.mp4`);
|
const outputPath = join(tmpdir(), `share-faststart-${nanoid(8)}.mp4`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $`ffmpeg -y -i ${inputPath} -c copy -movflags +faststart ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"ffmpeg", "-y", "-i", inputPath,
|
||||||
|
"-c", "copy", "-movflags", "+faststart",
|
||||||
|
outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -857,9 +879,9 @@ async function addFaststart(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nFaststart optimization error:"));
|
||||||
`Faststart optimization failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Faststart optimization failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -873,7 +895,13 @@ async function reencodeVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
const outputPath = join(tmpdir(), `share-reencode-${nanoid(8)}.mp4`);
|
const outputPath = join(tmpdir(), `share-reencode-${nanoid(8)}.mp4`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $`ffmpeg -y -i ${inputPath} -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"ffmpeg", "-y", "-i", inputPath,
|
||||||
|
"-c:v", "libx264", "-crf", "23", "-preset", "medium",
|
||||||
|
"-c:a", "aac", "-b:a", "128k",
|
||||||
|
"-movflags", "+faststart",
|
||||||
|
outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -881,9 +909,9 @@ async function reencodeVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nRe-encode error:"));
|
||||||
`Video re-encode failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Video re-encode failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -894,11 +922,14 @@ async function remuxAudio(buffer: Buffer): Promise<Buffer> {
|
|||||||
spinner.start("Fixing audio metadata...");
|
spinner.start("Fixing audio metadata...");
|
||||||
|
|
||||||
const inputPath = await writeTempFile(buffer);
|
const inputPath = await writeTempFile(buffer);
|
||||||
// Detect format and use same extension
|
|
||||||
const outputPath = join(tmpdir(), `share-audio-${nanoid(8)}.mka`);
|
const outputPath = join(tmpdir(), `share-audio-${nanoid(8)}.mka`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $`ffmpeg -y -i ${inputPath} -c copy ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"ffmpeg", "-y", "-i", inputPath,
|
||||||
|
"-c", "copy",
|
||||||
|
outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -906,9 +937,9 @@ async function remuxAudio(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nAudio remux error:"));
|
||||||
`Audio remux failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Audio remux failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -922,7 +953,9 @@ async function convertImageToJpeg(buffer: Buffer): Promise<Buffer> {
|
|||||||
const outputPath = join(tmpdir(), `share-convert-${nanoid(8)}.jpg`);
|
const outputPath = join(tmpdir(), `share-convert-${nanoid(8)}.jpg`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $`convert ${inputPath} -quality 90 ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"convert", inputPath, "-quality", "90", outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -930,9 +963,9 @@ async function convertImageToJpeg(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nJPEG conversion error:"));
|
||||||
`JPEG conversion failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("JPEG conversion failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -946,7 +979,9 @@ async function convertImageToWebp(buffer: Buffer): Promise<Buffer> {
|
|||||||
const outputPath = join(tmpdir(), `share-convert-${nanoid(8)}.webp`);
|
const outputPath = join(tmpdir(), `share-convert-${nanoid(8)}.webp`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $`convert ${inputPath} -quality 85 ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"convert", inputPath, "-quality", "85", outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const result = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -954,9 +989,9 @@ async function convertImageToWebp(buffer: Buffer): Promise<Buffer> {
|
|||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nWebP conversion error:"));
|
||||||
`WebP conversion failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("WebP conversion failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -986,9 +1021,9 @@ async function convertImage(
|
|||||||
await Bun.write(inputPath, buffer);
|
await Bun.write(inputPath, buffer);
|
||||||
|
|
||||||
if (fromMime === "image/heic" || fromMime === "image/heif") {
|
if (fromMime === "image/heic" || fromMime === "image/heif") {
|
||||||
await $`convert ${inputPath} -quality 90 ${outputPath}`.quiet();
|
await runMediaCommand(["convert", inputPath, "-quality", "90", outputPath]);
|
||||||
} else {
|
} else {
|
||||||
await $`convert ${inputPath} ${outputPath}`.quiet();
|
await runMediaCommand(["convert", inputPath, outputPath]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const converted = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const converted = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
@@ -999,9 +1034,9 @@ async function convertImage(
|
|||||||
return converted;
|
return converted;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nImage conversion error:"));
|
||||||
`Image conversion failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Image conversion failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -1017,7 +1052,14 @@ async function convertVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
try {
|
try {
|
||||||
await Bun.write(inputPath, buffer);
|
await Bun.write(inputPath, buffer);
|
||||||
|
|
||||||
await $`ffmpeg -i ${inputPath} -c:v libx264 -crf 28 -preset slow -vf "scale=-2:min(720,ih)" -c:a aac -b:a 128k -movflags +faststart ${outputPath}`.quiet();
|
await runMediaCommand([
|
||||||
|
"ffmpeg", "-y", "-i", inputPath,
|
||||||
|
"-c:v", "libx264", "-crf", "28", "-preset", "slow",
|
||||||
|
"-vf", "scale=-2:'min(720,ih)'",
|
||||||
|
"-c:a", "aac", "-b:a", "128k",
|
||||||
|
"-movflags", "+faststart",
|
||||||
|
outputPath
|
||||||
|
]);
|
||||||
|
|
||||||
const converted = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
const converted = Buffer.from(await Bun.file(outputPath).arrayBuffer());
|
||||||
|
|
||||||
@@ -1027,9 +1069,9 @@ async function convertVideo(buffer: Buffer): Promise<Buffer> {
|
|||||||
return converted;
|
return converted;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
throw new Error(
|
console.error(chalk.hex(COLORS.error)("\nConversion error:"));
|
||||||
`Video conversion failed: ${e instanceof Error ? e.message : String(e)}`,
|
console.error(chalk.hex(COLORS.dim)(e instanceof Error ? e.message : String(e)));
|
||||||
);
|
throw new Error("Video conversion failed");
|
||||||
} finally {
|
} finally {
|
||||||
await cleanupTempFiles(inputPath, outputPath);
|
await cleanupTempFiles(inputPath, outputPath);
|
||||||
}
|
}
|
||||||
@@ -1193,20 +1235,18 @@ ${chalk.hex(COLORS.label)("Examples:")}
|
|||||||
share -c large-video.mov # Convert/re-encode then upload
|
share -c large-video.mov # Convert/re-encode then upload
|
||||||
share -Fa video.webm # Fix all issues automatically
|
share -Fa video.webm # Fix all issues automatically
|
||||||
|
|
||||||
${chalk.hex(COLORS.label)("Environment Variables:")}
|
${chalk.hex(COLORS.label)("Note:")}
|
||||||
R2_ENDPOINT S3-compatible endpoint URL
|
R2 credentials are embedded via chezmoi template from Doppler.
|
||||||
R2_ACCESS_KEY_ID Access key ID
|
Use 'chezmoi apply' to update the deployed script with new credentials.
|
||||||
R2_SECRET_ACCESS_KEY Secret access key
|
|
||||||
R2_BUCKET Bucket name
|
|
||||||
`);
|
`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VERBOSE = values.verbose || false;
|
VERBOSE = values.verbose || false;
|
||||||
|
|
||||||
const missing = REQUIRED_ENV.filter((key) => !process.env[key]);
|
// Credentials are embedded via chezmoi template - check ENV object instead
|
||||||
if (missing.length > 0) {
|
if (!ENV.endpoint || !ENV.accessKeyId || !ENV.secretAccessKey || !ENV.bucket) {
|
||||||
log(`Missing environment variables: ${missing.join(", ")}`, "error");
|
log("Missing R2 credentials - chezmoi template may not have been processed correctly", "error");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user