UNPKG

pomodoro-beep

Version:

Node.js tool to provide pomodoro-like timer with beep sound playback that helps controlling AI coding agent reporting frequency.

2 lines 3.36 kB
#!/usr/bin/env node import e from"yargs";import{hideBin as t}from"yargs/helpers";import{exec as r}from"child_process";import o from"path";import{promisify as a}from"util";import i from"play-sound";import{readFileSync as s}from"fs";import{fileURLToPath as n}from"url";class p{execPromise=a(r);async play(e){if(!e||""===e.trim())throw new Error("ファイルパスは必須です。");e=e.replace("^",""),e=o.resolve(e).replaceAll("\\","/");try{const{stdout:t,stderr:r}=await this.execPromise(`powershell.exe -Command "& { ${this.getPowerShellCommand()} }" -AudioFilePath '${e}'`);if(r)throw new Error(`PowerShellエラー: ${r}`);return t}catch(e){throw new Error(`音声ファイルの再生に失敗しました: ${e.message}`)}}getPowerShellCommand(){return"\nparam([string]$AudioFilePath);\nAdd-Type -AssemblyName presentationCore;\n$player = New-Object System.Windows.Media.MediaPlayer;\n$url = [Uri][System.IO.Path]::GetFullPath($AudioFilePath);\necho $url;\n$player.Open($url);\n$player.Play();\nStart-Sleep -s ($player.NaturalDuration.TimeSpan.TotalSeconds + 1);\n$player.Close();\n".trim().replace(/\r?\n/g," ")}}function l(e){return new Promise(t=>setTimeout(t,e))}class m{_beepFilePath;constructor(e){this._beepFilePath=e}async _playBeep(){if(this._beepFilePath)try{if("win32"===process.platform){const e=new p;await e.play(this._beepFilePath)}else i().play(this._beepFilePath)}catch(e){}}async startAt(e){if(e<0||e>59)throw new Error("minute は 0 から 59 の間の値である必要があります。");const t=new Date,r=new Date(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),e,0,0);r.getTime()<=t.getTime()&&r.setHours(r.getHours()+1);const o=r.getTime()-t.getTime();await l(o),await this._playBeep()}async startFor(e){if(e<0)throw new Error("minutes は 0 またはそれより大きい値である必要があります。");const t=60*e*1e3;await l(t),await this._playBeep()}}(async function(){const r=await e(t(process.argv)).usage("使い方: $0 [オプション]\n\nポモドーロタイマーを実行します。").option("at",{type:"number",description:"指定した時刻(分針、0-59)にタイマーを設定します。例: `--at 30`。`--for` とは同時に使用できません。",conflicts:"for"}).option("for",{type:"number",description:"指定した分数後にタイマーを終了します。例: `--for 25`。`--at` とは同時に使用できません。",conflicts:"at"}).option("beep",{type:"string",description:"タイマー終了時に再生する音声ファイルのパスを指定します。ファイルが見つからない場合でもエラーにはなりません。"}).help("h").alias("h","help").version("v",JSON.parse(s(o.resolve(o.dirname(n(import.meta.url)),"../package.json"),"utf8")).version).alias("v","version").check(e=>{if("number"!=typeof e.at&&"number"!=typeof e.for)throw new Error("`--at` または `--for` のいずれかのオプションが必要です。");if(void 0!==e.at&&(e.at<0||e.at>59))throw new Error("`--at` は 0 から 59 の間の値である必要があります。");if(void 0!==e.for&&e.for<0)throw new Error("`--for` は 0 またはそれより大きい値である必要があります。");return!0}).strict().parse(),a=new m(r.beep);"number"==typeof r.at?await a.startAt(r.at):"number"==typeof r.for&&await a.startFor(r.for)})().catch(e=>{process.exit(1)});