diff --git a/src/backend/types.ts b/src/backend/types.ts index 67f8a3b..085fa22 100644 --- a/src/backend/types.ts +++ b/src/backend/types.ts @@ -1,3 +1,7 @@ +// Refactored: removed CfApi interface (CF API no longer used for deploy/invoke). +// Removed ResolveInvoke types (302 redirect workaround no longer needed). +// invoke() now executes directly via Dynamic Workers. + import type { InputSchema } from '../codegen.js' export interface DeployParams { @@ -14,7 +18,7 @@ export interface DeployParams { } export interface DeployResult { - capability: string // 直接就是 name,如 "ping" + capability: string url: string expires_at?: string cold_start: boolean @@ -22,7 +26,7 @@ export interface DeployResult { } export interface Capability { - capability: string // 直接就是 name,如 "ping" + capability: string type: 'persistent' | 'normal' | 'ephemeral' deployed: boolean last_access: number @@ -33,7 +37,7 @@ export interface Capability { description?: string tags?: string[] examples?: string[] - schema?: InputSchema // 新增:find 模式返回,让 Agent 知道怎么调用 + schema?: InputSchema } export interface QueryParams { @@ -52,7 +56,7 @@ export interface QueryItem { deployed?: boolean access_count?: number score: number - schema?: InputSchema // 新增:find 模式返回 + schema?: InputSchema } export interface QueryResult { diff --git a/src/backend/worker-pool.ts b/src/backend/worker-pool.ts index 210ab71..d7b9c19 100644 --- a/src/backend/worker-pool.ts +++ b/src/backend/worker-pool.ts @@ -1,8 +1,8 @@ -// Refactored: uses Cloudflare Dynamic Workers (env.LOADER) for invoke. -// Deploy only writes to KV; no CF API calls needed. -// LRU tracks access stats but no longer manages deploy/evict of worker scripts. +// Dynamic Workers backend — no CF REST API calls. +// deploy() stores code in KV; invoke() uses LOADER.get() to run code inline. +// LRU tracks logical "deployed" state for quota semantics. -import type { SigilBackend, DeployParams, DeployResult, Capability, BackendStatus, QueryParams, QueryResult, QueryItem, ResolveInvokeResult, ResolveInvokeError } from './types.js' +import type { SigilBackend, DeployParams, DeployResult, Capability, BackendStatus, QueryParams, QueryResult, QueryItem } from './types.js' import { KvStore } from '../kv.js' import { LruScheduler } from '../lru.js' import { CONFIG } from '../config.js' @@ -11,18 +11,17 @@ import { EmbeddingService, cosineSimilarity, mmrSelect } from '../embedding.js' /** * Dynamic Workers loader binding type. * env.LOADER.get(id, callback) caches a worker by id; callback loads on miss. - * env.LOADER.load(config) creates a one-shot worker. + * Note: CF LOADER.get() is synchronous and returns a DynamicWorkerHandle directly. */ export interface WorkerLoader { - get(id: string, loader: () => Promise | DynamicWorkerConfig): DynamicWorkerHandle - load(config: DynamicWorkerConfig): DynamicWorkerHandle + get(id: string, loader: () => DynamicWorkerConfig): DynamicWorkerHandle } export interface DynamicWorkerConfig { compatibilityDate: string mainModule: string modules: Record - globalOutbound?: null // null = block network access + globalOutbound?: null // null = block outbound network for safety } export interface DynamicWorkerHandle { @@ -53,10 +52,6 @@ export class WorkerPool implements SigilBackend { return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, this.config.HASH_LENGTH) } - private getWorkerName(capability: string): string { - return `${this.config.WORKER_PREFIX}${capability}` - } - async deploy(params: DeployParams): Promise { const { name, code, schema, type, ttl, bindings, description, tags, examples } = params @@ -73,10 +68,32 @@ export class WorkerPool implements SigilBackend { capability = name } - const workerName = this.getWorkerName(capability) const now = Date.now() - // Write KV entries (no CF API deploy needed — code is loaded dynamically at invoke time) + // LRU eviction: mark oldest as not-deployed when quota exceeded + let deployed = await this.lru.countDeployed() + const evictedCapabilities: string[] = [] + + while (deployed >= this.config.MAX_SLOTS) { + const candidate = await this.lru.findEvictionCandidate() + if (!candidate) break + + evictedCapabilities.push(candidate.capability) + const existingLru = await this.kv.getLru(candidate.capability) + if (existingLru) { + await this.kv.setLru(candidate.capability, { + ...existingLru, + deployed: false, + }) + } + await this.kv.incrementEvictionCount() + + deployed = await this.lru.countDeployed() + } + + const evictedCapability = evictedCapabilities[0] + + // Write KV entries — code is loaded dynamically at invoke time via LOADER await this.kv.setCode(capability, code) await this.kv.setMeta(capability, { type, @@ -91,10 +108,7 @@ export class WorkerPool implements SigilBackend { await this.kv.setLru(capability, { last_access: now, access_count: 0, - deployed: true, // always "deployed" since code is in KV - }) - await this.kv.setRoute(capability, { - worker_name: workerName, + deployed: true, }) // Compute and store embedding @@ -122,12 +136,16 @@ export class WorkerPool implements SigilBackend { result.expires_at = new Date(now + ttl * 1000).toISOString() } + if (evictedCapability) { + result.evicted = evictedCapability + } + return result } /** * Invoke a capability using Dynamic Workers. - * LOADER.get() caches warm instances; callback only fires on cache miss. + * LOADER.get() caches warm instances by id; callback fires on cache miss. */ async invoke(capabilityName: string, request: Request): Promise { const code = await this.kv.getCode(capabilityName) @@ -138,43 +156,63 @@ export class WorkerPool implements SigilBackend { }) } - // Update LRU access stats const lru = await this.kv.getLru(capabilityName) - if (lru) { - await this.kv.setLru(capabilityName, { - ...lru, - last_access: Date.now(), - access_count: lru.access_count + 1, - deployed: true, + const isColdStart = !lru?.deployed + + // Update LRU access stats + const now = Date.now() + await this.kv.setLru(capabilityName, { + last_access: now, + access_count: (lru?.access_count ?? 0) + 1, + deployed: true, + }) + + // Use Dynamic Workers LOADER to execute the capability code inline. + // LOADER.get(id, fn) caches the worker instance by id. + const codeHash = await this.generateHash(code) + const workerId = `sigil:${capabilityName}:${codeHash}` + + try { + const worker = this.loader.get(workerId, () => ({ + compatibilityDate: '2026-04-03', + mainModule: 'worker.js', + modules: { 'worker.js': code }, + globalOutbound: null, + })) + + const response = await worker.getEntrypoint().fetch(request) + + if (isColdStart) { + const headers = new Headers(response.headers) + headers.set('X-Sigil-Cold-Start', 'true') + return new Response(response.body, { status: response.status, headers }) + } + + return response + } catch (e: any) { + console.error(`[sigil] Dynamic Worker invoke error for ${capabilityName}:`, e) + return new Response(JSON.stringify({ error: e.message || 'Invoke failed' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, }) } - - // Use Dynamic Workers to load and execute the capability code. - // LOADER.get() caches by id — subsequent requests reuse the warm instance. - const worker = this.loader.get(capabilityName, async () => ({ - compatibilityDate: '2026-04-03', - mainModule: 'worker.js', - modules: { 'worker.js': code }, - })) - - return worker.getEntrypoint().fetch(request) } /** - * resolveInvoke is no longer needed (was used for 302 redirect workaround). - * Kept for interface compatibility; delegates to invoke internally. + * resolveInvoke: kept for SigilBackend interface compatibility. + * With Dynamic Workers, invocation is direct — no subdomain redirect needed. */ async resolveInvoke(capabilityName: string, _request: Request): Promise { const code = await this.kv.getCode(capabilityName) if (!code) { return { error: 'Capability not found', status: 404 } } - // Return a synthetic result; actual invocation now goes through invoke() return { subdomain: '', cold_start: false } } async remove(capabilityName: string): Promise { - // Just clean KV — no CF API script to delete + // Dynamic Workers: no CF API script deletion needed — just clear KV. + // LOADER cache evicts naturally when code is gone. await this.kv.deleteCode(capabilityName) await this.kv.deleteMeta(capabilityName) await this.kv.deleteLru(capabilityName) @@ -270,13 +308,11 @@ export class WorkerPool implements SigilBackend { const qLower = q.toLowerCase() const fallbackItems: QueryItem[] = fallbackCandidates - .filter(cap => { - return ( - cap.capability.toLowerCase().includes(qLower) || - cap.description?.toLowerCase().includes(qLower) || - cap.tags?.some(t => t.toLowerCase().includes(qLower)) - ) - }) + .filter(cap => ( + cap.capability.toLowerCase().includes(qLower) || + cap.description?.toLowerCase().includes(qLower) || + cap.tags?.some(t => t.toLowerCase().includes(qLower)) + )) .map(cap => ({ capability: cap.capability, description: cap.description, @@ -293,10 +329,7 @@ export class WorkerPool implements SigilBackend { if (effectiveMode === 'find') { const scored = embeddingCandidates - .map(c => ({ - ...c, - score: cosineSimilarity(queryVec, c.vector), - })) + .map(c => ({ ...c, score: cosineSimilarity(queryVec, c.vector) })) .filter(c => c.score > 0.3) .sort((a, b) => b.score - a.score) .slice(0, limit) @@ -373,12 +406,19 @@ export class WorkerPool implements SigilBackend { async status(): Promise { const caps = await this.kv.listCapabilities() + let usedSlots = 0 + + for (const cap of caps) { + const lru = await this.kv.getLru(cap) + if (lru?.deployed) usedSlots++ + } + const evictionCount = await this.kv.getEvictionCount() return { backend: 'worker-pool', total_slots: this.config.MAX_SLOTS, - used_slots: caps.length, // All capabilities with code in KV are "deployed" + used_slots: Math.min(usedSlots, this.config.MAX_SLOTS), lru_enabled: true, eviction_count: evictionCount, } diff --git a/src/cf-api.ts b/src/cf-api.ts index c21c479..9eac49f 100644 --- a/src/cf-api.ts +++ b/src/cf-api.ts @@ -1,31 +1,82 @@ -// Refactored: cf-api.ts now only provides optional legacy cleanup (deleteWorker). -// deployWorker, getWorkerSubdomain, and invoke via subdomain fetch are removed. -// Core invoke path now uses Dynamic Workers (LOADER binding) in worker-pool.ts. +// 空壳 Worker 代码 —— slot 未分配时返回 404 +export const IDLE_WORKER_CODE = `export default { + async fetch() { + return new Response(JSON.stringify({error: "Slot not assigned"}), { + status: 404, + headers: {"Content-Type": "application/json"} + }); + } +};` -/** - * Optional CF API helpers for legacy script cleanup. - * Only deleteWorker is retained; deploy and subdomain helpers are gone. - */ -export interface LegacyCfApi { - deleteWorker(name: string): Promise +export interface CfApi { + updateSlotCode(slotIndex: number, code: string): Promise + initSlot(slotIndex: number): Promise + getSlotSubdomain(slotIndex: number): string + invoke(slotIndex: number, request: Request): Promise } -export function createLegacyCfApi(accountId: string, apiToken: string): LegacyCfApi { +import { CONFIG } from './config.js' + +export function createCfApi(accountId: string, apiToken: string): CfApi { const baseUrl = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/scripts` + function getSlotName(slotIndex: number): string { + return `${CONFIG.SLOT_PREFIX}${slotIndex}` + } + + async function putWorkerCode(name: string, code: string): Promise { + const metadata = JSON.stringify({ + main_module: 'worker.js', + compatibility_date: '2026-04-03', + }) + const formData = new FormData() + formData.append('metadata', new Blob([metadata], { type: 'application/json' })) + formData.append('worker.js', new Blob([code], { type: 'application/javascript+module' }), 'worker.js') + const resp = await fetch(`${baseUrl}/${name}`, { + method: 'PUT', + headers: { Authorization: `Bearer ${apiToken}` }, + body: formData, + }) + if (!resp.ok) { + const text = await resp.text() + throw new Error(`CF API PUT worker failed (${resp.status}): ${text}`) + } + } + return { - async deleteWorker(name: string): Promise { - // CF API: DELETE /accounts/{account_id}/workers/scripts/{script_name} - const resp = await fetch(`${baseUrl}/${name}`, { - method: 'DELETE', - headers: { - Authorization: `Bearer ${apiToken}`, - }, + async updateSlotCode(slotIndex: number, code: string): Promise { + await putWorkerCode(getSlotName(slotIndex), code) + }, + + async initSlot(slotIndex: number): Promise { + const name = getSlotName(slotIndex) + await putWorkerCode(name, IDLE_WORKER_CODE) + const subdomainResp = await fetch(`${baseUrl}/${name}/subdomain`, { + method: 'POST', + headers: { Authorization: `Bearer ${apiToken}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ enabled: true }), }) - if (!resp.ok && resp.status !== 404) { - const text = await resp.text() - throw new Error(`CF API delete failed (${resp.status}): ${text}`) + if (!subdomainResp.ok) { + console.warn(`[sigil] failed to enable subdomain for ${name}: ${subdomainResp.status}`) } }, + + getSlotSubdomain(slotIndex: number): string { + return `${getSlotName(slotIndex)}${CONFIG.SUBDOMAIN_SUFFIX}` + }, + + async invoke(slotIndex: number, request: Request): Promise { + const subdomain = `${getSlotName(slotIndex)}${CONFIG.SUBDOMAIN_SUFFIX}` + const url = new URL(request.url) + const targetUrl = `https://${subdomain}${url.pathname}${url.search}` + const headers = new Headers(request.headers) + headers.delete('host') + return fetch(targetUrl, { + method: request.method, + headers, + body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined, + redirect: 'follow', + }) + }, } -} \ No newline at end of file +} diff --git a/src/config.ts b/src/config.ts index 3d3befa..8b2cb12 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,8 +1,10 @@ export const CONFIG = { - MAX_SLOTS: 3, // LRU 验证用,生产 ~400 + MAX_SLOTS: 3, // 预分配 slot 数量(物理页帧总数) DEPLOY_COOLDOWN_MS: 5000, PAGE_RATE_LIMIT: 10, // 次/分钟 PAGE_RATE_WINDOW_MS: 60000, - HASH_LENGTH: 8, + HASH_LENGTH: 6, + SLOT_PREFIX: 's-slot-', // slot Worker 名前缀:s-slot-0, s-slot-1, ... + SUBDOMAIN_SUFFIX: '.shazhou.workers.dev', GATEWAY_URL: 'https://sigil.shazhou.workers.dev', } as const diff --git a/src/index.ts b/src/index.ts index f16a51d..959545a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,8 @@ import { EmbeddingService } from './embedding.js' export interface Env { SIGIL_KV: KVNamespace - AI: any // Cloudflare Workers AI binding - LOADER: any // Dynamic Workers loader binding + AI: any // Cloudflare Workers AI binding + LOADER: any // Dynamic Workers Loader binding (worker_loaders) } export default { diff --git a/src/router.ts b/src/router.ts index 3de1bf5..9617a2d 100644 --- a/src/router.ts +++ b/src/router.ts @@ -43,7 +43,7 @@ export async function handleRequest(request: Request, env: RouterEnv): Promise let schema: InputSchema | undefined if (body.code) { - // Mode A: deploy raw code code = body.code } else { - // Mode B: schema + execute if (!body.execute) { return jsonError(400, 'execute is required when using schema mode') } @@ -173,6 +171,7 @@ async function handleInvoke( request: Request, env: RouterEnv, ): Promise { + // Direct invocation via Dynamic Workers — no redirect, no sub-worker fetch return await env.backend.invoke(capability, request) } diff --git a/test/query.test.ts b/test/query.test.ts index 63844c0..ba78886 100644 --- a/test/query.test.ts +++ b/test/query.test.ts @@ -112,7 +112,7 @@ describe('Query API', () => { // Re-deploy with the new overrides in place const mockKv2 = createMockKv() const mockLoader2 = createMockLoader() - const pool2 = new WorkerPool(mockKv2, mockCf2.loader, mockEmbed as any) + const pool2 = new WorkerPool(mockKv2, mockLoader2.loader, mockEmbed as any) const kv2 = new KvStore(mockKv2) const auth2 = new AuthModule(kv2) await auth2.setToken('deploy-token') diff --git a/test/s06-remove.test.ts b/test/s06-remove.test.ts index 171b301..0856022 100644 --- a/test/s06-remove.test.ts +++ b/test/s06-remove.test.ts @@ -32,7 +32,7 @@ describe('S6: 删除能力', () => { mockLoader.reset() }) - it('should NOT call CF API deleteWorker (Dynamic Workers; KV cleanup only)', async () => { + it('should clear all KV entries (Dynamic Workers: no CF API deleteWorker needed)', async () => { const req = makeRequest('DELETE', '/_api/remove', { token: 'deploy-token', body: { capability: 'ping' }, @@ -40,7 +40,13 @@ describe('S6: 删除能力', () => { const resp = await handleRequest(req, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(resp.status).toBe(200) - // LOADER should not be called during remove + + // All KV entries should be gone + expect(await kv.getCode('ping')).toBeNull() + expect(await kv.getMeta('ping')).toBeNull() + expect(await kv.getLru('ping')).toBeNull() + + // No LOADER.get() calls during remove expect(mockLoader.loaderCalls()).toHaveLength(0) }) @@ -50,7 +56,6 @@ describe('S6: 删除能力', () => { expect(await kv.getCode('ping')).toBeNull() expect(await kv.getMeta('ping')).toBeNull() expect(await kv.getLru('ping')).toBeNull() - expect(await kv.getRoute('ping')).toBeNull() }) it('should return removed capability in response', async () => { diff --git a/test/s07-list.test.ts b/test/s07-list.test.ts index 614d994..ad62b6d 100644 --- a/test/s07-list.test.ts +++ b/test/s07-list.test.ts @@ -7,7 +7,7 @@ import { handleRequest } from '../src/router.js' describe('S7: 列出能力(已迁移至 query 接口)', () => { let mockKv: KVNamespace - let mockCf: ReturnType + let mockLoader: ReturnType let mockEmbed: MockEmbeddingService let pool: WorkerPool let auth: AuthModule @@ -15,7 +15,7 @@ describe('S7: 列出能力(已迁移至 query 接口)', () => { beforeEach(async () => { mockKv = createMockKv() - mockCf = createMockLoader() + mockLoader = createMockLoader() mockEmbed = new MockEmbeddingService() pool = new WorkerPool(mockKv, mockLoader.loader, mockEmbed as any) kv = new KvStore(mockKv) diff --git a/test/s08-health.test.ts b/test/s08-health.test.ts index b6e5ef3..5095838 100644 --- a/test/s08-health.test.ts +++ b/test/s08-health.test.ts @@ -7,7 +7,7 @@ import { handleRequest } from '../src/router.js' describe('S8: 健康端点', () => { let mockKv: KVNamespace - let mockCf: ReturnType + let mockLoader: ReturnType let mockEmbed: MockEmbeddingService let pool: WorkerPool let auth: AuthModule @@ -15,7 +15,7 @@ describe('S8: 健康端点', () => { beforeEach(async () => { mockKv = createMockKv() - mockCf = createMockLoader() + mockLoader = createMockLoader() mockEmbed = new MockEmbeddingService() pool = new WorkerPool(mockKv, mockLoader.loader, mockEmbed as any) kv = new KvStore(mockKv) diff --git a/test/s11-concurrent-page-in.test.ts b/test/s11-concurrent-page-in.test.ts index d09493a..eb92092 100644 --- a/test/s11-concurrent-page-in.test.ts +++ b/test/s11-concurrent-page-in.test.ts @@ -32,7 +32,7 @@ describe('S11: 并发换入去重', () => { }) }) - it('should call deployWorker only once for concurrent page-ins', async () => { + it('should handle concurrent page-ins without error', async () => { const req1 = new Request('https://sigil.shazhou.workers.dev/run/ping') const req2 = new Request('https://sigil.shazhou.workers.dev/run/ping') @@ -45,9 +45,8 @@ describe('S11: 并发换入去重', () => { expect(resp1.status).toBe(200) expect(resp2.status).toBe(200) - // Both calls go through LOADER.get(); Dynamic Workers deduplicates internally + // LOADER.get() should be called (at least once — may be called for each concurrent request) const loaderCalls = mockLoader.loaderCalls() expect(loaderCalls.length).toBeGreaterThanOrEqual(1) - expect(loaderCalls.every(id => id === 's-ping')).toBe(true) }) }) diff --git a/test/s12-page-rate-limit.test.ts b/test/s12-page-rate-limit.test.ts index 3a87ebc..e401c7b 100644 --- a/test/s12-page-rate-limit.test.ts +++ b/test/s12-page-rate-limit.test.ts @@ -2,10 +2,8 @@ import { describe, it, expect, beforeEach } from 'vitest' import { createMockKv, createMockLoader, MockEmbeddingService } from './setup.js' import { WorkerPool } from '../src/backend/worker-pool.js' import { KvStore } from '../src/kv.js' -import { CONFIG } from '../src/config.js' -import { PageRateLimitError } from '../src/lru.js' -describe('S12: 换页速率限制', () => { +describe('S12: Dynamic Workers invoke(原 page-rate-limit)', () => { let mockKv: KVNamespace let mockLoader: ReturnType let mockEmbed: MockEmbeddingService @@ -35,67 +33,26 @@ describe('S12: 换页速率限制', () => { }) } - it(`should allow up to ${CONFIG.PAGE_RATE_LIMIT} page-ins per minute`, async () => { - const results: boolean[] = [] - - for (let i = 0; i < CONFIG.PAGE_RATE_LIMIT; i++) { - const name = `cap${i}` - await setupCapability(name) - - const req = new Request(`https://sigil.shazhou.workers.dev/run/${name}`) - const resp = await pool.invoke(name, req) - results.push(resp.status === 200) - } - - expect(results.every(Boolean)).toBe(true) - }) - - it(`should reject the ${CONFIG.PAGE_RATE_LIMIT + 1}th page-in with 503`, async () => { - // Do PAGE_RATE_LIMIT page-ins - for (let i = 0; i < CONFIG.PAGE_RATE_LIMIT; i++) { + it('should invoke evicted capabilities without page-rate-limit', async () => { + // With Dynamic Workers, there is no page rate limit — invoke always works. + for (let i = 0; i < 15; i++) { const name = `cap${i}` await setupCapability(name) const req = new Request(`https://sigil.shazhou.workers.dev/run/${name}`) - await pool.invoke(name, req) - } - - // 11th one should fail - const name = `cap${CONFIG.PAGE_RATE_LIMIT}` - await setupCapability(name) - - const req = new Request(`https://sigil.shazhou.workers.dev/run/${name}`) - try { const resp = await pool.invoke(name, req) - // If it doesn't throw, check status - expect(resp.status).toBe(503) - } catch (e) { - expect(e).toBeInstanceOf(PageRateLimitError) + expect(resp.status).toBe(200) } }) - it('should include retry_after in error', async () => { - // Fill rate - for (let i = 0; i < CONFIG.PAGE_RATE_LIMIT; i++) { - const name = `cap${i}` - await setupCapability(name) - const req = new Request(`https://sigil.shazhou.workers.dev/run/${name}`) - await pool.invoke(name, req) - } + it('should mark cold-start capability as deployed after invoke', async () => { + await setupCapability('cold') + const lruBefore = await kv.getLru('cold') + expect(lruBefore!.deployed).toBe(false) - const name = `cap${CONFIG.PAGE_RATE_LIMIT}` - await setupCapability(name) - const req = new Request(`https://sigil.shazhou.workers.dev/run/${name}`) + const req = new Request('https://sigil.shazhou.workers.dev/run/cold') + await pool.invoke('cold', req) - try { - const resp = await pool.invoke(name, req) - if (resp.status === 503) { - const body = await resp.json() as { error: string; retry_after?: number } - // retry_after may be 0 for immediate window, just check it exists or we got exception - expect(body.error).toBeTruthy() - } - } catch (e) { - expect(e).toBeInstanceOf(PageRateLimitError) - expect((e as PageRateLimitError).retry_after).toBeGreaterThanOrEqual(0) - } + const lruAfter = await kv.getLru('cold') + expect(lruAfter!.deployed).toBe(true) }) }) diff --git a/wrangler.toml b/wrangler.toml index d54e56d..0559033 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,16 +1,14 @@ name = "sigil" main = "src/index.ts" compatibility_date = "2026-04-03" -compatibility_flags = ["global_fetch_strictly_public"] [[kv_namespaces]] binding = "SIGIL_KV" id = "9943c8873e724b0fb2cf24b4475e5a52" # Dynamic Workers loader binding (open beta) -# Allows Gateway Worker to load and execute sub-Worker code dynamically -# without deploying independent scripts, avoiding Worker-to-Worker fetch -# restrictions (error 1042) on workers.dev subdomains. +# Loads and executes capability code at runtime inside the Gateway Worker, +# bypassing Worker-to-Worker fetch restrictions (error 1042). [[worker_loaders]] binding = "LOADER" @@ -19,11 +17,3 @@ binding = "AI" [vars] SIGIL_ENV = "production" - -# Worker Secrets (set via `wrangler secret put`, never committed to source): -# CF_API_TOKEN - Cloudflare API token with Workers:Edit permission (optional, for legacy cleanup) -# CF_ACCOUNT_ID - Cloudflare Account ID (optional, for legacy cleanup) -# -# To configure: -# echo "$CF_API_TOKEN" | npx wrangler secret put CF_API_TOKEN -# echo "$CF_ACCOUNT_ID" | npx wrangler secret put CF_ACCOUNT_ID