feat(cad): LLM-orchestrated MCP tool-calling for KiCad and FreeCAD
Add Gemini Flash agentic loop that converts natural language prompts into real MCP tool call sequences for PCB design (KiCad) and parametric CAD (FreeCAD). Dynamic schema conversion from MCP tools to Gemini function declarations, 8-turn/60s loop with real execution and result feedback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d8736ba341
commit
bf0661fab2
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
id: TASK-124
|
||||||
|
title: Encrypt all PII at rest in EncryptID database
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-24 00:29'
|
||||||
|
updated_date: '2026-03-24 00:29'
|
||||||
|
labels:
|
||||||
|
- security
|
||||||
|
- encryptid
|
||||||
|
- database
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- src/encryptid/server-crypto.ts
|
||||||
|
- src/encryptid/migrations/encrypt-pii.ts
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Server-side AES-256-GCM encryption for all PII fields stored in PostgreSQL. Keys derived from JWT_SECRET via HKDF with dedicated salts (`pii-v1` for encryption, `pii-hash-v1` for HMAC). HMAC-SHA256 hash indexes for equality lookups on email and UP address fields.
|
||||||
|
|
||||||
|
**Scope:** 18 fields across 6 tables (users, guardians, identity_invites, space_invites, notifications, fund_claims). Username and display_name excluded (public identifiers, needed for ILIKE search).
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `src/encryptid/server-crypto.ts` — NEW: encryptField(), decryptField(), hashForLookup()
|
||||||
|
- `src/encryptid/schema.sql` — 18 _enc/_hash columns + 4 indexes
|
||||||
|
- `src/encryptid/db.ts` — async row mappers with decrypt fallback, dual-write on inserts/updates, hash-based lookups
|
||||||
|
- `src/encryptid/server.ts` — replaced unkeyed hashEmail() with HMAC hashForLookup()
|
||||||
|
- `src/encryptid/migrations/encrypt-pii.ts` — NEW: idempotent backfill script
|
||||||
|
|
||||||
|
**Remaining:** Drop plaintext columns after extended verification period.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 All PII fields have corresponding _enc columns with AES-256-GCM ciphertext
|
||||||
|
- [x] #2 HMAC-SHA256 hash indexes enable email and UP address lookups without plaintext
|
||||||
|
- [x] #3 Row mappers decrypt transparently — callers receive plaintext
|
||||||
|
- [x] #4 Wrong encryption key cannot decrypt (verified with test)
|
||||||
|
- [x] #5 Same plaintext produces different ciphertext each time (random IV)
|
||||||
|
- [x] #6 Backfill migration encrypts all existing rows (0 remaining unencrypted)
|
||||||
|
- [x] #7 Legacy plaintext fallback works for pre-migration rows during transition
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Deployed 2026-03-23. Commit `9695e95`. Backfill completed: 1 user, 2 guardians, 8 identity invites, 2 fund claims encrypted. 19/19 verification tests passed (ciphertext format, decryption, HMAC determinism, wrong-key rejection, random IV uniqueness). Plaintext columns retained for rollback safety — drop in follow-up task after extended verification.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
id: TASK-125
|
||||||
|
title: Configure Stripe & Mollie API keys and test HyperSwitch payment channels
|
||||||
|
status: To Do
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-24 00:56'
|
||||||
|
labels:
|
||||||
|
- payments
|
||||||
|
- hyperswitch
|
||||||
|
- infrastructure
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- 'https://pay.rspace.online/health'
|
||||||
|
- 'https://dashboard.stripe.com/test/apikeys'
|
||||||
|
- 'https://my.mollie.com/dashboard'
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
HyperSwitch payment orchestrator is deployed at `pay.rspace.online` with merchant account `rspace_merchant` and DB migrations complete. The connector configuration and end-to-end payment testing is blocked on obtaining real API keys from Stripe and Mollie.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
- HyperSwitch is live: `https://pay.rspace.online/health`
|
||||||
|
- Merchant account created with publishable key `pk_snd_9167de4f...`
|
||||||
|
- Merchant API key saved to `.env` as `HS_MERCHANT_SECRET_KEY`
|
||||||
|
- Internal mint/escrow/confirm APIs verified working on rspace-online
|
||||||
|
- Bonding curve ($MYCO) endpoints live and tested
|
||||||
|
- `INTERNAL_API_KEY` and `RSPACE_INTERNAL_API_KEY` deployed to both repos
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
1. **Obtain Stripe API key** — create Stripe account or use existing, get test mode API key (`sk_test_...`)
|
||||||
|
2. **Obtain Mollie API key** — create Mollie account, get test API key
|
||||||
|
3. **Add keys to Infisical** — `STRIPE_API_KEY`, `STRIPE_WEBHOOK_SECRET`, `MOLLIE_API_KEY` in rspace project
|
||||||
|
4. **Add keys to payment-infra `.env`** on Netcup
|
||||||
|
5. **Run `scripts/setup-hyperswitch.sh`** — configures Stripe + Mollie connectors, geo-based routing (EU→Mollie, US→Stripe), webhook endpoint
|
||||||
|
6. **Rebuild payment-infra onramp/offramp services** — they have new HyperSwitch integration code (`hyperswitch.ts`, `hyperswitch-offramp.ts`) but haven't been rebuilt
|
||||||
|
7. **Test Stripe channel** — create payment intent, complete with test card `4242424242424242`, verify cUSDC minted
|
||||||
|
8. **Test Mollie channel** — create payment intent with EU billing, complete via Mollie test mode, verify cUSDC minted
|
||||||
|
9. **Test off-ramp** — initiate withdrawal, verify escrow burn, simulate payout webhook, verify confirm/reverse
|
||||||
|
10. **Run `bun scripts/test-full-loop.ts`** — full loop: fiat in → cUSDC → $MYCO → cUSDC → fiat out
|
||||||
|
|
||||||
|
## Key files
|
||||||
|
- `payment-infra/scripts/setup-hyperswitch.sh` — connector + routing setup script
|
||||||
|
- `payment-infra/services/onramp-service/src/hyperswitch.ts` — on-ramp integration
|
||||||
|
- `payment-infra/services/offramp-service/src/hyperswitch-offramp.ts` — off-ramp integration
|
||||||
|
- `rspace-online/scripts/test-full-loop.ts` — end-to-end test script
|
||||||
|
- `rspace-online/server/index.ts` — internal mint/escrow/confirm endpoints (lines 570-680)
|
||||||
|
- `payment-infra/config/hyperswitch/config.toml` — HyperSwitch TOML config on Netcup
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [ ] #1 Stripe test API key obtained and added to Infisical + payment-infra .env
|
||||||
|
- [ ] #2 Mollie test API key obtained and added to Infisical + payment-infra .env
|
||||||
|
- [ ] #3 setup-hyperswitch.sh runs successfully — Stripe + Mollie connectors configured with geo-based routing
|
||||||
|
- [ ] #4 onramp-service and offramp-service rebuilt with HyperSwitch integration code
|
||||||
|
- [ ] #5 Stripe test payment completes end-to-end: card payment → webhook → cUSDC minted in CRDT ledger
|
||||||
|
- [ ] #6 Mollie test payment completes end-to-end: iDEAL/SEPA → webhook → cUSDC minted
|
||||||
|
- [ ] #7 Off-ramp escrow flow verified: escrow burn → payout → confirm (or reverse on failure)
|
||||||
|
- [ ] #8 Full loop test passes: fiat → cUSDC → $MYCO swap → cUSDC → fiat withdrawal
|
||||||
|
<!-- AC:END -->
|
||||||
136
bun.lock
136
bun.lock
|
|
@ -11,6 +11,7 @@
|
||||||
"@google/genai": "^1.43.0",
|
"@google/genai": "^1.43.0",
|
||||||
"@google/generative-ai": "^0.24.1",
|
"@google/generative-ai": "^0.24.1",
|
||||||
"@lit/reactive-element": "^2.0.4",
|
"@lit/reactive-element": "^2.0.4",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
"@noble/curves": "^1.8.0",
|
"@noble/curves": "^1.8.0",
|
||||||
"@noble/hashes": "^1.7.0",
|
"@noble/hashes": "^1.7.0",
|
||||||
"@openfort/openfort-node": "^0.7.0",
|
"@openfort/openfort-node": "^0.7.0",
|
||||||
|
|
@ -42,6 +43,7 @@
|
||||||
"mailparser": "^3.7.2",
|
"mailparser": "^3.7.2",
|
||||||
"marked": "^17.0.3",
|
"marked": "^17.0.3",
|
||||||
"nodemailer": "^6.9.0",
|
"nodemailer": "^6.9.0",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
"perfect-arrows": "^0.3.7",
|
"perfect-arrows": "^0.3.7",
|
||||||
"perfect-freehand": "^1.2.2",
|
"perfect-freehand": "^1.2.2",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
|
|
@ -215,6 +217,8 @@
|
||||||
|
|
||||||
"@google/generative-ai": ["@google/generative-ai@0.24.1", "", {}, "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q=="],
|
"@google/generative-ai": ["@google/generative-ai@0.24.1", "", {}, "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q=="],
|
||||||
|
|
||||||
|
"@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
|
||||||
|
|
||||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
||||||
|
|
||||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
||||||
|
|
@ -261,6 +265,8 @@
|
||||||
|
|
||||||
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
|
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
|
||||||
|
|
||||||
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
|
||||||
|
|
||||||
"@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
|
"@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
|
||||||
|
|
||||||
"@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="],
|
"@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="],
|
||||||
|
|
@ -271,6 +277,10 @@
|
||||||
|
|
||||||
"@openfort/shield-js": ["@openfort/shield-js@0.1.35", "", { "dependencies": { "axios": "1.13.6", "axios-retry": "4.5.0" } }, "sha512-S/v73xRnbgv5i47IRJ7cPWOnJ1bUTOJN+048Y8mJ+ya+iRJUXKTTqePaWApvfrVHHcKA+li8QGyDsRRDefLLVQ=="],
|
"@openfort/shield-js": ["@openfort/shield-js@0.1.35", "", { "dependencies": { "axios": "1.13.6", "axios-retry": "4.5.0" } }, "sha512-S/v73xRnbgv5i47IRJ7cPWOnJ1bUTOJN+048Y8mJ+ya+iRJUXKTTqePaWApvfrVHHcKA+li8QGyDsRRDefLLVQ=="],
|
||||||
|
|
||||||
|
"@pdf-lib/standard-fonts": ["@pdf-lib/standard-fonts@1.0.0", "", { "dependencies": { "pako": "^1.0.6" } }, "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA=="],
|
||||||
|
|
||||||
|
"@pdf-lib/upng": ["@pdf-lib/upng@1.0.1", "", { "dependencies": { "pako": "^1.0.10" } }, "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ=="],
|
||||||
|
|
||||||
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
||||||
|
|
||||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||||
|
|
@ -603,12 +613,16 @@
|
||||||
|
|
||||||
"abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="],
|
"abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="],
|
||||||
|
|
||||||
|
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||||
|
|
||||||
"aes-js": ["aes-js@4.0.0-beta.5", "", {}, "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="],
|
"aes-js": ["aes-js@4.0.0-beta.5", "", {}, "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q=="],
|
||||||
|
|
||||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||||
|
|
||||||
|
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||||
|
|
||||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
@ -637,6 +651,8 @@
|
||||||
|
|
||||||
"bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="],
|
"bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="],
|
||||||
|
|
||||||
|
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||||
|
|
||||||
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
|
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
|
||||||
|
|
||||||
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
@ -647,8 +663,12 @@
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
|
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
|
||||||
|
|
||||||
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
|
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||||
|
|
||||||
"camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
"camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
||||||
|
|
||||||
"cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
"cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
||||||
|
|
@ -663,8 +683,18 @@
|
||||||
|
|
||||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||||
|
|
||||||
|
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
|
||||||
|
|
||||||
|
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||||
|
|
||||||
|
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||||
|
|
||||||
|
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||||
|
|
||||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||||
|
|
||||||
|
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||||
|
|
||||||
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||||
|
|
||||||
"cron-parser": ["cron-parser@5.5.0", "", { "dependencies": { "luxon": "^3.7.1" } }, "sha512-oML4lKUXxizYswqmxuOCpgFS8BNUJpIu6k/2HVHyaL8Ynnf3wdf9tkns0yRdJLSIjkJ+b0DXHMZEHGpMwjnPww=="],
|
"cron-parser": ["cron-parser@5.5.0", "", { "dependencies": { "luxon": "^3.7.1" } }, "sha512-oML4lKUXxizYswqmxuOCpgFS8BNUJpIu6k/2HVHyaL8Ynnf3wdf9tkns0yRdJLSIjkJ+b0DXHMZEHGpMwjnPww=="],
|
||||||
|
|
@ -683,6 +713,8 @@
|
||||||
|
|
||||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||||
|
|
||||||
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
|
|
||||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
@ -705,8 +737,12 @@
|
||||||
|
|
||||||
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
||||||
|
|
||||||
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||||
|
|
||||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
"encoding-japanese": ["encoding-japanese@2.2.0", "", {}, "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A=="],
|
"encoding-japanese": ["encoding-japanese@2.2.0", "", {}, "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A=="],
|
||||||
|
|
||||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
|
|
@ -721,12 +757,24 @@
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||||
|
|
||||||
|
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
|
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||||
|
|
||||||
"ethers": ["ethers@6.16.0", "", { "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", "@types/node": "22.7.5", "aes-js": "4.0.0-beta.5", "tslib": "2.7.0", "ws": "8.17.1" } }, "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A=="],
|
"ethers": ["ethers@6.16.0", "", { "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", "@types/node": "22.7.5", "aes-js": "4.0.0-beta.5", "tslib": "2.7.0", "ws": "8.17.1" } }, "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A=="],
|
||||||
|
|
||||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
||||||
|
|
||||||
|
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||||
|
|
||||||
|
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
|
||||||
|
|
||||||
|
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||||
|
|
||||||
|
"express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="],
|
||||||
|
|
||||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||||
|
|
||||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
@ -739,6 +787,8 @@
|
||||||
|
|
||||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
|
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||||
|
|
||||||
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||||
|
|
||||||
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
|
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
|
||||||
|
|
@ -749,6 +799,10 @@
|
||||||
|
|
||||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
|
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
@ -789,6 +843,8 @@
|
||||||
|
|
||||||
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
|
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
|
||||||
|
|
||||||
|
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
"http_ece": ["http_ece@1.2.0", "", {}, "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA=="],
|
"http_ece": ["http_ece@1.2.0", "", {}, "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA=="],
|
||||||
|
|
||||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||||
|
|
@ -803,10 +859,14 @@
|
||||||
|
|
||||||
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||||
|
|
||||||
|
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
||||||
|
|
||||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||||
|
|
||||||
"is-retry-allowed": ["is-retry-allowed@2.2.0", "", {}, "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg=="],
|
"is-retry-allowed": ["is-retry-allowed@2.2.0", "", {}, "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg=="],
|
||||||
|
|
||||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
|
|
@ -825,6 +885,8 @@
|
||||||
|
|
||||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
|
|
||||||
|
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||||
|
|
||||||
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
|
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
|
||||||
|
|
||||||
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
||||||
|
|
@ -867,9 +929,13 @@
|
||||||
|
|
||||||
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
||||||
|
|
||||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||||
|
|
||||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||||
|
|
||||||
|
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||||
|
|
||||||
|
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="],
|
"minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="],
|
||||||
|
|
||||||
|
|
@ -883,14 +949,24 @@
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||||
|
|
||||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||||
|
|
||||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||||
|
|
||||||
"nodemailer": ["nodemailer@6.10.1", "", {}, "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="],
|
"nodemailer": ["nodemailer@6.10.1", "", {}, "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="],
|
||||||
|
|
||||||
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
|
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||||
|
|
||||||
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
|
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
|
||||||
|
|
||||||
|
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||||
|
|
||||||
|
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||||
|
|
||||||
"orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
|
"orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
|
||||||
|
|
||||||
"ox": ["ox@0.12.4", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q=="],
|
"ox": ["ox@0.12.4", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q=="],
|
||||||
|
|
@ -909,12 +985,18 @@
|
||||||
|
|
||||||
"parseley": ["parseley@0.12.1", "", { "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" } }, "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw=="],
|
"parseley": ["parseley@0.12.1", "", { "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" } }, "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw=="],
|
||||||
|
|
||||||
|
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||||
|
|
||||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
|
|
||||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
|
"path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
||||||
|
|
||||||
|
"pdf-lib": ["pdf-lib@1.17.1", "", { "dependencies": { "@pdf-lib/standard-fonts": "^1.0.0", "@pdf-lib/upng": "^1.0.1", "pako": "^1.0.11", "tslib": "^1.11.1" } }, "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw=="],
|
||||||
|
|
||||||
"peberminta": ["peberminta@0.9.0", "", {}, "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="],
|
"peberminta": ["peberminta@0.9.0", "", {}, "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="],
|
||||||
|
|
||||||
"perfect-arrows": ["perfect-arrows@0.3.7", "", {}, "sha512-wEN2gerTPVWl3yqoFEF8OeGbg3aRe2sxNUi9rnyYrCsL4JcI6K2tBDezRtqVrYG0BNtsWLdYiiTrYm+X//8yLQ=="],
|
"perfect-arrows": ["perfect-arrows@0.3.7", "", {}, "sha512-wEN2gerTPVWl3yqoFEF8OeGbg3aRe2sxNUi9rnyYrCsL4JcI6K2tBDezRtqVrYG0BNtsWLdYiiTrYm+X//8yLQ=="],
|
||||||
|
|
@ -931,6 +1013,8 @@
|
||||||
|
|
||||||
"pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="],
|
"pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="],
|
||||||
|
|
||||||
|
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
||||||
|
|
||||||
"playwright": ["playwright@1.58.2", "", { "dependencies": { "playwright-core": "1.58.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A=="],
|
"playwright": ["playwright@1.58.2", "", { "dependencies": { "playwright-core": "1.58.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A=="],
|
||||||
|
|
||||||
"playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
|
"playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
|
||||||
|
|
@ -983,6 +1067,8 @@
|
||||||
|
|
||||||
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
|
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
|
||||||
|
|
||||||
|
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||||
|
|
||||||
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
@ -991,8 +1077,14 @@
|
||||||
|
|
||||||
"qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
|
"qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
|
||||||
|
|
||||||
|
"qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
||||||
|
|
||||||
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
||||||
|
|
||||||
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
|
|
||||||
|
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
|
|
||||||
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
||||||
|
|
@ -1011,6 +1103,8 @@
|
||||||
|
|
||||||
"rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
|
"rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
|
||||||
|
|
||||||
|
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||||
|
|
||||||
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
||||||
|
|
@ -1021,16 +1115,30 @@
|
||||||
|
|
||||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
|
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
||||||
|
|
||||||
|
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
|
||||||
|
|
||||||
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
|
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
|
||||||
|
|
||||||
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
|
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
|
||||||
|
|
||||||
|
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||||
|
|
||||||
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
||||||
|
|
||||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
|
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||||
|
|
||||||
|
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||||
|
|
||||||
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||||
|
|
||||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
||||||
|
|
@ -1047,6 +1155,8 @@
|
||||||
|
|
||||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||||
|
|
||||||
|
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
@ -1065,6 +1175,8 @@
|
||||||
|
|
||||||
"tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="],
|
"tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="],
|
||||||
|
|
||||||
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
@ -1073,12 +1185,16 @@
|
||||||
|
|
||||||
"tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="],
|
"tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="],
|
||||||
|
|
||||||
|
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
|
||||||
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
@ -1087,6 +1203,8 @@
|
||||||
|
|
||||||
"valid-url": ["valid-url@1.0.9", "", {}, "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="],
|
"valid-url": ["valid-url@1.0.9", "", {}, "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="],
|
||||||
|
|
||||||
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
"viem": ["viem@2.46.3", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.12.4", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-2LJS+Hyh2sYjHXQtzfv1kU9pZx9dxFzvoU/ZKIcn0FNtOU0HQuIICuYdWtUDFHaGXbAdVo8J1eCvmjkL9JVGwg=="],
|
"viem": ["viem@2.46.3", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.12.4", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-2LJS+Hyh2sYjHXQtzfv1kU9pZx9dxFzvoU/ZKIcn0FNtOU0HQuIICuYdWtUDFHaGXbAdVo8J1eCvmjkL9JVGwg=="],
|
||||||
|
|
||||||
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
@ -1113,6 +1231,8 @@
|
||||||
|
|
||||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
|
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||||
|
|
||||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
"y-indexeddb": ["y-indexeddb@9.0.12", "", { "dependencies": { "lib0": "^0.2.74" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg=="],
|
"y-indexeddb": ["y-indexeddb@9.0.12", "", { "dependencies": { "lib0": "^0.2.74" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg=="],
|
||||||
|
|
@ -1133,6 +1253,8 @@
|
||||||
|
|
||||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
||||||
|
|
||||||
"@automerge/automerge/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
"@automerge/automerge/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||||
|
|
||||||
"@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
"@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
||||||
|
|
@ -1153,6 +1275,8 @@
|
||||||
|
|
||||||
"@openfort/openfort-node/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
|
"@openfort/openfort-node/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
|
||||||
|
|
||||||
|
"body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"ecdsa-sig-formatter/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
"ecdsa-sig-formatter/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||||
|
|
||||||
"ethers/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.10.1", "", {}, "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="],
|
"ethers/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.10.1", "", {}, "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="],
|
||||||
|
|
@ -1167,6 +1291,8 @@
|
||||||
|
|
||||||
"ethers/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
"ethers/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
||||||
|
|
||||||
|
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
"gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
|
|
||||||
"imapflow/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
"imapflow/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
@ -1181,8 +1307,12 @@
|
||||||
|
|
||||||
"mailparser/nodemailer": ["nodemailer@7.0.13", "", {}, "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw=="],
|
"mailparser/nodemailer": ["nodemailer@7.0.13", "", {}, "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw=="],
|
||||||
|
|
||||||
|
"pdf-lib/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||||
|
|
||||||
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
|
"raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||||
|
|
||||||
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||||
|
|
@ -1197,6 +1327,8 @@
|
||||||
|
|
||||||
"ethers/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
"ethers/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||||
|
|
||||||
|
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||||
|
|
||||||
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||||
|
|
|
||||||
|
|
@ -269,6 +269,7 @@ export class FolkBlender extends FolkShape {
|
||||||
|
|
||||||
#isLoading = false;
|
#isLoading = false;
|
||||||
#error: string | null = null;
|
#error: string | null = null;
|
||||||
|
#prompt: string | null = null;
|
||||||
#renderUrl: string | null = null;
|
#renderUrl: string | null = null;
|
||||||
#script: string | null = null;
|
#script: string | null = null;
|
||||||
#blendUrl: string | null = null;
|
#blendUrl: string | null = null;
|
||||||
|
|
@ -365,6 +366,25 @@ export class FolkBlender extends FolkShape {
|
||||||
this.dispatchEvent(new CustomEvent("close"));
|
this.dispatchEvent(new CustomEvent("close"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Restore persisted state
|
||||||
|
if (this.#prompt && this.#promptInput) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#renderUrl || this.#script) {
|
||||||
|
this.#renderResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
fetch("/api/blender-gen/health").then(r => r.json()).then((h: any) => {
|
||||||
|
if (!h.available && this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = (h.issues || []).join(", ") || "Blender service unavailable";
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
if (this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = "Cannot reach Blender health endpoint";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,6 +392,7 @@ export class FolkBlender extends FolkShape {
|
||||||
const prompt = this.#promptInput?.value.trim();
|
const prompt = this.#promptInput?.value.trim();
|
||||||
if (!prompt || this.#isLoading) return;
|
if (!prompt || this.#isLoading) return;
|
||||||
|
|
||||||
|
this.#prompt = prompt;
|
||||||
this.#isLoading = true;
|
this.#isLoading = true;
|
||||||
this.#error = null;
|
this.#error = null;
|
||||||
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
||||||
|
|
@ -437,6 +458,10 @@ export class FolkBlender extends FolkShape {
|
||||||
|
|
||||||
static override fromData(data: Record<string, any>): FolkBlender {
|
static override fromData(data: Record<string, any>): FolkBlender {
|
||||||
const shape = FolkShape.fromData(data) as FolkBlender;
|
const shape = FolkShape.fromData(data) as FolkBlender;
|
||||||
|
if (data.prompt) shape.#prompt = data.prompt;
|
||||||
|
if (data.renderUrl) shape.#renderUrl = data.renderUrl;
|
||||||
|
if (data.script) shape.#script = data.script;
|
||||||
|
if (data.blendUrl) shape.#blendUrl = data.blendUrl;
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,6 +469,7 @@ export class FolkBlender extends FolkShape {
|
||||||
return {
|
return {
|
||||||
...super.toJSON(),
|
...super.toJSON(),
|
||||||
type: "folk-blender",
|
type: "folk-blender",
|
||||||
|
prompt: this.#prompt,
|
||||||
renderUrl: this.#renderUrl,
|
renderUrl: this.#renderUrl,
|
||||||
script: this.#script,
|
script: this.#script,
|
||||||
blendUrl: this.#blendUrl,
|
blendUrl: this.#blendUrl,
|
||||||
|
|
@ -452,5 +478,13 @@ export class FolkBlender extends FolkShape {
|
||||||
|
|
||||||
override applyData(data: Record<string, any>): void {
|
override applyData(data: Record<string, any>): void {
|
||||||
super.applyData(data);
|
super.applyData(data);
|
||||||
|
if ("prompt" in data) this.#prompt = data.prompt;
|
||||||
|
if ("renderUrl" in data) this.#renderUrl = data.renderUrl;
|
||||||
|
if ("script" in data) this.#script = data.script;
|
||||||
|
if ("blendUrl" in data) this.#blendUrl = data.blendUrl;
|
||||||
|
if (this.#promptInput && this.#prompt) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#renderUrl || this.#script) {
|
||||||
|
this.#renderResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,7 @@ export class FolkEmbed extends FolkShape {
|
||||||
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
|
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
|
||||||
iframe.allowFullscreen = true;
|
iframe.allowFullscreen = true;
|
||||||
iframe.referrerPolicy = "no-referrer";
|
iframe.referrerPolicy = "no-referrer";
|
||||||
|
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-presentation");
|
||||||
|
|
||||||
content.appendChild(iframe);
|
content.appendChild(iframe);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,7 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
|
|
||||||
#isLoading = false;
|
#isLoading = false;
|
||||||
#error: string | null = null;
|
#error: string | null = null;
|
||||||
|
#prompt: string | null = null;
|
||||||
#previewUrl: string | null = null;
|
#previewUrl: string | null = null;
|
||||||
#stepUrl: string | null = null;
|
#stepUrl: string | null = null;
|
||||||
#stlUrl: string | null = null;
|
#stlUrl: string | null = null;
|
||||||
|
|
@ -293,6 +294,25 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
this.dispatchEvent(new CustomEvent("close"));
|
this.dispatchEvent(new CustomEvent("close"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Restore persisted state
|
||||||
|
if (this.#prompt && this.#promptInput) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#previewUrl || this.#stepUrl || this.#stlUrl) {
|
||||||
|
this.#renderResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
fetch("/api/freecad/health").then(r => r.json()).then((h: any) => {
|
||||||
|
if (!h.available && this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = h.error || "FreeCAD MCP server unavailable";
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
if (this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = "Cannot reach FreeCAD health endpoint";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,12 +320,13 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
const prompt = this.#promptInput?.value.trim();
|
const prompt = this.#promptInput?.value.trim();
|
||||||
if (!prompt || this.#isLoading) return;
|
if (!prompt || this.#isLoading) return;
|
||||||
|
|
||||||
|
this.#prompt = prompt;
|
||||||
this.#isLoading = true;
|
this.#isLoading = true;
|
||||||
this.#error = null;
|
this.#error = null;
|
||||||
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
||||||
|
|
||||||
if (this.#previewArea) {
|
if (this.#previewArea) {
|
||||||
this.#previewArea.innerHTML = '<div class="loading"><div class="spinner"></div><span>Generating CAD model...</span></div>';
|
this.#previewArea.innerHTML = '<div class="loading"><div class="spinner"></div><span>Generating CAD model with AI... (may take 30-60s)</span></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -369,6 +390,10 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
|
|
||||||
static override fromData(data: Record<string, any>): FolkFreeCAD {
|
static override fromData(data: Record<string, any>): FolkFreeCAD {
|
||||||
const shape = FolkShape.fromData(data) as FolkFreeCAD;
|
const shape = FolkShape.fromData(data) as FolkFreeCAD;
|
||||||
|
if (data.prompt) shape.#prompt = data.prompt;
|
||||||
|
if (data.previewUrl) shape.#previewUrl = data.previewUrl;
|
||||||
|
if (data.stepUrl) shape.#stepUrl = data.stepUrl;
|
||||||
|
if (data.stlUrl) shape.#stlUrl = data.stlUrl;
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,6 +401,7 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
return {
|
return {
|
||||||
...super.toJSON(),
|
...super.toJSON(),
|
||||||
type: "folk-freecad",
|
type: "folk-freecad",
|
||||||
|
prompt: this.#prompt,
|
||||||
previewUrl: this.#previewUrl,
|
previewUrl: this.#previewUrl,
|
||||||
stepUrl: this.#stepUrl,
|
stepUrl: this.#stepUrl,
|
||||||
stlUrl: this.#stlUrl,
|
stlUrl: this.#stlUrl,
|
||||||
|
|
@ -384,5 +410,13 @@ export class FolkFreeCAD extends FolkShape {
|
||||||
|
|
||||||
override applyData(data: Record<string, any>): void {
|
override applyData(data: Record<string, any>): void {
|
||||||
super.applyData(data);
|
super.applyData(data);
|
||||||
|
if ("prompt" in data) this.#prompt = data.prompt;
|
||||||
|
if ("previewUrl" in data) this.#previewUrl = data.previewUrl;
|
||||||
|
if ("stepUrl" in data) this.#stepUrl = data.stepUrl;
|
||||||
|
if ("stlUrl" in data) this.#stlUrl = data.stlUrl;
|
||||||
|
if (this.#promptInput && this.#prompt) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#previewUrl || this.#stepUrl || this.#stlUrl) {
|
||||||
|
this.#renderResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,8 @@ export class FolkKiCAD extends FolkShape {
|
||||||
|
|
||||||
#isLoading = false;
|
#isLoading = false;
|
||||||
#error: string | null = null;
|
#error: string | null = null;
|
||||||
|
#prompt: string | null = null;
|
||||||
|
#components: string[] = [];
|
||||||
#schematicSvg: string | null = null;
|
#schematicSvg: string | null = null;
|
||||||
#boardSvg: string | null = null;
|
#boardSvg: string | null = null;
|
||||||
#gerberUrl: string | null = null;
|
#gerberUrl: string | null = null;
|
||||||
|
|
@ -373,6 +375,27 @@ export class FolkKiCAD extends FolkShape {
|
||||||
this.dispatchEvent(new CustomEvent("close"));
|
this.dispatchEvent(new CustomEvent("close"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Restore persisted state
|
||||||
|
if (this.#prompt && this.#promptInput) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#components.length && this.#componentInput) this.#componentInput.value = this.#components.join(", ");
|
||||||
|
if (this.#schematicSvg || this.#boardSvg || this.#drcResults) {
|
||||||
|
this.#renderPreview();
|
||||||
|
this.#showExports();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
fetch("/api/kicad/health").then(r => r.json()).then((h: any) => {
|
||||||
|
if (!h.available && this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = h.error || "KiCAD MCP server unavailable";
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
if (this.#generateBtn) {
|
||||||
|
this.#generateBtn.disabled = true;
|
||||||
|
this.#generateBtn.title = "Cannot reach KiCAD health endpoint";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,17 +408,18 @@ export class FolkKiCAD extends FolkShape {
|
||||||
.map((c) => c.trim())
|
.map((c) => c.trim())
|
||||||
.filter(Boolean) || [];
|
.filter(Boolean) || [];
|
||||||
|
|
||||||
|
this.#prompt = prompt;
|
||||||
|
this.#components = components;
|
||||||
this.#isLoading = true;
|
this.#isLoading = true;
|
||||||
this.#error = null;
|
this.#error = null;
|
||||||
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
if (this.#generateBtn) this.#generateBtn.disabled = true;
|
||||||
|
|
||||||
if (this.#previewArea) {
|
if (this.#previewArea) {
|
||||||
this.#previewArea.innerHTML = '<div class="loading"><div class="spinner"></div><span>Generating PCB design...</span></div>';
|
this.#previewArea.innerHTML = '<div class="loading"><div class="spinner"></div><span>Designing PCB with AI... (may take 30-60s)</span></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 1: Create project
|
const createRes = await fetch("/api/kicad/generate", {
|
||||||
const createRes = await fetch("/api/kicad/create_project", {
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ prompt, components }),
|
body: JSON.stringify({ prompt, components }),
|
||||||
|
|
@ -485,6 +509,14 @@ export class FolkKiCAD extends FolkShape {
|
||||||
|
|
||||||
static override fromData(data: Record<string, any>): FolkKiCAD {
|
static override fromData(data: Record<string, any>): FolkKiCAD {
|
||||||
const shape = FolkShape.fromData(data) as FolkKiCAD;
|
const shape = FolkShape.fromData(data) as FolkKiCAD;
|
||||||
|
if (data.prompt) shape.#prompt = data.prompt;
|
||||||
|
if (data.components) shape.#components = data.components;
|
||||||
|
if (data.schematicSvg) shape.#schematicSvg = data.schematicSvg;
|
||||||
|
if (data.boardSvg) shape.#boardSvg = data.boardSvg;
|
||||||
|
if (data.gerberUrl) shape.#gerberUrl = data.gerberUrl;
|
||||||
|
if (data.bomUrl) shape.#bomUrl = data.bomUrl;
|
||||||
|
if (data.pdfUrl) shape.#pdfUrl = data.pdfUrl;
|
||||||
|
if (data.drcResults) shape.#drcResults = data.drcResults;
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,15 +524,32 @@ export class FolkKiCAD extends FolkShape {
|
||||||
return {
|
return {
|
||||||
...super.toJSON(),
|
...super.toJSON(),
|
||||||
type: "folk-kicad",
|
type: "folk-kicad",
|
||||||
|
prompt: this.#prompt,
|
||||||
|
components: this.#components,
|
||||||
schematicSvg: this.#schematicSvg,
|
schematicSvg: this.#schematicSvg,
|
||||||
boardSvg: this.#boardSvg,
|
boardSvg: this.#boardSvg,
|
||||||
gerberUrl: this.#gerberUrl,
|
gerberUrl: this.#gerberUrl,
|
||||||
bomUrl: this.#bomUrl,
|
bomUrl: this.#bomUrl,
|
||||||
pdfUrl: this.#pdfUrl,
|
pdfUrl: this.#pdfUrl,
|
||||||
|
drcResults: this.#drcResults,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
override applyData(data: Record<string, any>): void {
|
override applyData(data: Record<string, any>): void {
|
||||||
super.applyData(data);
|
super.applyData(data);
|
||||||
|
if ("prompt" in data) this.#prompt = data.prompt;
|
||||||
|
if ("components" in data) this.#components = data.components || [];
|
||||||
|
if ("schematicSvg" in data) this.#schematicSvg = data.schematicSvg;
|
||||||
|
if ("boardSvg" in data) this.#boardSvg = data.boardSvg;
|
||||||
|
if ("gerberUrl" in data) this.#gerberUrl = data.gerberUrl;
|
||||||
|
if ("bomUrl" in data) this.#bomUrl = data.bomUrl;
|
||||||
|
if ("pdfUrl" in data) this.#pdfUrl = data.pdfUrl;
|
||||||
|
if ("drcResults" in data) this.#drcResults = data.drcResults;
|
||||||
|
if (this.#promptInput && this.#prompt) this.#promptInput.value = this.#prompt;
|
||||||
|
if (this.#componentInput && this.#components.length) this.#componentInput.value = this.#components.join(", ");
|
||||||
|
if (this.#schematicSvg || this.#boardSvg || this.#drcResults) {
|
||||||
|
this.#renderPreview();
|
||||||
|
this.#showExports();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import { createSlashCommandPlugin } from './slash-command';
|
||||||
import type { ImportExportDialog } from './import-export-dialog';
|
import type { ImportExportDialog } from './import-export-dialog';
|
||||||
import { SpeechDictation } from '../../../lib/speech-dictation';
|
import { SpeechDictation } from '../../../lib/speech-dictation';
|
||||||
import { TourEngine } from '../../../shared/tour-engine';
|
import { TourEngine } from '../../../shared/tour-engine';
|
||||||
import { ViewHistory } from '../../../shared/view-history.js';
|
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
import { IndexeddbPersistence } from 'y-indexeddb';
|
import { IndexeddbPersistence } from 'y-indexeddb';
|
||||||
import { ySyncPlugin, yUndoPlugin, yCursorPlugin } from '@tiptap/y-tiptap';
|
import { ySyncPlugin, yUndoPlugin, yCursorPlugin } from '@tiptap/y-tiptap';
|
||||||
|
|
@ -124,7 +123,6 @@ interface NotebookDoc {
|
||||||
class FolkNotesApp extends HTMLElement {
|
class FolkNotesApp extends HTMLElement {
|
||||||
private shadow!: ShadowRoot;
|
private shadow!: ShadowRoot;
|
||||||
private space = "";
|
private space = "";
|
||||||
private view: "notebooks" | "notebook" | "note" = "notebooks";
|
|
||||||
private notebooks: Notebook[] = [];
|
private notebooks: Notebook[] = [];
|
||||||
private selectedNotebook: (Notebook & { notes: Note[] }) | null = null;
|
private selectedNotebook: (Notebook & { notes: Note[] }) | null = null;
|
||||||
private selectedNote: Note | null = null;
|
private selectedNote: Note | null = null;
|
||||||
|
|
@ -134,19 +132,21 @@ class FolkNotesApp extends HTMLElement {
|
||||||
private loading = false;
|
private loading = false;
|
||||||
private error = "";
|
private error = "";
|
||||||
|
|
||||||
|
// Sidebar state
|
||||||
|
private expandedNotebooks = new Set<string>();
|
||||||
|
private notebookNotes = new Map<string, Note[]>();
|
||||||
|
private sidebarOpen = true;
|
||||||
|
|
||||||
// Zone-based rendering
|
// Zone-based rendering
|
||||||
private navZone!: HTMLDivElement;
|
private navZone!: HTMLDivElement;
|
||||||
private contentZone!: HTMLDivElement;
|
private contentZone!: HTMLDivElement;
|
||||||
private metaZone!: HTMLDivElement;
|
private metaZone!: HTMLDivElement;
|
||||||
|
|
||||||
// Navigation history
|
|
||||||
private _history = new ViewHistory<"notebooks" | "notebook" | "note">("notebooks");
|
|
||||||
|
|
||||||
// Guided tour
|
// Guided tour
|
||||||
private _tour!: TourEngine;
|
private _tour!: TourEngine;
|
||||||
private static readonly TOUR_STEPS = [
|
private static readonly TOUR_STEPS = [
|
||||||
{ target: '#create-notebook', title: "Create a Notebook", message: "Notebooks organise your notes by topic. Click '+ New Notebook' to create one.", advanceOnClick: true },
|
{ target: '#create-notebook', title: "Create a Notebook", message: "Notebooks organise your notes by topic. Click '+ New Notebook' to create one.", advanceOnClick: true },
|
||||||
{ target: '#create-note', title: "Create a Note", message: "Inside a notebook you can add notes, links, tasks, and more. Click '+ New Note' to add one.", advanceOnClick: true },
|
{ target: '.sbt-nb-add', title: "Create a Note", message: "Each notebook has a '+' button to add new notes. Click it to create one.", advanceOnClick: true },
|
||||||
{ target: '#editor-toolbar', title: "Editor Toolbar", message: "Format text with the toolbar — bold, lists, code blocks, headings, and more. Click Next to continue.", advanceOnClick: false },
|
{ target: '#editor-toolbar', title: "Editor Toolbar", message: "Format text with the toolbar — bold, lists, code blocks, headings, and more. Click Next to continue.", advanceOnClick: false },
|
||||||
{ target: '[data-cmd="mic"]', title: "Voice Notes", message: "Record voice notes with live transcription. Your words appear as you speak — no uploads needed.", advanceOnClick: false },
|
{ target: '[data-cmd="mic"]', title: "Voice Notes", message: "Record voice notes with live transcription. Your words appear as you speak — no uploads needed.", advanceOnClick: false },
|
||||||
];
|
];
|
||||||
|
|
@ -260,16 +260,50 @@ class FolkNotesApp extends HTMLElement {
|
||||||
private setupShadow() {
|
private setupShadow() {
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.textContent = this.getStyles();
|
style.textContent = this.getStyles();
|
||||||
|
|
||||||
|
const layout = document.createElement('div');
|
||||||
|
layout.id = 'notes-layout';
|
||||||
|
|
||||||
this.navZone = document.createElement('div');
|
this.navZone = document.createElement('div');
|
||||||
this.navZone.id = 'nav-zone';
|
this.navZone.id = 'nav-zone';
|
||||||
|
|
||||||
|
const rightCol = document.createElement('div');
|
||||||
|
rightCol.className = 'notes-right-col';
|
||||||
|
|
||||||
this.contentZone = document.createElement('div');
|
this.contentZone = document.createElement('div');
|
||||||
this.contentZone.id = 'content-zone';
|
this.contentZone.id = 'content-zone';
|
||||||
this.metaZone = document.createElement('div');
|
this.metaZone = document.createElement('div');
|
||||||
this.metaZone.id = 'meta-zone';
|
this.metaZone.id = 'meta-zone';
|
||||||
|
|
||||||
|
rightCol.appendChild(this.contentZone);
|
||||||
|
rightCol.appendChild(this.metaZone);
|
||||||
|
|
||||||
|
layout.appendChild(this.navZone);
|
||||||
|
layout.appendChild(rightCol);
|
||||||
|
|
||||||
this.shadow.appendChild(style);
|
this.shadow.appendChild(style);
|
||||||
this.shadow.appendChild(this.navZone);
|
this.shadow.appendChild(layout);
|
||||||
this.shadow.appendChild(this.contentZone);
|
|
||||||
this.shadow.appendChild(this.metaZone);
|
// Mobile sidebar toggle
|
||||||
|
const mobileToggle = document.createElement('button');
|
||||||
|
mobileToggle.className = 'mobile-sidebar-toggle';
|
||||||
|
mobileToggle.innerHTML = '\u2630';
|
||||||
|
mobileToggle.addEventListener('click', () => {
|
||||||
|
this.sidebarOpen = !this.sidebarOpen;
|
||||||
|
this.navZone.querySelector('.notes-sidebar')?.classList.toggle('open', this.sidebarOpen);
|
||||||
|
this.shadow.querySelector('.sidebar-overlay')?.classList.toggle('open', this.sidebarOpen);
|
||||||
|
});
|
||||||
|
this.shadow.appendChild(mobileToggle);
|
||||||
|
|
||||||
|
// Mobile overlay
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'sidebar-overlay';
|
||||||
|
overlay.addEventListener('click', () => {
|
||||||
|
this.sidebarOpen = false;
|
||||||
|
this.navZone.querySelector('.notes-sidebar')?.classList.remove('open');
|
||||||
|
overlay.classList.remove('open');
|
||||||
|
});
|
||||||
|
this.shadow.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Demo data ──
|
// ── Demo data ──
|
||||||
|
|
@ -429,6 +463,13 @@ Gear: EUR 400 (10%)</code></pre><p><em>Maya is tracking expenses in rF
|
||||||
];
|
];
|
||||||
|
|
||||||
this.notebooks = this.demoNotebooks.map(({ notes, ...nb }) => nb as Notebook);
|
this.notebooks = this.demoNotebooks.map(({ notes, ...nb }) => nb as Notebook);
|
||||||
|
// Populate sidebar note cache and expand first notebook
|
||||||
|
for (const nb of this.demoNotebooks) {
|
||||||
|
this.notebookNotes.set(nb.id, nb.notes);
|
||||||
|
}
|
||||||
|
if (this.demoNotebooks.length > 0) {
|
||||||
|
this.expandedNotebooks.add(this.demoNotebooks[0].id);
|
||||||
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -401,6 +401,126 @@
|
||||||
background: rgba(20, 184, 166, 0.1);
|
background: rgba(20, 184, 166, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Platform selector chips ── */
|
||||||
|
.cw-platform-selector {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-platform-selector__label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--rs-text-muted, #64748b);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-platform-selector__chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-platform-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0.35rem 0.7rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid var(--rs-border, #444);
|
||||||
|
background: transparent;
|
||||||
|
color: var(--rs-text-muted, #64748b);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-platform-chip:hover {
|
||||||
|
border-color: var(--rs-accent, #14b8a6);
|
||||||
|
color: var(--rs-text-primary, #e1e1e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-platform-chip--active {
|
||||||
|
background: rgba(20, 184, 166, 0.15);
|
||||||
|
border-color: var(--rs-accent, #14b8a6);
|
||||||
|
color: var(--rs-accent, #14b8a6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Clickable phase card headers ── */
|
||||||
|
.cw-phase-card__header--clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-card__header--clickable:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Phase chevron ── */
|
||||||
|
.cw-phase-chevron {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
color: var(--rs-text-muted, #64748b);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-chevron--open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Phase expanded preview ── */
|
||||||
|
.cw-phase-card__preview {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
border-top: 1px solid var(--rs-border, #333);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-preview-post {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--rs-text-secondary, #94a3b8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-preview-post__spread {
|
||||||
|
color: var(--rs-text-muted, #64748b);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Phase instructions textarea ── */
|
||||||
|
.cw-phase-instructions {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 56px;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border: 1px solid var(--rs-input-border, #334155);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--rs-bg-surface, #1e1e2e);
|
||||||
|
color: var(--rs-text-primary, #e1e1e1);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: vertical;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-instructions:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--rs-accent, #14b8a6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-phase-instructions::placeholder {
|
||||||
|
color: var(--rs-text-muted, #64748b);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Responsive ── */
|
/* ── Responsive ── */
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.cw-brief-summary {
|
.cw-brief-summary {
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
private _expandedPosts: Set<number> = new Set();
|
private _expandedPosts: Set<number> = new Set();
|
||||||
private _regenIndex = -1;
|
private _regenIndex = -1;
|
||||||
private _regenInstructions = '';
|
private _regenInstructions = '';
|
||||||
|
private _selectedPlatforms: Set<string> = new Set(['x', 'linkedin', 'instagram', 'youtube', 'threads', 'bluesky', 'newsletter']);
|
||||||
|
private _expandedPhases: Set<number> = new Set();
|
||||||
|
private _phaseInstructions: Map<number, string> = new Map();
|
||||||
|
|
||||||
static get observedAttributes() { return ['space', 'wizard-id']; }
|
static get observedAttributes() { return ['space', 'wizard-id']; }
|
||||||
|
|
||||||
|
|
@ -155,6 +158,11 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
this._campaignDraft = data.campaignDraft;
|
this._campaignDraft = data.campaignDraft;
|
||||||
this._committedCampaignId = data.committedCampaignId;
|
this._committedCampaignId = data.committedCampaignId;
|
||||||
|
|
||||||
|
// Sync selected platforms from extracted brief
|
||||||
|
if (data.extractedBrief?.platforms?.length) {
|
||||||
|
this._selectedPlatforms = new Set(data.extractedBrief.platforms);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.step === 'committed') {
|
if (data.step === 'committed') {
|
||||||
this._step = 'activate';
|
this._step = 'activate';
|
||||||
} else if (data.step === 'abandoned') {
|
} else if (data.step === 'abandoned') {
|
||||||
|
|
@ -212,7 +220,7 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
try {
|
try {
|
||||||
const res = await this.apiFetch(`/api/campaign/wizard/${this._wizardId}/structure`, {
|
const res = await this.apiFetch(`/api/campaign/wizard/${this._wizardId}/structure`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ rawBrief: this._rawBrief }),
|
body: JSON.stringify({ rawBrief: this._rawBrief, selectedPlatforms: Array.from(this._selectedPlatforms) }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Analysis failed');
|
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Analysis failed');
|
||||||
const data: WizardState = await res.json();
|
const data: WizardState = await res.json();
|
||||||
|
|
@ -232,9 +240,11 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const phaseInstructions: Record<number, string> = {};
|
||||||
|
this._phaseInstructions.forEach((v, k) => { if (v.trim()) phaseInstructions[k] = v; });
|
||||||
const res = await this.apiFetch(`/api/campaign/wizard/${this._wizardId}/content`, {
|
const res = await this.apiFetch(`/api/campaign/wizard/${this._wizardId}/content`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({}),
|
body: JSON.stringify({ phaseInstructions }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Generation failed');
|
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Generation failed');
|
||||||
const data: WizardState = await res.json();
|
const data: WizardState = await res.json();
|
||||||
|
|
@ -367,10 +377,20 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderBriefStep(): string {
|
private renderBriefStep(): string {
|
||||||
|
const allPlatforms = ['x', 'linkedin', 'instagram', 'youtube', 'threads', 'bluesky', 'newsletter'];
|
||||||
|
const platformChips = allPlatforms.map(p => {
|
||||||
|
const active = this._selectedPlatforms.has(p);
|
||||||
|
return `<button class="cw-platform-chip ${active ? 'cw-platform-chip--active' : ''}" data-platform="${p}">${PLATFORM_ICONS[p] || ''} ${p}</button>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
return `<div class="cw-panel">
|
return `<div class="cw-panel">
|
||||||
<h2>Step 1: Paste Your Campaign Brief</h2>
|
<h2>Step 1: Paste Your Campaign Brief</h2>
|
||||||
<p class="cw-hint">Paste raw text describing your event, product launch, or campaign goals. The AI will extract key details and propose a structure.</p>
|
<p class="cw-hint">Paste raw text describing your event, product launch, or campaign goals. The AI will extract key details and propose a structure.</p>
|
||||||
<textarea class="cw-textarea" id="brief-input" placeholder="Example: We're launching MycoFi at ETHDenver on March 25, 2026. Target audience is web3 developers and DeFi enthusiasts. We want to build hype for 2 weeks before, have a strong presence during the 3-day event, and follow up for a week after...">${this.escHtml(this._rawBrief)}</textarea>
|
<textarea class="cw-textarea" id="brief-input" placeholder="Example: We're launching MycoFi at ETHDenver on March 25, 2026. Target audience is web3 developers and DeFi enthusiasts. We want to build hype for 2 weeks before, have a strong presence during the 3-day event, and follow up for a week after...">${this.escHtml(this._rawBrief)}</textarea>
|
||||||
|
<div class="cw-platform-selector">
|
||||||
|
<span class="cw-platform-selector__label">Include platforms:</span>
|
||||||
|
<div class="cw-platform-selector__chips">${platformChips}</div>
|
||||||
|
</div>
|
||||||
<div class="cw-actions">
|
<div class="cw-actions">
|
||||||
<button class="cw-btn cw-btn--primary" id="analyze-btn" ${this._rawBrief.trim().length < 10 ? 'disabled' : ''}>Analyze Brief</button>
|
<button class="cw-btn cw-btn--primary" id="analyze-btn" ${this._rawBrief.trim().length < 10 ? 'disabled' : ''}>Analyze Brief</button>
|
||||||
<button class="cw-btn cw-btn--ghost" id="mi-btn">Refine with MI</button>
|
<button class="cw-btn cw-btn--ghost" id="mi-btn">Refine with MI</button>
|
||||||
|
|
@ -394,19 +414,48 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const phases = structure.phases.map(p => `
|
const phases = structure.phases.map((p, pi) => {
|
||||||
|
const isExpanded = this._expandedPhases.has(pi);
|
||||||
|
const chevronCls = isExpanded ? 'cw-phase-chevron cw-phase-chevron--open' : 'cw-phase-chevron';
|
||||||
|
const instructions = this._phaseInstructions.get(pi) || '';
|
||||||
|
|
||||||
|
// Infer post type per platform
|
||||||
|
const postTypeMap: Record<string, string> = {
|
||||||
|
x: 'thread', linkedin: 'text', instagram: 'carousel',
|
||||||
|
youtube: 'video', threads: 'thread', bluesky: 'text', newsletter: 'email',
|
||||||
|
};
|
||||||
|
|
||||||
|
const postStubs = Object.entries(p.cadence || {}).map(([plat, count]) =>
|
||||||
|
`<div class="cw-phase-preview-post">
|
||||||
|
<span class="cw-cadence-badge">${PLATFORM_ICONS[plat] || ''} ${plat}</span>
|
||||||
|
<span>${count} ${postTypeMap[plat] || 'text'} post${count !== 1 ? 's' : ''}</span>
|
||||||
|
<span class="cw-phase-preview-post__spread">spread over ${this.escHtml(p.days)}</span>
|
||||||
|
</div>`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
return `
|
||||||
<div class="cw-phase-card">
|
<div class="cw-phase-card">
|
||||||
<div class="cw-phase-card__header">
|
<div class="cw-phase-card__header cw-phase-card__header--clickable" data-phase-toggle="${pi}">
|
||||||
<span class="cw-phase-card__name">${this.escHtml(p.label)}</span>
|
<span class="cw-phase-card__name">${this.escHtml(p.label)}</span>
|
||||||
<span class="cw-phase-card__days">${this.escHtml(p.days)}</span>
|
<span style="display:flex;align-items:center;gap:0.5rem">
|
||||||
|
<span class="cw-phase-card__days">${this.escHtml(p.days)}</span>
|
||||||
|
<span class="${chevronCls}">\u25B6</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="cw-phase-card__cadence">
|
<div class="cw-phase-card__cadence">
|
||||||
${Object.entries(p.cadence || {}).map(([plat, count]) =>
|
${Object.entries(p.cadence || {}).map(([plat, count]) =>
|
||||||
`<span class="cw-cadence-badge">${PLATFORM_ICONS[plat] || ''} ${plat}: ${count} post${count !== 1 ? 's' : ''}</span>`
|
`<span class="cw-cadence-badge">${PLATFORM_ICONS[plat] || ''} ${plat}: ${count} post${count !== 1 ? 's' : ''}</span>`
|
||||||
).join('')}
|
).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
${isExpanded ? `
|
||||||
|
<div class="cw-phase-card__preview">
|
||||||
|
${postStubs}
|
||||||
|
<textarea class="cw-phase-instructions" data-phase-instructions="${pi}" placeholder="Optional: guide the AI for this phase (e.g. 'focus on countdown energy')">${this.escHtml(instructions)}</textarea>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="cw-panel">
|
<div class="cw-panel">
|
||||||
|
|
@ -567,6 +616,21 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Platform chip toggles
|
||||||
|
sr.querySelectorAll('[data-platform]').forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const plat = el.getAttribute('data-platform')!;
|
||||||
|
if (this._selectedPlatforms.has(plat)) {
|
||||||
|
if (this._selectedPlatforms.size > 1) {
|
||||||
|
this._selectedPlatforms.delete(plat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._selectedPlatforms.add(plat);
|
||||||
|
}
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
sr.querySelector('#analyze-btn')?.addEventListener('click', () => this.analyzebrief());
|
sr.querySelector('#analyze-btn')?.addEventListener('click', () => this.analyzebrief());
|
||||||
sr.querySelector('#mi-btn')?.addEventListener('click', () => {
|
sr.querySelector('#mi-btn')?.addEventListener('click', () => {
|
||||||
this.dispatchEvent(new CustomEvent('mi-prompt', {
|
this.dispatchEvent(new CustomEvent('mi-prompt', {
|
||||||
|
|
@ -575,7 +639,24 @@ export class FolkCampaignWizard extends HTMLElement {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Structure step
|
// Structure step — phase card expand/collapse
|
||||||
|
sr.querySelectorAll('[data-phase-toggle]').forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const pi = parseInt(el.getAttribute('data-phase-toggle')!);
|
||||||
|
if (this._expandedPhases.has(pi)) this._expandedPhases.delete(pi);
|
||||||
|
else this._expandedPhases.add(pi);
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
sr.querySelectorAll('[data-phase-instructions]').forEach(el => {
|
||||||
|
const ta = el as HTMLTextAreaElement;
|
||||||
|
ta.addEventListener('click', (e) => e.stopPropagation());
|
||||||
|
ta.addEventListener('input', () => {
|
||||||
|
const pi = parseInt(ta.getAttribute('data-phase-instructions')!);
|
||||||
|
this._phaseInstructions.set(pi, ta.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
sr.querySelector('#approve-structure-btn')?.addEventListener('click', () => this.generateContent());
|
sr.querySelector('#approve-structure-btn')?.addEventListener('click', () => this.generateContent());
|
||||||
sr.querySelector('#regen-structure-btn')?.addEventListener('click', () => this.analyzebrief());
|
sr.querySelector('#regen-structure-btn')?.addEventListener('click', () => this.analyzebrief());
|
||||||
sr.querySelector('#back-to-brief-btn')?.addEventListener('click', () => { this._step = 'brief'; this.render(); });
|
sr.querySelector('#back-to-brief-btn')?.addEventListener('click', () => { this._step = 'brief'; this.render(); });
|
||||||
|
|
|
||||||
|
|
@ -1241,6 +1241,13 @@ routes.post("/api/campaign/wizard/:id/structure", async (c) => {
|
||||||
return c.json({ error: "Brief is required (min 10 characters)" }, 400);
|
return c.json({ error: "Brief is required (min 10 characters)" }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedPlatforms: string[] = Array.isArray(body.selectedPlatforms) && body.selectedPlatforms.length > 0
|
||||||
|
? body.selectedPlatforms
|
||||||
|
: null;
|
||||||
|
const platformConstraint = selectedPlatforms
|
||||||
|
? `\n- ONLY use these platforms: ${selectedPlatforms.join(', ')}. Do NOT include any other platforms.`
|
||||||
|
: '';
|
||||||
|
|
||||||
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
||||||
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
|
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
|
||||||
const model = genAI.getGenerativeModel({
|
const model = genAI.getGenerativeModel({
|
||||||
|
|
@ -1284,7 +1291,7 @@ Return JSON with this exact shape:
|
||||||
Rules:
|
Rules:
|
||||||
- Generate 3-5 phases (pre-launch, launch, amplification, follow-up, etc.)
|
- Generate 3-5 phases (pre-launch, launch, amplification, follow-up, etc.)
|
||||||
- Cadence = number of posts per platform for that phase
|
- Cadence = number of posts per platform for that phase
|
||||||
- Platforms should be realistic for the brief content
|
- Platforms should be realistic for the brief content${platformConstraint}
|
||||||
- Use today's date (${new Date().toISOString().split('T')[0]}) as reference for dates`;
|
- Use today's date (${new Date().toISOString().split('T')[0]}) as reference for dates`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -1292,6 +1299,25 @@ Rules:
|
||||||
const text = result.response.text();
|
const text = result.response.text();
|
||||||
const parsed = JSON.parse(text);
|
const parsed = JSON.parse(text);
|
||||||
|
|
||||||
|
// Safety net: filter platforms and cadences to only selected platforms
|
||||||
|
if (selectedPlatforms && parsed.extractedBrief) {
|
||||||
|
parsed.extractedBrief.platforms = (parsed.extractedBrief.platforms || [])
|
||||||
|
.filter((p: string) => selectedPlatforms.includes(p));
|
||||||
|
if (parsed.extractedBrief.platforms.length === 0) {
|
||||||
|
parsed.extractedBrief.platforms = selectedPlatforms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedPlatforms && parsed.structure?.phases) {
|
||||||
|
for (const phase of parsed.structure.phases) {
|
||||||
|
phase.platforms = (phase.platforms || []).filter((p: string) => selectedPlatforms.includes(p));
|
||||||
|
if (phase.cadence) {
|
||||||
|
for (const key of Object.keys(phase.cadence)) {
|
||||||
|
if (!selectedPlatforms.includes(key)) delete phase.cadence[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_syncServer!.changeDoc<SocialsDoc>(docId, `wizard ${id} → structure`, (d) => {
|
_syncServer!.changeDoc<SocialsDoc>(docId, `wizard ${id} → structure`, (d) => {
|
||||||
const w = d.campaignWizards?.[id];
|
const w = d.campaignWizards?.[id];
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
@ -1325,10 +1351,19 @@ routes.post("/api/campaign/wizard/:id/content", async (c) => {
|
||||||
return c.json({ error: "Must complete structure step first" }, 400);
|
return c.json({ error: "Must complete structure step first" }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = await c.req.json().catch(() => ({}));
|
||||||
|
const phaseInstructions: Record<string, string> = body.phaseInstructions || {};
|
||||||
|
|
||||||
const brief = wizard.extractedBrief;
|
const brief = wizard.extractedBrief;
|
||||||
const structure = wizard.structure;
|
const structure = wizard.structure;
|
||||||
const selectedPlatforms = brief.platforms.length > 0 ? brief.platforms : ["x", "linkedin", "instagram", "newsletter"];
|
const selectedPlatforms = brief.platforms.length > 0 ? brief.platforms : ["x", "linkedin", "instagram", "newsletter"];
|
||||||
|
|
||||||
|
// Build phase-specific instruction hints
|
||||||
|
const phaseHints = structure.phases.map((p, i) => {
|
||||||
|
const hint = phaseInstructions[String(i)];
|
||||||
|
return hint ? `- Phase ${i + 1} (${p.label}): ${hint}` : '';
|
||||||
|
}).filter(Boolean).join('\n');
|
||||||
|
|
||||||
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
||||||
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
|
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
|
||||||
const model = genAI.getGenerativeModel({
|
const model = genAI.getGenerativeModel({
|
||||||
|
|
@ -1391,7 +1426,7 @@ Rules:
|
||||||
- For newsletter, include emailSubject + emailHtml with inline CSS
|
- For newsletter, include emailSubject + emailHtml with inline CSS
|
||||||
- scheduledAt dates should spread across the phase day ranges, during working hours (9am-5pm)
|
- scheduledAt dates should spread across the phase day ranges, during working hours (9am-5pm)
|
||||||
- Content must reference specific details from the brief key messages
|
- Content must reference specific details from the brief key messages
|
||||||
- Respect each platform's character limits`;
|
- Respect each platform's character limits${phaseHints ? `\n\nPhase-specific instructions from the user:\n${phaseHints}` : ''}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await model.generateContent(prompt);
|
const result = await model.generateContent(prompt);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
"@google/genai": "^1.43.0",
|
"@google/genai": "^1.43.0",
|
||||||
"@google/generative-ai": "^0.24.1",
|
"@google/generative-ai": "^0.24.1",
|
||||||
"@lit/reactive-element": "^2.0.4",
|
"@lit/reactive-element": "^2.0.4",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
"@noble/curves": "^1.8.0",
|
"@noble/curves": "^1.8.0",
|
||||||
"@noble/hashes": "^1.7.0",
|
"@noble/hashes": "^1.7.0",
|
||||||
"@openfort/openfort-node": "^0.7.0",
|
"@openfort/openfort-node": "^0.7.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
/**
|
||||||
|
* CAD Orchestrator — LLM-driven MCP tool calling for KiCad and FreeCAD
|
||||||
|
*
|
||||||
|
* Converts MCP tool schemas to Gemini function declarations, runs an agentic
|
||||||
|
* loop where Gemini Flash plans and executes real MCP tool sequences, then
|
||||||
|
* assembles artifacts (SVGs, exports) for the frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||||
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
|
// ── MCP → Gemini schema conversion ──
|
||||||
|
|
||||||
|
/** Strip keys that Gemini function-calling schema rejects */
|
||||||
|
function cleanSchema(obj: any): any {
|
||||||
|
if (obj === null || obj === undefined) return obj;
|
||||||
|
if (Array.isArray(obj)) return obj.map(cleanSchema);
|
||||||
|
if (typeof obj !== "object") return obj;
|
||||||
|
|
||||||
|
const out: any = {};
|
||||||
|
for (const [k, v] of Object.entries(obj)) {
|
||||||
|
// Gemini rejects these in FunctionDeclaration parameters
|
||||||
|
if (["default", "additionalProperties", "$schema", "examples", "title"].includes(k)) continue;
|
||||||
|
out[k] = cleanSchema(v);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a single MCP Tool to a Gemini FunctionDeclaration */
|
||||||
|
export function mcpToolToGeminiFn(tool: Tool): any {
|
||||||
|
const params = tool.inputSchema ? cleanSchema(tool.inputSchema) : { type: "object", properties: {} };
|
||||||
|
// Gemini requires type:"OBJECT" (uppercase) in some SDK versions, but @google/generative-ai handles lowercase
|
||||||
|
return {
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description || tool.name,
|
||||||
|
parameters: params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Extract text from MCP CallToolResult ──
|
||||||
|
|
||||||
|
export function extractMcpText(result: any): string {
|
||||||
|
const content = result?.content;
|
||||||
|
if (!Array.isArray(content)) return JSON.stringify(result);
|
||||||
|
return content
|
||||||
|
.filter((c: any) => c.type === "text")
|
||||||
|
.map((c: any) => c.text || "")
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Agentic loop ──
|
||||||
|
|
||||||
|
export interface ToolCallLogEntry {
|
||||||
|
tool: string;
|
||||||
|
args: Record<string, any>;
|
||||||
|
result: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrchestrationResult {
|
||||||
|
artifacts: Map<string, string>;
|
||||||
|
finalMessage: string;
|
||||||
|
toolCallLog: ToolCallLogEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runCadAgentLoop(
|
||||||
|
client: Client,
|
||||||
|
systemPrompt: string,
|
||||||
|
userPrompt: string,
|
||||||
|
geminiApiKey: string,
|
||||||
|
maxTurns = 8,
|
||||||
|
): Promise<OrchestrationResult> {
|
||||||
|
// 1. Fetch real tool schemas from MCP server
|
||||||
|
const { tools } = await client.listTools();
|
||||||
|
const functionDeclarations = tools.map(mcpToolToGeminiFn);
|
||||||
|
|
||||||
|
// 2. Initialize Gemini Flash with function calling
|
||||||
|
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
||||||
|
const genAI = new GoogleGenerativeAI(geminiApiKey);
|
||||||
|
const model = genAI.getGenerativeModel({
|
||||||
|
model: "gemini-2.5-flash",
|
||||||
|
systemInstruction: systemPrompt,
|
||||||
|
generationConfig: { temperature: 0.2 },
|
||||||
|
tools: [{ functionDeclarations }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Agentic loop
|
||||||
|
const artifacts = new Map<string, string>();
|
||||||
|
const toolCallLog: ToolCallLogEntry[] = [];
|
||||||
|
const deadline = Date.now() + 60_000;
|
||||||
|
|
||||||
|
let contents: any[] = [
|
||||||
|
{ role: "user", parts: [{ text: userPrompt }] },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let turn = 0; turn < maxTurns; turn++) {
|
||||||
|
if (Date.now() > deadline) {
|
||||||
|
console.warn("[cad-orchestrator] 60s deadline reached");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await model.generateContent({ contents });
|
||||||
|
const candidate = result.response.candidates?.[0];
|
||||||
|
if (!candidate) break;
|
||||||
|
|
||||||
|
const parts = candidate.content?.parts || [];
|
||||||
|
const fnCalls = parts.filter((p: any) => p.functionCall);
|
||||||
|
|
||||||
|
if (fnCalls.length === 0) {
|
||||||
|
// Done — extract final text
|
||||||
|
const finalText = parts
|
||||||
|
.filter((p: any) => p.text)
|
||||||
|
.map((p: any) => p.text)
|
||||||
|
.join("\n");
|
||||||
|
return { artifacts, finalMessage: finalText || "Design complete.", toolCallLog };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute each function call on MCP
|
||||||
|
const fnResponseParts: any[] = [];
|
||||||
|
for (const part of fnCalls) {
|
||||||
|
const fc = part.functionCall!;
|
||||||
|
console.log(`[cad-orchestrator] Turn ${turn + 1}: ${fc.name}(${JSON.stringify(fc.args).slice(0, 200)})`);
|
||||||
|
|
||||||
|
let mcpResultText: string;
|
||||||
|
try {
|
||||||
|
const mcpResult = await client.callTool({
|
||||||
|
name: fc.name,
|
||||||
|
arguments: (fc.args || {}) as Record<string, unknown>,
|
||||||
|
});
|
||||||
|
mcpResultText = extractMcpText(mcpResult);
|
||||||
|
} catch (err) {
|
||||||
|
mcpResultText = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolCallLog.push({ tool: fc.name, args: fc.args || {}, result: mcpResultText });
|
||||||
|
artifacts.set(fc.name, mcpResultText);
|
||||||
|
|
||||||
|
fnResponseParts.push({
|
||||||
|
functionResponse: {
|
||||||
|
name: fc.name,
|
||||||
|
response: { result: mcpResultText },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed results back for next turn
|
||||||
|
contents.push({ role: "model", parts });
|
||||||
|
contents.push({ role: "user", parts: fnResponseParts });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
artifacts,
|
||||||
|
finalMessage: "Design generation completed (max turns reached).",
|
||||||
|
toolCallLog,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Result assemblers ──
|
||||||
|
|
||||||
|
export interface KicadResult {
|
||||||
|
schematicSvg: string | null;
|
||||||
|
boardSvg: string | null;
|
||||||
|
gerberUrl: string | null;
|
||||||
|
bomUrl: string | null;
|
||||||
|
pdfUrl: string | null;
|
||||||
|
drcResults: { violations: string[] } | null;
|
||||||
|
summary: string;
|
||||||
|
toolCallLog: ToolCallLogEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assembleKicadResult(orch: OrchestrationResult): KicadResult {
|
||||||
|
let schematicSvg: string | null = null;
|
||||||
|
let boardSvg: string | null = null;
|
||||||
|
let gerberUrl: string | null = null;
|
||||||
|
let bomUrl: string | null = null;
|
||||||
|
let pdfUrl: string | null = null;
|
||||||
|
let drcResults: { violations: string[] } | null = null;
|
||||||
|
|
||||||
|
for (const entry of orch.toolCallLog) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(entry.result);
|
||||||
|
switch (entry.tool) {
|
||||||
|
case "export_svg":
|
||||||
|
// Could be schematic or board SVG — check args or content
|
||||||
|
if (entry.args.type === "board" || entry.args.board) {
|
||||||
|
boardSvg = parsed.svg_path || parsed.path || parsed.url || null;
|
||||||
|
} else {
|
||||||
|
schematicSvg = parsed.svg_path || parsed.path || parsed.url || null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "run_drc":
|
||||||
|
drcResults = {
|
||||||
|
violations: parsed.violations || parsed.errors || [],
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "export_gerber":
|
||||||
|
gerberUrl = parsed.gerber_path || parsed.path || parsed.url || null;
|
||||||
|
break;
|
||||||
|
case "export_bom":
|
||||||
|
bomUrl = parsed.bom_path || parsed.path || parsed.url || null;
|
||||||
|
break;
|
||||||
|
case "export_pdf":
|
||||||
|
pdfUrl = parsed.pdf_path || parsed.path || parsed.url || null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Non-JSON results are fine (intermediate steps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
schematicSvg,
|
||||||
|
boardSvg,
|
||||||
|
gerberUrl,
|
||||||
|
bomUrl,
|
||||||
|
pdfUrl,
|
||||||
|
drcResults,
|
||||||
|
summary: orch.finalMessage,
|
||||||
|
toolCallLog: orch.toolCallLog,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FreecadResult {
|
||||||
|
previewUrl: string | null;
|
||||||
|
stepUrl: string | null;
|
||||||
|
stlUrl: string | null;
|
||||||
|
summary: string;
|
||||||
|
toolCallLog: ToolCallLogEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assembleFreecadResult(orch: OrchestrationResult): FreecadResult {
|
||||||
|
let previewUrl: string | null = null;
|
||||||
|
let stepUrl: string | null = null;
|
||||||
|
let stlUrl: string | null = null;
|
||||||
|
|
||||||
|
for (const entry of orch.toolCallLog) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(entry.result);
|
||||||
|
// FreeCAD exports via execute_python_script — look for file paths in results
|
||||||
|
if (entry.tool === "execute_python_script" || entry.tool === "execute_script") {
|
||||||
|
const text = entry.result.toLowerCase();
|
||||||
|
if (text.includes(".step") || text.includes(".stp")) {
|
||||||
|
stepUrl = parsed.path || parsed.file_path || extractPathFromText(entry.result, [".step", ".stp"]);
|
||||||
|
}
|
||||||
|
if (text.includes(".stl")) {
|
||||||
|
stlUrl = parsed.path || parsed.file_path || extractPathFromText(entry.result, [".stl"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save_document may also produce a path
|
||||||
|
if (entry.tool === "save_document") {
|
||||||
|
const path = parsed.path || parsed.file_path || null;
|
||||||
|
if (path && (path.endsWith(".FCStd") || path.endsWith(".fcstd"))) {
|
||||||
|
// Not directly servable, but note it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Try extracting paths from raw text
|
||||||
|
stepUrl = stepUrl || extractPathFromText(entry.result, [".step", ".stp"]);
|
||||||
|
stlUrl = stlUrl || extractPathFromText(entry.result, [".stl"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
previewUrl,
|
||||||
|
stepUrl,
|
||||||
|
stlUrl,
|
||||||
|
summary: orch.finalMessage,
|
||||||
|
toolCallLog: orch.toolCallLog,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extract a file path ending with one of the given extensions from text */
|
||||||
|
function extractPathFromText(text: string, extensions: string[]): string | null {
|
||||||
|
for (const ext of extensions) {
|
||||||
|
const regex = new RegExp(`(/[\\w./-]+${ext.replace(".", "\\.")})`, "i");
|
||||||
|
const match = text.match(regex);
|
||||||
|
if (match) return match[1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── System prompts ──
|
||||||
|
|
||||||
|
export const KICAD_SYSTEM_PROMPT = `You are a KiCad PCB design assistant. You have access to KiCad MCP tools to create real PCB designs.
|
||||||
|
|
||||||
|
Follow this workflow:
|
||||||
|
1. create_project — Create a new KiCad project in /tmp/kicad-gen-<timestamp>/
|
||||||
|
2. search_symbols — Find component symbols in KiCad libraries (e.g. ESP32, BME280, capacitors, resistors)
|
||||||
|
3. add_schematic_component — Place each component on the schematic
|
||||||
|
4. add_schematic_net_label — Add net labels for connections
|
||||||
|
5. add_schematic_connection — Wire components together
|
||||||
|
6. generate_netlist — Generate the netlist from schematic
|
||||||
|
7. place_component — Place footprints on the board
|
||||||
|
8. route_trace — Route traces between pads
|
||||||
|
9. export_svg — Export schematic SVG (type: "schematic") and board SVG (type: "board")
|
||||||
|
10. run_drc — Run Design Rule Check
|
||||||
|
11. export_gerber, export_bom, export_pdf — Generate manufacturing outputs
|
||||||
|
|
||||||
|
Important:
|
||||||
|
- Use /tmp/kicad-gen-${Date.now()}/ as the project directory
|
||||||
|
- Search for real symbols before placing components
|
||||||
|
- Add decoupling capacitors and pull-up resistors as needed
|
||||||
|
- Set reasonable board outline dimensions
|
||||||
|
- After placing components, route critical traces
|
||||||
|
- Always run DRC before exporting
|
||||||
|
- If a tool call fails, try an alternative approach rather than repeating the same call`;
|
||||||
|
|
||||||
|
export const FREECAD_SYSTEM_PROMPT = `You are a FreeCAD parametric CAD assistant. You have access to FreeCAD MCP tools to create real 3D models.
|
||||||
|
|
||||||
|
Follow this workflow:
|
||||||
|
1. execute_python_script — Create output directory: import os; os.makedirs("/tmp/freecad-gen-<timestamp>", exist_ok=True)
|
||||||
|
2. Create base geometry using create_box, create_cylinder, or create_sphere
|
||||||
|
3. Use boolean_operation (union, cut, intersection) to combine shapes
|
||||||
|
4. list_objects to verify the model state
|
||||||
|
5. save_document to save the FreeCAD file
|
||||||
|
6. execute_python_script to export STEP: Part.export([obj], "/tmp/freecad-gen-<id>/model.step")
|
||||||
|
7. execute_python_script to export STL: Mesh.export([obj], "/tmp/freecad-gen-<id>/model.stl")
|
||||||
|
|
||||||
|
Important:
|
||||||
|
- Use /tmp/freecad-gen-${Date.now()}/ as the working directory
|
||||||
|
- For hollow objects, create the outer shell then cut the inner volume
|
||||||
|
- For complex shapes, build up from primitives with boolean operations
|
||||||
|
- Wall thickness should be at least 1mm for 3D printing
|
||||||
|
- Always export both STEP (for CAD) and STL (for 3D printing)
|
||||||
|
- If a tool call fails, try an alternative approach`;
|
||||||
244
server/index.ts
244
server/index.ts
|
|
@ -1144,6 +1144,20 @@ async function process3DGenJob(job: Gen3DJob) {
|
||||||
|
|
||||||
// ── Image helpers ──
|
// ── Image helpers ──
|
||||||
|
|
||||||
|
/** Copy a file from a tmp path to the served generated directory → return server-relative URL */
|
||||||
|
async function copyToServed(srcPath: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const srcFile = Bun.file(srcPath);
|
||||||
|
if (!(await srcFile.exists())) return null;
|
||||||
|
const basename = srcPath.split("/").pop() || `file-${Date.now()}`;
|
||||||
|
const dir = resolve(process.env.FILES_DIR || "./data/files", "generated");
|
||||||
|
await Bun.write(resolve(dir, basename), srcFile);
|
||||||
|
return `/data/files/generated/${basename}`;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Read a /data/files/generated/... path from disk → base64 */
|
/** Read a /data/files/generated/... path from disk → base64 */
|
||||||
async function readFileAsBase64(serverPath: string): Promise<string> {
|
async function readFileAsBase64(serverPath: string): Promise<string> {
|
||||||
const filename = serverPath.split("/").pop();
|
const filename = serverPath.split("/").pop();
|
||||||
|
|
@ -1606,6 +1620,19 @@ app.get("/api/3d-gen/:jobId", async (c) => {
|
||||||
// Blender 3D generation via LLM + RunPod
|
// Blender 3D generation via LLM + RunPod
|
||||||
const RUNPOD_API_KEY = process.env.RUNPOD_API_KEY || "";
|
const RUNPOD_API_KEY = process.env.RUNPOD_API_KEY || "";
|
||||||
|
|
||||||
|
app.get("/api/blender-gen/health", async (c) => {
|
||||||
|
const issues: string[] = [];
|
||||||
|
if (!RUNPOD_API_KEY) issues.push("RUNPOD_API_KEY not configured");
|
||||||
|
const OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${OLLAMA_URL}/api/tags`, { signal: AbortSignal.timeout(3000) });
|
||||||
|
if (!res.ok) issues.push("Ollama not responding");
|
||||||
|
} catch {
|
||||||
|
issues.push("Ollama unreachable");
|
||||||
|
}
|
||||||
|
return c.json({ available: issues.length === 0, issues });
|
||||||
|
});
|
||||||
|
|
||||||
app.post("/api/blender-gen", async (c) => {
|
app.post("/api/blender-gen", async (c) => {
|
||||||
if (!RUNPOD_API_KEY) return c.json({ error: "RUNPOD_API_KEY not configured" }, 503);
|
if (!RUNPOD_API_KEY) return c.json({ error: "RUNPOD_API_KEY not configured" }, 503);
|
||||||
|
|
||||||
|
|
@ -1675,8 +1702,89 @@ app.post("/api/blender-gen", async (c) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// KiCAD PCB design — REST-to-MCP bridge
|
// KiCAD PCB design — MCP stdio bridge
|
||||||
const KICAD_MCP_URL = process.env.KICAD_MCP_URL || "http://localhost:3001";
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||||
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||||
|
import { runCadAgentLoop, assembleKicadResult, assembleFreecadResult, KICAD_SYSTEM_PROMPT, FREECAD_SYSTEM_PROMPT } from "./cad-orchestrator";
|
||||||
|
|
||||||
|
const KICAD_MCP_PATH = process.env.KICAD_MCP_PATH || "/home/jeffe/KiCAD-MCP-Server/dist/index.js";
|
||||||
|
let kicadClient: Client | null = null;
|
||||||
|
|
||||||
|
async function getKicadClient(): Promise<Client> {
|
||||||
|
if (kicadClient) return kicadClient;
|
||||||
|
|
||||||
|
const transport = new StdioClientTransport({
|
||||||
|
command: "node",
|
||||||
|
args: [KICAD_MCP_PATH],
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = new Client({ name: "rspace-kicad-bridge", version: "1.0.0" });
|
||||||
|
|
||||||
|
transport.onclose = () => { kicadClient = null; };
|
||||||
|
transport.onerror = () => { kicadClient = null; };
|
||||||
|
|
||||||
|
await client.connect(transport);
|
||||||
|
kicadClient = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/kicad/health", async (c) => {
|
||||||
|
try {
|
||||||
|
const client = await getKicadClient();
|
||||||
|
const tools = await client.listTools();
|
||||||
|
return c.json({ available: true, tools: tools.tools.length });
|
||||||
|
} catch (e) {
|
||||||
|
return c.json({ available: false, error: e instanceof Error ? e.message : "Connection failed" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// KiCAD AI-orchestrated design generation
|
||||||
|
app.post("/api/kicad/generate", async (c) => {
|
||||||
|
if (!GEMINI_API_KEY) return c.json({ error: "GEMINI_API_KEY not configured" }, 503);
|
||||||
|
|
||||||
|
const { prompt, components } = await c.req.json();
|
||||||
|
if (!prompt) return c.json({ error: "prompt required" }, 400);
|
||||||
|
|
||||||
|
const enrichedPrompt = components?.length
|
||||||
|
? `${prompt}\n\nKey components to use: ${components.join(", ")}`
|
||||||
|
: prompt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = await getKicadClient();
|
||||||
|
const orch = await runCadAgentLoop(client, KICAD_SYSTEM_PROMPT, enrichedPrompt, GEMINI_API_KEY);
|
||||||
|
const result = assembleKicadResult(orch);
|
||||||
|
|
||||||
|
// Copy generated files to served directory
|
||||||
|
const filesToCopy = [
|
||||||
|
{ path: result.schematicSvg, key: "schematicSvg" },
|
||||||
|
{ path: result.boardSvg, key: "boardSvg" },
|
||||||
|
{ path: result.gerberUrl, key: "gerberUrl" },
|
||||||
|
{ path: result.bomUrl, key: "bomUrl" },
|
||||||
|
{ path: result.pdfUrl, key: "pdfUrl" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { path, key } of filesToCopy) {
|
||||||
|
if (path && path.startsWith("/tmp/")) {
|
||||||
|
const served = await copyToServed(path);
|
||||||
|
if (served) (result as any)[key] = served;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
schematic_svg: result.schematicSvg,
|
||||||
|
board_svg: result.boardSvg,
|
||||||
|
gerber_url: result.gerberUrl,
|
||||||
|
bom_url: result.bomUrl,
|
||||||
|
pdf_url: result.pdfUrl,
|
||||||
|
drc: result.drcResults,
|
||||||
|
summary: result.summary,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[kicad/generate] error:", e);
|
||||||
|
kicadClient = null;
|
||||||
|
return c.json({ error: e instanceof Error ? e.message : "KiCAD generation failed" }, 502);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post("/api/kicad/:action", async (c) => {
|
app.post("/api/kicad/:action", async (c) => {
|
||||||
const action = c.req.param("action");
|
const action = c.req.param("action");
|
||||||
|
|
@ -1693,38 +1801,102 @@ app.post("/api/kicad/:action", async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mcpRes = await fetch(`${KICAD_MCP_URL}/call-tool`, {
|
const client = await getKicadClient();
|
||||||
method: "POST",
|
const result = await client.callTool({ name: action, arguments: body });
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: action,
|
|
||||||
arguments: body,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!mcpRes.ok) {
|
// Extract text content and parse as JSON if possible
|
||||||
const err = await mcpRes.text();
|
const content = result.content as Array<{ type: string; text?: string }>;
|
||||||
console.error(`[kicad/${action}] MCP error:`, err);
|
const textContent = content
|
||||||
return c.json({ error: `KiCAD action failed: ${action}` }, 502);
|
?.filter((c) => c.type === "text")
|
||||||
|
.map((c) => c.text)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return c.json(JSON.parse(textContent || "{}"));
|
||||||
|
} catch {
|
||||||
|
return c.json({ result: textContent });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await mcpRes.json();
|
|
||||||
return c.json(data);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[kicad/${action}] Connection error:`, e);
|
console.error(`[kicad/${action}] MCP error:`, e);
|
||||||
|
kicadClient = null;
|
||||||
return c.json({ error: "KiCAD MCP server not available" }, 503);
|
return c.json({ error: "KiCAD MCP server not available" }, 503);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// FreeCAD parametric CAD — REST-to-MCP bridge
|
// FreeCAD parametric CAD — MCP stdio bridge
|
||||||
const FREECAD_MCP_URL = process.env.FREECAD_MCP_URL || "http://localhost:3002";
|
const FREECAD_MCP_PATH = process.env.FREECAD_MCP_PATH || "/home/jeffe/freecad-mcp-server/build/index.js";
|
||||||
|
let freecadClient: Client | null = null;
|
||||||
|
|
||||||
|
async function getFreecadClient(): Promise<Client> {
|
||||||
|
if (freecadClient) return freecadClient;
|
||||||
|
|
||||||
|
const transport = new StdioClientTransport({
|
||||||
|
command: "node",
|
||||||
|
args: [FREECAD_MCP_PATH],
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = new Client({ name: "rspace-freecad-bridge", version: "1.0.0" });
|
||||||
|
|
||||||
|
transport.onclose = () => { freecadClient = null; };
|
||||||
|
transport.onerror = () => { freecadClient = null; };
|
||||||
|
|
||||||
|
await client.connect(transport);
|
||||||
|
freecadClient = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/freecad/health", async (c) => {
|
||||||
|
try {
|
||||||
|
const client = await getFreecadClient();
|
||||||
|
const tools = await client.listTools();
|
||||||
|
return c.json({ available: true, tools: tools.tools.length });
|
||||||
|
} catch (e) {
|
||||||
|
return c.json({ available: false, error: e instanceof Error ? e.message : "Connection failed" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// FreeCAD AI-orchestrated CAD generation
|
||||||
|
app.post("/api/freecad/generate", async (c) => {
|
||||||
|
if (!GEMINI_API_KEY) return c.json({ error: "GEMINI_API_KEY not configured" }, 503);
|
||||||
|
|
||||||
|
const { prompt } = await c.req.json();
|
||||||
|
if (!prompt) return c.json({ error: "prompt required" }, 400);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = await getFreecadClient();
|
||||||
|
const orch = await runCadAgentLoop(client, FREECAD_SYSTEM_PROMPT, prompt, GEMINI_API_KEY);
|
||||||
|
const result = assembleFreecadResult(orch);
|
||||||
|
|
||||||
|
// Copy generated files to served directory
|
||||||
|
for (const key of ["stepUrl", "stlUrl"] as const) {
|
||||||
|
const path = result[key];
|
||||||
|
if (path && path.startsWith("/tmp/")) {
|
||||||
|
const served = await copyToServed(path);
|
||||||
|
if (served) (result as any)[key] = served;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
preview_url: result.previewUrl,
|
||||||
|
step_url: result.stepUrl,
|
||||||
|
stl_url: result.stlUrl,
|
||||||
|
summary: result.summary,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[freecad/generate] error:", e);
|
||||||
|
freecadClient = null;
|
||||||
|
return c.json({ error: e instanceof Error ? e.message : "FreeCAD generation failed" }, 502);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post("/api/freecad/:action", async (c) => {
|
app.post("/api/freecad/:action", async (c) => {
|
||||||
const action = c.req.param("action");
|
const action = c.req.param("action");
|
||||||
const body = await c.req.json();
|
const body = await c.req.json();
|
||||||
|
|
||||||
const validActions = [
|
const validActions = [
|
||||||
"generate", "export_step", "export_stl", "update_parameters",
|
"generate", "create_box", "create_cylinder", "create_sphere",
|
||||||
|
"boolean_operation", "save_document", "list_objects", "execute_script",
|
||||||
|
"export_step", "export_stl", "update_parameters",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!validActions.includes(action)) {
|
if (!validActions.includes(action)) {
|
||||||
|
|
@ -1732,25 +1904,23 @@ app.post("/api/freecad/:action", async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mcpRes = await fetch(`${FREECAD_MCP_URL}/call-tool`, {
|
const client = await getFreecadClient();
|
||||||
method: "POST",
|
const result = await client.callTool({ name: action, arguments: body });
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: action,
|
|
||||||
arguments: body,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!mcpRes.ok) {
|
const fcContent = result.content as Array<{ type: string; text?: string }>;
|
||||||
const err = await mcpRes.text();
|
const textContent = fcContent
|
||||||
console.error(`[freecad/${action}] MCP error:`, err);
|
?.filter((c) => c.type === "text")
|
||||||
return c.json({ error: `FreeCAD action failed: ${action}` }, 502);
|
.map((c) => c.text)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return c.json(JSON.parse(textContent || "{}"));
|
||||||
|
} catch {
|
||||||
|
return c.json({ result: textContent });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await mcpRes.json();
|
|
||||||
return c.json(data);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[freecad/${action}] Connection error:`, e);
|
console.error(`[freecad/${action}] MCP error:`, e);
|
||||||
|
freecadClient = null;
|
||||||
return c.json({ error: "FreeCAD MCP server not available" }, 503);
|
return c.json({ error: "FreeCAD MCP server not available" }, 503);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue