maestro/ui/src/components/settings/ToolsExternalForm.tsx
oss-sync 3848b5efd7
Some checks failed
CI / build-and-test (push) Has been cancelled
sync: update from private repo (63a6e76)
2026-06-09 03:17:43 +00:00

170 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { HelpText } from './HelpText';
import { FieldLabel, FieldInput } from './formUtils';
import { StringArrayEditor } from './StringArrayEditor';
import type { SectionFormProps } from './types';
/**
* External Services settings — credentials and gates for third-party
* API integrations (X / Twitter, Maps, Amazon / Keepa) plus the
* user-supplied scripts security gate.
*
* Replaces the `x` / `maps` / `amazon` / `user-folder` tabs of the
* legacy grab-bag `ToolsForm`. The config keys are unchanged:
*
* tools.x_auth_token / x_ct0 / x_cli_command / x_timeout / x_proxy / x_chrome_profile
* tools.google_maps_api_key
* tools.amazon_affiliate_tag / keepa_api_key
* tools.user_scripts_enabled / user_scripts_allow_userids
*
* Note: trash_retention_days lives in Paths & Storage (storage.*) since
* config v2 normalization (#360/#362). It is intentionally NOT shown
* here — see PathsStorageForm.
*/
export function ToolsExternalForm({ config, onChange }: SectionFormProps) {
const tools = config.tools ?? {};
return (
<div className="space-y-6">
<h2 className="text-base font-semibold text-slate-800">External Services</h2>
<section className="space-y-5">
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-500">
X / Twitter
</h3>
<div>
<FieldLabel>X Auth Token</FieldLabel>
<FieldInput type="password" value={tools.xAuthToken ?? ''} onChange={v => onChange('tools.xAuthToken', v)} />
<HelpText>X / Twitter auth_token cookie</HelpText>
</div>
<div>
<FieldLabel>X ct0</FieldLabel>
<FieldInput type="password" value={tools.xCt0 ?? ''} onChange={v => onChange('tools.xCt0', v)} />
<HelpText>X / Twitter ct0 cookie</HelpText>
</div>
<div>
<FieldLabel>X CLI Command</FieldLabel>
<FieldInput value={Array.isArray(tools.xCliCommand) ? tools.xCliCommand.join(' ') : (tools.xCliCommand ?? '')}
onChange={v => onChange('tools.xCliCommand', v)} />
<HelpText>twitter-cli </HelpText>
</div>
<div>
<FieldLabel>X Timeout ()</FieldLabel>
<FieldInput type="number" value={tools.xTimeout ?? 90}
onChange={v => onChange('tools.xTimeout', Number(v))} />
</div>
<div>
<FieldLabel>X Proxy</FieldLabel>
<FieldInput value={tools.xProxy ?? ''} onChange={v => onChange('tools.xProxy', v)}
placeholder="http://proxy:port" />
</div>
<div>
<FieldLabel>X Chrome Profile</FieldLabel>
<FieldInput value={tools.xChromeProfile ?? ''} onChange={v => onChange('tools.xChromeProfile', v)}
placeholder="/path/to/chrome/profile" />
<HelpText>Cookie Chrome </HelpText>
</div>
<div>
<FieldLabel>X Media Download</FieldLabel>
<select value={tools.xDownloadMedia ?? 'auto'}
onChange={e => onChange('tools.xDownloadMedia', e.target.value)}
className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md bg-canvas focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none transition-shadow">
<option value="auto">auto/</option>
<option value="never">never</option>
</select>
<HelpText>X 稿デフォルト: auto</HelpText>
</div>
<div>
<FieldLabel>X Video Download</FieldLabel>
<select value={tools.xDownloadVideo ?? 'thumbnail'}
onChange={e => onChange('tools.xDownloadVideo', e.target.value)}
className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md bg-canvas focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none transition-shadow">
<option value="thumbnail">thumbnail</option>
<option value="full">full</option>
<option value="never">never</option>
</select>
<HelpText>X 稿デフォルト: thumbnail</HelpText>
</div>
<div>
<FieldLabel>X Media Max (MB)</FieldLabel>
<FieldInput type="number" value={tools.xMediaMaxMb ?? ''}
onChange={v => onChange('tools.xMediaMaxMb', v ? Number(v) : undefined)} />
<HelpText>1 MB</HelpText>
</div>
<div>
<FieldLabel>X Media Fetch Timeout ()</FieldLabel>
<FieldInput type="number" value={tools.xMediaFetchTimeoutSeconds ?? ''}
onChange={v => onChange('tools.xMediaFetchTimeoutSeconds', v ? Number(v) : undefined)} />
<HelpText></HelpText>
</div>
</section>
<section className="space-y-5 pt-2 border-t border-hairline">
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-500">
Maps
</h3>
<div>
<FieldLabel>Google Maps API Key</FieldLabel>
<FieldInput type="password" value={tools.googleMapsApiKey ?? ''} onChange={v => onChange('tools.googleMapsApiKey', v)} />
<HelpText>Google Maps Places / Directions API Nominatim / OSRM使</HelpText>
</div>
<div>
<FieldLabel>Maps Timeout ()</FieldLabel>
<FieldInput type="number" value={tools.mapsTimeout ?? ''}
onChange={v => onChange('tools.mapsTimeout', v ? Number(v) : undefined)} />
<HelpText>Maps / Nominatim / OSRM デフォルト: 30</HelpText>
</div>
</section>
<section className="space-y-5 pt-2 border-t border-hairline">
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-500">
Amazon / Keepa
</h3>
<div>
<FieldLabel>Amazon Affiliate Tag</FieldLabel>
<FieldInput value={tools.amazonAffiliateTag ?? ''} onChange={v => onChange('tools.amazonAffiliateTag', v)}
placeholder="your-tag-22" />
<HelpText>SearchAmazon 使</HelpText>
</div>
<div>
<FieldLabel>Keepa API Key</FieldLabel>
<FieldInput type="password" value={tools.keepaApiKey ?? ''} onChange={v => onChange('tools.keepaApiKey', v)} />
<HelpText>Keepa API </HelpText>
</div>
</section>
<section className="space-y-5 pt-2 border-t border-hairline">
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-500">
User-supplied Scripts
</h3>
<div>
<FieldLabel>RunUserScript </FieldLabel>
<label className="inline-flex items-center gap-2 text-[13px] text-slate-700">
<input
type="checkbox"
checked={tools.userScriptsEnabled === true}
onChange={e => onChange('tools.userScriptsEnabled', e.target.checked)}
/>
<span> (LLM RunUserScript + scheduled script task )</span>
</label>
<HelpText>
plain runtime Node <code>--permission</code> sandbox child_process / worker / tmpdir FS deny
browser-macros Playwright (child_process / native bindings / network) sandbox Node.js capability
</HelpText>
</div>
<div>
<FieldLabel> allowlist ( = )</FieldLabel>
<StringArrayEditor
value={tools.userScriptsAllowUserids ?? []}
onChange={v => onChange('tools.userScriptsAllowUserids', v)}
placeholder="user id (例: 12345)"
/>
<HelpText>
<code>user_scripts_enabled</code> ID RunUserScript / scheduled script task
</HelpText>
</div>
</section>
</div>
);
}