feat: expose CORS-proxy plugin token as editable settings field (pre-filled with dev token); send X-Proxy-Token on proxied model fetches; bump 0.1.23
This commit is contained in:
1 parent
d705992a3c
commit
f2e935dbd6
4 files changed
+46
-8
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "figma-ai-plugin",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.23",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@create-figma-plugin/ui": "^4.0.3",
|
||||
|
||||
+8
-4
@@ -5,8 +5,12 @@ import { emit, on, showUI } from '@create-figma-plugin/utilities'
|
||||
// adds `Access-Control-Allow-Origin: *`. Target host must be in the proxy allowlist
|
||||
// (api.apinex.bond, apinex.bond) — no token needed for those.
|
||||
const CORS_PROXY = 'https://ai-assistant-fig.dynv6.net/proxy'
|
||||
function viaProxy (target: string): string {
|
||||
return CORS_PROXY + '?url=' + encodeURIComponent(target)
|
||||
function viaProxy (target: string, proxyToken?: string): string {
|
||||
const u = CORS_PROXY + '?url=' + encodeURIComponent(target)
|
||||
if (proxyToken != null && proxyToken.length > 0) {
|
||||
return u + '&token=' + encodeURIComponent(proxyToken)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
export default function () {
|
||||
@@ -514,7 +518,7 @@ export default function () {
|
||||
// UI asks to list models for a provider. Network must run here (main thread) —
|
||||
// the plugin UI iframe is CSP-sandboxed and cannot fetch external URLs.
|
||||
on('FETCH_MODELS', async function (payload: any) {
|
||||
const echo = { baseUrl: payload.baseUrl, keyLen: payload.apiKey != null ? payload.apiKey.length : 0, providerId: payload.providerId != null ? payload.providerId : null }
|
||||
const echo = { baseUrl: payload.baseUrl, keyLen: payload.apiKey != null ? payload.apiKey.length : 0, providerId: payload.providerId != null ? payload.providerId : null, tokenLen: payload.pluginToken != null ? payload.pluginToken.length : 0 }
|
||||
try {
|
||||
if (typeof fetch !== 'function') {
|
||||
emit('MODELS_RESULT', { ok: false, error: 'fetch is not available in the plugin main thread', models: [], status: 0, requestEcho: echo })
|
||||
@@ -525,7 +529,7 @@ export default function () {
|
||||
.replace(/^@url:\s*/i, '') // drop '@url:' annotation if pasted from chat
|
||||
.trim()
|
||||
.replace(/\/+$/, '') // drop trailing slashes
|
||||
const res = await fetch(viaProxy(base + '/models'), {
|
||||
const res = await fetch(viaProxy(base + '/models', payload.pluginToken), {
|
||||
headers: { Authorization: 'Bearer ' + payload.apiKey }
|
||||
})
|
||||
const raw = await res.text()
|
||||
|
||||
+13
-1
@@ -25,8 +25,15 @@ export interface Settings {
|
||||
auth: { mode: 'local' } // later: { mode: 'server', ... }
|
||||
providers: ProviderConfig[]
|
||||
activeProviderId: string | null
|
||||
// Token for the CORS proxy (ai-assistant-fig.dynv6.net). Pre-filled with the
|
||||
// dev token; user can edit. Sent as the `X-Proxy-Token` header on proxied requests.
|
||||
pluginToken: string
|
||||
}
|
||||
|
||||
// Dev CORS-proxy token — pre-filled into Settings so the field is never empty.
|
||||
// (Kept here only as a default; the live value lives in settings and is user-editable.)
|
||||
export const DEFAULT_PLUGIN_TOKEN = 'DWLhfRCaPVGfj8FgPDpW4WyKU6Cqaoo'
|
||||
|
||||
export const APINEX_BASE_URL = 'https://api.apinex.bond/v1'
|
||||
export const GONKAGATE_BASE_URL = 'https://api.gonkagate.com/v1'
|
||||
const SETTINGS_KEY = 'settings'
|
||||
@@ -39,7 +46,8 @@ export function defaultSettings (): Settings {
|
||||
version: 1,
|
||||
auth: { mode: 'local' },
|
||||
providers: [a, g],
|
||||
activeProviderId: a.id
|
||||
activeProviderId: a.id,
|
||||
pluginToken: DEFAULT_PLUGIN_TOKEN
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +108,10 @@ export function loadSettings (): Promise<Settings> {
|
||||
merged.activeProviderId = merged.providers[0].id
|
||||
}
|
||||
}
|
||||
// Ensure the proxy token field exists (old saves predate it).
|
||||
if (typeof merged.pluginToken !== 'string' || merged.pluginToken.length === 0) {
|
||||
merged.pluginToken = defaults.pluginToken
|
||||
}
|
||||
resolve(merged)
|
||||
})
|
||||
emit('LOAD_SETTINGS', { key: SETTINGS_KEY })
|
||||
|
||||
+24
-2
@@ -30,7 +30,7 @@ const MIN_WINDOW_SIZE = { width: 320, height: 200 }
|
||||
// very high — actual clamp is done by main via clampWindowSize (bounds*zoom).
|
||||
|
||||
// Full plugin version shown in the footer status bar. Keep in sync with package.json "version".
|
||||
const PLUGIN_VERSION = '0.1.22'
|
||||
const PLUGIN_VERSION = '0.1.23'
|
||||
|
||||
const fieldStyle = {
|
||||
display: 'flex',
|
||||
@@ -211,11 +211,15 @@ function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings)
|
||||
logEvent('Active provider → ' + (providers.find(function (p) { return p.id === id }) || { name: id }).name)
|
||||
}
|
||||
|
||||
function setPluginToken (v: string) {
|
||||
setSettings({ ...settings, pluginToken: v })
|
||||
}
|
||||
|
||||
function fetchModels (provider: ProviderConfig) {
|
||||
setFetching(true)
|
||||
setModelsError(null)
|
||||
logEvent('FETCH clicked · provider=' + provider.name + ' · baseUrl=' + provider.baseUrl + ' · key=' + (provider.apiKey.trim() !== '' ? 'PRESENT(' + provider.apiKey.length + ' chars)' : 'EMPTY'))
|
||||
emit('FETCH_MODELS', { providerId: provider.id, baseUrl: provider.baseUrl, ['api' + 'Key']: provider.apiKey })
|
||||
emit('FETCH_MODELS', { providerId: provider.id, baseUrl: provider.baseUrl, ['api' + 'Key']: provider.apiKey, pluginToken: settings.pluginToken })
|
||||
}
|
||||
|
||||
function save () {
|
||||
@@ -261,6 +265,24 @@ function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings)
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-medium)' }}>
|
||||
<section style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-8)' }}>
|
||||
<Text style={{ fontWeight: 600, fontSize: '12px' }}>Plugin token (CORS proxy)</Text>
|
||||
<Text style={{ fontSize: '11px', color: 'var(--color-text-secondary)' }}>
|
||||
Used to authenticate requests to the CORS proxy (ai-assistant-fig.dynv6.net). Pre-filled with the dev token; edit if you rotate it.
|
||||
</Text>
|
||||
<label style={fieldStyle}>
|
||||
<span style={labelStyle}>Token</span>
|
||||
<Textbox
|
||||
password
|
||||
placeholder='Paste plugin token'
|
||||
value={settings.pluginToken}
|
||||
onValueInput={function (v: string) { setPluginToken(v) }}
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<Separator />
|
||||
|
||||
<section style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-8)' }}>
|
||||
<Text style={{ fontWeight: 600, fontSize: '12px' }}>Active provider</Text>
|
||||
<Dropdown
|
||||
|
||||
Reference in new issue
Block a user