Compare commits
1 Commits
feat/insta
...
docs/insta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6117f1a4a |
@@ -72,17 +72,11 @@
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/testing": "^11.1.18",
|
||||
"@swc/core": "^1.15.24",
|
||||
"@swc/helpers": "^0.5.21",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/supertest": "^7.2.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"supertest": "^7.2.2",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.8.0",
|
||||
"unplugin-swc": "^1.5.9",
|
||||
"vitest": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ import type { Auth } from '@mosaicstack/auth';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { AUTH } from '../auth/auth.tokens.js';
|
||||
import { DB } from '../database/database.module.js';
|
||||
import { BootstrapSetupDto } from './bootstrap.dto.js';
|
||||
import type { BootstrapStatusDto, BootstrapResultDto } from './bootstrap.dto.js';
|
||||
import type { BootstrapSetupDto, BootstrapStatusDto, BootstrapResultDto } from './bootstrap.dto.js';
|
||||
|
||||
@Controller('api/bootstrap')
|
||||
export class BootstrapController {
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
/**
|
||||
* E2E integration test — POST /api/bootstrap/setup
|
||||
*
|
||||
* Regression guard for the `import type { BootstrapSetupDto }` class-erasure
|
||||
* bug (IUV-M01, issue #436).
|
||||
*
|
||||
* When `BootstrapSetupDto` is imported with `import type`, TypeScript erases
|
||||
* the class at compile time. NestJS then sees `Object` as the `@Body()`
|
||||
* metatype, and ValidationPipe with `whitelist:true + forbidNonWhitelisted:true`
|
||||
* treats every property as non-whitelisted, returning:
|
||||
*
|
||||
* 400 { message: ["property email should not exist", "property password should not exist"] }
|
||||
*
|
||||
* The fix is a plain value import (`import { BootstrapSetupDto }`), which
|
||||
* preserves the class reference so Nest can read the class-validator decorators.
|
||||
*
|
||||
* This test MUST fail if `import type` is re-introduced on `BootstrapSetupDto`.
|
||||
* A controller unit test that constructs ValidationPipe manually won't catch
|
||||
* this — only the real DI binding path exercises the metatype lookup.
|
||||
*/
|
||||
|
||||
import 'reflect-metadata';
|
||||
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { ValidationPipe, type INestApplication } from '@nestjs/common';
|
||||
import { FastifyAdapter, type NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import request from 'supertest';
|
||||
import { BootstrapController } from './bootstrap.controller.js';
|
||||
import type { BootstrapResultDto } from './bootstrap.dto.js';
|
||||
|
||||
// ─── Minimal mock dependencies ───────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* We use explicit `@Inject(AUTH)` / `@Inject(DB)` in the controller so we
|
||||
* can provide mock values by token without spinning up the real DB or Auth.
|
||||
*/
|
||||
import { AUTH } from '../auth/auth.tokens.js';
|
||||
import { DB } from '../database/database.module.js';
|
||||
|
||||
const MOCK_USER_ID = 'mock-user-id-001';
|
||||
|
||||
const mockAuth = {
|
||||
api: {
|
||||
createUser: () =>
|
||||
Promise.resolve({
|
||||
user: {
|
||||
id: MOCK_USER_ID,
|
||||
name: 'Admin',
|
||||
email: 'admin@example.com',
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
// Override db.select() so the second query (verify user exists) returns a user.
|
||||
// The bootstrap controller calls select().from() twice:
|
||||
// 1. count() to check zero users → returns [{total: 0}]
|
||||
// 2. select().where().limit() → returns [the created user]
|
||||
let selectCallCount = 0;
|
||||
const mockDbWithUser = {
|
||||
select: () => {
|
||||
selectCallCount++;
|
||||
return {
|
||||
from: () => {
|
||||
if (selectCallCount === 1) {
|
||||
// First call: count — zero users
|
||||
return Promise.resolve([{ total: 0 }]);
|
||||
}
|
||||
// Subsequent calls: return a mock user row
|
||||
return {
|
||||
where: () => ({
|
||||
limit: () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
id: MOCK_USER_ID,
|
||||
name: 'Admin',
|
||||
email: 'admin@example.com',
|
||||
role: 'admin',
|
||||
},
|
||||
]),
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
update: () => ({
|
||||
set: () => ({
|
||||
where: () => Promise.resolve([]),
|
||||
}),
|
||||
}),
|
||||
insert: () => ({
|
||||
values: () => ({
|
||||
returning: () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
id: 'token-id-001',
|
||||
label: 'Initial setup token',
|
||||
},
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
// ─── Test suite ───────────────────────────────────────────────────────────────
|
||||
|
||||
describe('POST /api/bootstrap/setup — ValidationPipe DTO binding', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeAll(async () => {
|
||||
selectCallCount = 0;
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
controllers: [BootstrapController],
|
||||
providers: [
|
||||
{ provide: AUTH, useValue: mockAuth },
|
||||
{ provide: DB, useValue: mockDbWithUser },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication<NestFastifyApplication>(new FastifyAdapter());
|
||||
|
||||
// Mirror main.ts configuration exactly — this is what reproduced the 400.
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
transform: true,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
// Fastify requires waiting for the adapter to be ready
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('returns 201 (not 400) when a valid {name, email, password} body is sent', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/api/bootstrap/setup')
|
||||
.send({ name: 'Admin', email: 'admin@example.com', password: 'password123' })
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
// Before the fix (import type), Nest ValidationPipe returned 400 with
|
||||
// "property email should not exist" / "property password should not exist"
|
||||
// because the DTO class was erased and every field looked non-whitelisted.
|
||||
expect(res.status).not.toBe(400);
|
||||
expect(res.status).toBe(201);
|
||||
const body = res.body as BootstrapResultDto;
|
||||
expect(body.user).toBeDefined();
|
||||
expect(body.user.email).toBe('admin@example.com');
|
||||
expect(body.token).toBeDefined();
|
||||
expect(body.token.plaintext).toBeDefined();
|
||||
});
|
||||
|
||||
it('returns 400 when extra forbidden properties are sent', async () => {
|
||||
// This proves ValidationPipe IS active and working (forbidNonWhitelisted).
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/api/bootstrap/setup')
|
||||
.send({
|
||||
name: 'Admin',
|
||||
email: 'admin@example.com',
|
||||
password: 'password123',
|
||||
extraField: 'should-be-rejected',
|
||||
})
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it('returns 400 when email is invalid', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/api/bootstrap/setup')
|
||||
.send({ name: 'Admin', email: 'not-an-email', password: 'password123' })
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it('returns 400 when password is too short', async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/api/bootstrap/setup')
|
||||
.send({ name: 'Admin', email: 'admin@example.com', password: 'short' })
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import swc from 'unplugin-swc';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
@@ -6,22 +5,4 @@ export default defineConfig({
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
},
|
||||
plugins: [
|
||||
swc.vite({
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
decorators: true,
|
||||
},
|
||||
transform: {
|
||||
decoratorMetadata: true,
|
||||
legacyDecorator: true,
|
||||
},
|
||||
target: 'es2022',
|
||||
},
|
||||
module: {
|
||||
type: 'nodenext',
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
**ID:** install-ux-v2-20260405
|
||||
**Statement:** The install-ux-hardening mission shipped the plumbing (uninstall, masked password, hooks consent, unified flow, headless path), but the first real end-to-end run surfaced a critical regression and a collection of UX failings that make the wizard feel neither quick nor intelligent. This mission closes the bootstrap regression as a hotfix, then rethinks the first-run experience around a provider-first, intent-driven flow with a drill-down main menu and a genuinely fast quick-start.
|
||||
**Phase:** Execution
|
||||
**Current Milestone:** IUV-M02
|
||||
**Progress:** 1 / 3 milestones
|
||||
**Phase:** Planning
|
||||
**Current Milestone:** IUV-M01
|
||||
**Progress:** 0 / 3 milestones
|
||||
**Status:** active
|
||||
**Last Updated:** 2026-04-05 (IUV-M01 complete — mosaic-v0.0.26 released)
|
||||
**Last Updated:** 2026-04-05
|
||||
**Parent Mission:** [install-ux-hardening-20260405](./archive/missions/install-ux-hardening-20260405/MISSION-MANIFEST.md) (complete — `mosaic-v0.0.25`)
|
||||
|
||||
## Context
|
||||
@@ -30,11 +30,11 @@ Real-run testing of `@mosaicstack/mosaic@0.0.25` uncovered:
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [x] AC-1: Admin bootstrap completes successfully end-to-end on a fresh install (DTO value import, no forbidNonWhitelisted regression); covered by an integration or e2e test that exercises the real DTO binding. _(PR #440)_
|
||||
- [x] AC-2: Wizard fails loudly (non-zero exit, clear error) when the bootstrap stage returns `completed: false`, in both interactive and headless modes. No more silent `✔ Wizard complete` after a 400. _(PR #440)_
|
||||
- [x] AC-3: Gateway port prompt prefills `14242` in the input field (user can press Enter to accept). _(PR #440)_
|
||||
- [x] AC-4: `"What is Mosaic?"` intro copy mentions Pi SDK as the underlying agent runtime. _(PR #440)_
|
||||
- [x] AC-5: Release `mosaic-v0.0.26` tagged and published to the Gitea npm registry, unblocking the 0.0.25 happy path. _(tag: mosaic-v0.0.26, registry: 0.0.26 live)_
|
||||
- [ ] AC-1: Admin bootstrap completes successfully end-to-end on a fresh install (DTO value import, no forbidNonWhitelisted regression); covered by an integration or e2e test that exercises the real DTO binding.
|
||||
- [ ] AC-2: Wizard fails loudly (non-zero exit, clear error) when the bootstrap stage returns `completed: false`, in both interactive and headless modes. No more silent `✔ Wizard complete` after a 400.
|
||||
- [ ] AC-3: Gateway port prompt prefills `14242` in the input field (user can press Enter to accept).
|
||||
- [ ] AC-4: `"What is Mosaic?"` intro copy mentions Pi SDK as the underlying agent runtime.
|
||||
- [ ] AC-5: Release `mosaic-v0.0.26` tagged and published to the Gitea npm registry, unblocking the 0.0.25 happy path.
|
||||
- [ ] AC-6: CORS origin prompt replaced with FQDN/hostname input; CORS string is derived from that.
|
||||
- [ ] AC-7: Skill / additional feature install section is reworked until it is actually usable end-to-end (worker defines the concrete failure modes during diagnosis).
|
||||
- [ ] AC-8: First-run flow has a drill-down main menu with at least `Plugins` (Recommended / Custom), `Providers`, and the other top-level configuration groups. Linear interrogation is gone.
|
||||
@@ -44,11 +44,11 @@ Real-run testing of `@mosaicstack/mosaic@0.0.25` uncovered:
|
||||
|
||||
## Milestones
|
||||
|
||||
| # | ID | Name | Status | Branch | Issue | Started | Completed |
|
||||
| --- | ------- | ------------------------------------------------------------ | ----------- | ---------------------- | ----- | ---------- | ---------- |
|
||||
| 1 | IUV-M01 | Hotfix: bootstrap DTO + wizard failure + port prefill + copy | complete | fix/bootstrap-hotfix | #436 | 2026-04-05 | 2026-04-05 |
|
||||
| 2 | IUV-M02 | UX polish: CORS/FQDN, skill installer rework | not-started | feat/install-ux-polish | #437 | — | — |
|
||||
| 3 | IUV-M03 | Provider-first intelligent flow + drill-down main menu | not-started | feat/install-ux-intent | #438 | — | — |
|
||||
| # | ID | Name | Status | Branch | Issue | Started | Completed |
|
||||
| --- | ------- | ------------------------------------------------------------ | ----------- | ---------------------- | ----- | ---------- | --------- |
|
||||
| 1 | IUV-M01 | Hotfix: bootstrap DTO + wizard failure + port prefill + copy | in-progress | fix/bootstrap-hotfix | #436 | 2026-04-05 | — |
|
||||
| 2 | IUV-M02 | UX polish: CORS/FQDN, skill installer rework | not-started | feat/install-ux-polish | #437 | — | — |
|
||||
| 3 | IUV-M03 | Provider-first intelligent flow + drill-down main menu | not-started | feat/install-ux-intent | #438 | — | — |
|
||||
|
||||
## Subagent Delegation Plan
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
## Milestone 1 — Hotfix: bootstrap DTO + wizard failure + port prefill + copy (IUV-M01)
|
||||
|
||||
| id | status | description | issue | agent | branch | depends_on | estimate | notes |
|
||||
| --------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ------ | -------------------- | ---------- | -------- | --------------------------------------------------------------------------------------- |
|
||||
| IUV-01-01 | done | Fix `apps/gateway/src/admin/bootstrap.controller.ts:16` — switch `import type { BootstrapSetupDto }` to a value import so Nest's `@Body()` binds the real class | #436 | sonnet | fix/bootstrap-hotfix | — | 3K | PR #440 merged `0ae932ab` |
|
||||
| IUV-01-02 | done | Add integration / e2e test that POSTs `/api/bootstrap/setup` with `{name,email,password}` against a real Nest app instance and asserts 201 — NOT a mocked controller unit test | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-01 | 10K | `apps/gateway/src/admin/bootstrap.e2e.spec.ts` — 4 tests; unplugin-swc added for vitest |
|
||||
| IUV-01-03 | done | `packages/mosaic/src/wizard.ts:147` — propagate `!bootstrapResult.completed` as a wizard failure in **interactive** mode too (not only headless); non-zero exit + no `✔ Wizard complete` line | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-02 | 5K | removed `&& headlessRun` guard |
|
||||
| IUV-01-04 | done | Gateway port prompt prefills `14242` in the input buffer — investigate why `promptPort`'s `defaultValue` isn't reaching the user-visible input | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-03 | 5K | added `initialValue` through prompter interface → clack |
|
||||
| IUV-01-05 | done | `"What is Mosaic?"` intro copy updated to mention Pi SDK as the underlying agent runtime (alongside Claude Code / Codex / OpenCode) | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-04 | 2K | `packages/mosaic/src/stages/welcome.ts` |
|
||||
| IUV-01-06 | done | Tests + code review + PR merge + tag `mosaic-v0.0.26` + Gitea release + npm registry republish | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-05 | 10K | PRs #440/#441/#442 merged; tag `mosaic-v0.0.26`; registry latest=0.0.26 ✓ |
|
||||
| id | status | description | issue | agent | branch | depends_on | estimate | notes |
|
||||
| --------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ------ | -------------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------- |
|
||||
| IUV-01-01 | not-started | Fix `apps/gateway/src/admin/bootstrap.controller.ts:16` — switch `import type { BootstrapSetupDto }` to a value import so Nest's `@Body()` binds the real class | #436 | sonnet | fix/bootstrap-hotfix | — | 3K | one-character fix; repro is real-run `mosaic wizard` against `0.0.25` |
|
||||
| IUV-01-02 | not-started | Add integration / e2e test that POSTs `/api/bootstrap/setup` with `{name,email,password}` against a real Nest app instance and asserts 201 — NOT a mocked controller unit test | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-01 | 10K | must fail before the fix and pass after; guards against the class-erasure regression recurring |
|
||||
| IUV-01-03 | not-started | `packages/mosaic/src/wizard.ts:147` — propagate `!bootstrapResult.completed` as a wizard failure in **interactive** mode too (not only headless); non-zero exit + no `✔ Wizard complete` line | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-02 | 5K | |
|
||||
| IUV-01-04 | not-started | Gateway port prompt prefills `14242` in the input buffer — investigate why `promptPort`'s `defaultValue` isn't reaching the user-visible input | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-03 | 5K | likely WizardPrompter adapter or @clack/prompts `initialValue` vs `defaultValue` mismatch |
|
||||
| IUV-01-05 | not-started | `"What is Mosaic?"` intro copy updated to mention Pi SDK as the underlying agent runtime (alongside Claude Code / Codex / OpenCode) | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-04 | 2K | |
|
||||
| IUV-01-06 | not-started | Tests + code review + PR merge + tag `mosaic-v0.0.26` + Gitea release + npm registry republish | #436 | sonnet | fix/bootstrap-hotfix | IUV-01-05 | 10K | bump `packages/mosaic/package.json` to 0.0.25 → 0.0.26 |
|
||||
|
||||
## Milestone 2 — UX polish: CORS/FQDN, skill installer rework (IUV-M02)
|
||||
|
||||
| id | status | description | issue | agent | branch | depends_on | estimate | notes |
|
||||
| --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----- | ------ | ---------------------- | ---------- | -------- | --------------------------- |
|
||||
| IUV-02-01 | not-started | Replace CORS origin prompt with FQDN / hostname input; derive the CORS value internally; default to `localhost` with clear help text | #437 | sonnet | feat/install-ux-polish | — | 10K | |
|
||||
| IUV-02-01 | not-started | Replace CORS origin prompt with FQDN / hostname input; derive the CORS value internally; default to `localhost` with clear help text | #437 | sonnet | feat/install-ux-polish | IUV-01-06 | 10K | |
|
||||
| IUV-02-02 | not-started | Diagnose and document the concrete failure modes of the current skill / additional feature install section end-to-end | #437 | sonnet | feat/install-ux-polish | IUV-02-01 | 8K | needs real-run reproduction |
|
||||
| IUV-02-03 | not-started | Rework the skill installer so it is usable end-to-end (selection, install, verify, failure reporting) | #437 | sonnet | feat/install-ux-polish | IUV-02-02 | 20K | |
|
||||
| IUV-02-04 | not-started | Tests + code review + PR merge | #437 | sonnet | feat/install-ux-polish | IUV-02-03 | 10K | |
|
||||
|
||||
@@ -107,42 +107,3 @@ Sequencing: strict. M01 ships first as a hotfix release (mosaic-v0.0.26). M02 is
|
||||
1. Create Gitea issues for M01, M02, M03
|
||||
2. Open the mission-scaffold docs PR (same pattern as parent mission's PR #430)
|
||||
3. After merge, delegate IUV-M01 to a sonnet subagent in an isolated worktree with the concrete fix-site pointers above
|
||||
|
||||
## Session 2 — 2026-04-05 (IUV-M01 delivery + close-out)
|
||||
|
||||
### Outcome
|
||||
|
||||
IUV-M01 shipped. `mosaic-v0.0.26` released and registry latest confirmed `0.0.26`.
|
||||
|
||||
### PRs merged
|
||||
|
||||
| PR | Title | Merge |
|
||||
| ---- | ------------------------------------------------------------------------ | -------- |
|
||||
| #440 | fix: bootstrap hotfix — DTO erasure, wizard failure, port prefill, copy | 0ae932ab |
|
||||
| #441 | fix: add vitest.config.ts to eslint allowDefaultProject (#440 build fix) | c08aa6fa |
|
||||
| #442 | docs: mark IUV-M01 complete — mosaic-v0.0.26 released | 78388437 |
|
||||
|
||||
### Bugs fixed (all 4 in worker's PR #440)
|
||||
|
||||
1. **DTO class erasure** — `apps/gateway/src/admin/bootstrap.controller.ts:16` — dropped `type` from `import { BootstrapSetupDto }`. Guarded by new e2e test `bootstrap.e2e.spec.ts` (4 cases) that binds through a real Nest app with `ValidationPipe { whitelist, forbidNonWhitelisted }`. Test suite needed `unplugin-swc` in `apps/gateway/vitest.config.ts` to emit `decoratorMetadata` (tsx/esbuild can't).
|
||||
2. **Wizard silent failure** — `packages/mosaic/src/wizard.ts` — removed the `&& headlessRun` guard so `!bootstrapResult.completed` now aborts in both modes.
|
||||
3. **Port prefill** — root cause was clack's `defaultValue` vs `initialValue` semantics (`defaultValue` only fills on empty submit, `initialValue` prefills the buffer). Added an `initialValue` field to `WizardPrompter.text()` interface, threaded through clack and headless prompters, switched `gateway-config.ts` port/url prompts to use it.
|
||||
4. **Pi SDK copy** — `packages/mosaic/src/stages/welcome.ts` — intro copy now lists Pi SDK.
|
||||
|
||||
### Mid-delivery hiccup — tsconfig/eslint cross-contamination
|
||||
|
||||
Worker's initial approach added `vitest.config.ts` to `apps/gateway/tsconfig.json`'s `include` to appease the eslint parser. That broke `pnpm --filter @mosaicstack/gateway build` with TS6059 (`vitest.config.ts` outside `rootDir: "src"`). The publish pipeline on the `#440` merge commit failed.
|
||||
|
||||
**Correct fix** (worker's PR #441): leave `tsconfig.json` clean (`include: ["src/**/*"]`) and instead add the file to `allowDefaultProject` in the root `eslint.config.mjs`. This keeps the tsc program strict while letting eslint resolve a parser project for the standalone config file.
|
||||
|
||||
**Pattern to remember**: when adding root-level `.ts` config files (vitest, build scripts) to a package with `rootDir: "src"`, the eslint parser project conflict is solved with `allowDefaultProject`, NEVER by widening tsconfig include. I had independently arrived at the same fix on a branch before the worker shipped #441 — deleted the duplicate.
|
||||
|
||||
### Residual follow-ups carried forward
|
||||
|
||||
1. Headless prompter fallback order: worker set `initialValue > defaultValue` in the headless path. Correct semantic, but any future headless test that explicitly depends on `defaultValue` precedence will need review.
|
||||
2. Vitest + SWC decorator metadata pattern is now the blessed approach for NestJS e2e tests in this monorepo. Any other package that adds NestJS e2e tests should mirror `apps/gateway/vitest.config.ts`.
|
||||
|
||||
### Next action
|
||||
|
||||
- Close out orchestrator doc sync (this commit): mark M01 subtasks done in `TASKS.md`, update manifest phase to Execution, commit scratchpad session 2, PR to main.
|
||||
- After merge, delegate IUV-M02 (sonnet, isolated worktree). Dependencies: IUV-02-01 (CORS→FQDN) starts unblocked since M01 is released; first real task for the M02 worker is diagnosing the skill installer failure modes (IUV-02-02) against the fresh 0.0.26 install.
|
||||
|
||||
@@ -27,7 +27,6 @@ export default tseslint.config(
|
||||
'apps/web/e2e/*.ts',
|
||||
'apps/web/e2e/helpers/*.ts',
|
||||
'apps/web/playwright.config.ts',
|
||||
'apps/gateway/vitest.config.ts',
|
||||
'packages/mosaic/__tests__/*.ts',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -7,11 +7,6 @@ SKILLS_REPO_DIR="${MOSAIC_SKILLS_REPO_DIR:-$MOSAIC_HOME/sources/agent-skills}"
|
||||
MOSAIC_SKILLS_DIR="$MOSAIC_HOME/skills"
|
||||
MOSAIC_LOCAL_SKILLS_DIR="$MOSAIC_HOME/skills-local"
|
||||
|
||||
# Colon-separated list of skill names to install. When set, only these skills
|
||||
# are linked into runtime skill directories. Empty/unset = link all skills
|
||||
# (the legacy "mosaic sync" full-catalog behavior).
|
||||
MOSAIC_INSTALL_SKILLS="${MOSAIC_INSTALL_SKILLS:-}"
|
||||
|
||||
fetch=1
|
||||
link_only=0
|
||||
|
||||
@@ -30,7 +25,6 @@ Env:
|
||||
MOSAIC_HOME Default: ~/.config/mosaic
|
||||
MOSAIC_SKILLS_REPO_URL Default: https://git.mosaicstack.dev/mosaic/agent-skills.git
|
||||
MOSAIC_SKILLS_REPO_DIR Default: ~/.config/mosaic/sources/agent-skills
|
||||
MOSAIC_INSTALL_SKILLS Colon-separated list of skills to link (default: all)
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -162,27 +156,6 @@ link_targets=(
|
||||
|
||||
canonical_real="$(readlink -f "$MOSAIC_SKILLS_DIR")"
|
||||
|
||||
# Build an associative array from the colon-separated whitelist for O(1) lookup.
|
||||
# When MOSAIC_INSTALL_SKILLS is empty, all skills are allowed.
|
||||
declare -A _skill_whitelist=()
|
||||
_whitelist_active=0
|
||||
if [[ -n "$MOSAIC_INSTALL_SKILLS" ]]; then
|
||||
_whitelist_active=1
|
||||
IFS=':' read -ra _wl_items <<< "$MOSAIC_INSTALL_SKILLS"
|
||||
for _item in "${_wl_items[@]}"; do
|
||||
[[ -n "$_item" ]] && _skill_whitelist["$_item"]=1
|
||||
done
|
||||
fi
|
||||
|
||||
is_skill_selected() {
|
||||
local name="$1"
|
||||
if [[ $_whitelist_active -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
[[ -n "${_skill_whitelist[$name]:-}" ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
link_skill_into_target() {
|
||||
local skill_path="$1"
|
||||
local target_dir="$2"
|
||||
@@ -195,11 +168,6 @@ link_skill_into_target() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Respect the install whitelist (set during first-run wizard).
|
||||
if ! is_skill_selected "$name"; then
|
||||
return
|
||||
fi
|
||||
|
||||
link_path="$target_dir/$name"
|
||||
|
||||
if [[ -L "$link_path" ]]; then
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mosaicstack/mosaic",
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.25",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.mosaicstack.dev/mosaicstack/mosaic-stack.git",
|
||||
|
||||
@@ -39,7 +39,6 @@ export class ClackPrompter implements WizardPrompter {
|
||||
message: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
initialValue?: string;
|
||||
validate?: (value: string) => string | void;
|
||||
}): Promise<string> {
|
||||
const validate = opts.validate
|
||||
@@ -52,7 +51,6 @@ export class ClackPrompter implements WizardPrompter {
|
||||
message: opts.message,
|
||||
placeholder: opts.placeholder,
|
||||
defaultValue: opts.defaultValue,
|
||||
initialValue: opts.initialValue,
|
||||
validate,
|
||||
});
|
||||
return guardCancel(result);
|
||||
|
||||
@@ -35,18 +35,15 @@ export class HeadlessPrompter implements WizardPrompter {
|
||||
message: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
initialValue?: string;
|
||||
validate?: (value: string) => string | void;
|
||||
}): Promise<string> {
|
||||
const answer = this.answers.get(opts.message);
|
||||
const value =
|
||||
typeof answer === 'string'
|
||||
? answer
|
||||
: opts.initialValue !== undefined
|
||||
? opts.initialValue
|
||||
: opts.defaultValue !== undefined
|
||||
? opts.defaultValue
|
||||
: undefined;
|
||||
: opts.defaultValue !== undefined
|
||||
? opts.defaultValue
|
||||
: undefined;
|
||||
|
||||
if (value === undefined) {
|
||||
throw new Error(`HeadlessPrompter: no answer for "${opts.message}"`);
|
||||
|
||||
@@ -24,8 +24,6 @@ export interface WizardPrompter {
|
||||
message: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
/** Prefills the input buffer so the user sees the value and can press Enter to accept. */
|
||||
initialValue?: string;
|
||||
validate?: (value: string) => string | void;
|
||||
}): Promise<string>;
|
||||
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
/**
|
||||
* Tests for the skill installer rework (IUV-02-03).
|
||||
*
|
||||
* We mock `node:child_process` to verify that:
|
||||
* 1. syncSkills passes MOSAIC_INSTALL_SKILLS with the exact selected subset
|
||||
* 2. When the script exits non-zero, the failure is surfaced to the user
|
||||
* 3. When the script is missing, a clear error is shown (not a silent no-op)
|
||||
* 4. An empty selection is a no-op (script never called)
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import type { WizardState } from '../types.js';
|
||||
import type { ConfigService } from '../config/config-service.js';
|
||||
|
||||
// ── spawnSync mock ─────────────────────────────────────────────────────────────
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const spawnSyncMock = vi.fn<any>();
|
||||
|
||||
vi.mock('node:child_process', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
spawnSync: (...args: any[]) => spawnSyncMock(...args),
|
||||
}));
|
||||
|
||||
// ── platform stub ──────────────────────────────────────────────────────────────
|
||||
|
||||
vi.mock('../platform/detect.js', () => ({
|
||||
getShellProfilePath: () => null,
|
||||
}));
|
||||
|
||||
import { finalizeStage } from './finalize.js';
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function makeState(mosaicHome: string, selectedSkills: string[] = []): WizardState {
|
||||
return {
|
||||
mosaicHome,
|
||||
sourceDir: mosaicHome,
|
||||
mode: 'quick',
|
||||
installAction: 'fresh',
|
||||
soul: { agentName: 'TestBot', communicationStyle: 'direct' },
|
||||
user: {},
|
||||
tools: {},
|
||||
runtimes: { detected: [], mcpConfigured: false },
|
||||
selectedSkills,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPrompter() {
|
||||
return {
|
||||
intro: vi.fn(),
|
||||
outro: vi.fn(),
|
||||
note: vi.fn(),
|
||||
log: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
text: vi.fn(),
|
||||
confirm: vi.fn(),
|
||||
select: vi.fn(),
|
||||
multiselect: vi.fn(),
|
||||
groupMultiselect: vi.fn(),
|
||||
spinner: vi.fn().mockReturnValue({ update: vi.fn(), stop: vi.fn() }),
|
||||
separator: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function makeConfigService(): ConfigService {
|
||||
return {
|
||||
readSoul: vi.fn().mockResolvedValue({}),
|
||||
readUser: vi.fn().mockResolvedValue({}),
|
||||
readTools: vi.fn().mockResolvedValue({}),
|
||||
writeSoul: vi.fn().mockResolvedValue(undefined),
|
||||
writeUser: vi.fn().mockResolvedValue(undefined),
|
||||
writeTools: vi.fn().mockResolvedValue(undefined),
|
||||
syncFramework: vi.fn().mockResolvedValue(undefined),
|
||||
get: vi.fn(),
|
||||
set: vi.fn(),
|
||||
getSection: vi.fn(),
|
||||
} as unknown as ConfigService;
|
||||
}
|
||||
|
||||
// ── Tests ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('finalizeStage — skill installer', () => {
|
||||
let tmp: string;
|
||||
let binDir: string;
|
||||
let syncScript: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmp = mkdtempSync(join(tmpdir(), 'mosaic-finalize-'));
|
||||
binDir = join(tmp, 'bin');
|
||||
mkdirSync(binDir, { recursive: true });
|
||||
syncScript = join(binDir, 'mosaic-sync-skills');
|
||||
|
||||
// Default: script exists and succeeds
|
||||
writeFileSync(syncScript, '#!/usr/bin/env bash\necho ok\n', { mode: 0o755 });
|
||||
spawnSyncMock.mockReturnValue({ status: 0, stdout: 'ok', stderr: '' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function findSkillsSyncCall() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (spawnSyncMock.mock.calls as any[][]).find(
|
||||
(args) =>
|
||||
Array.isArray(args[1]) &&
|
||||
(args[1] as string[]).some((a) => a.includes('mosaic-sync-skills')),
|
||||
);
|
||||
}
|
||||
|
||||
it('passes MOSAIC_INSTALL_SKILLS with the selected skill list', async () => {
|
||||
const state = makeState(tmp, ['brainstorming', 'lint', 'systematic-debugging']);
|
||||
const p = buildPrompter();
|
||||
const config = makeConfigService();
|
||||
|
||||
await finalizeStage(p, state, config);
|
||||
|
||||
const call = findSkillsSyncCall();
|
||||
expect(call).toBeDefined();
|
||||
const opts = call![2] as { env?: Record<string, string> };
|
||||
expect(opts.env?.['MOSAIC_INSTALL_SKILLS']).toBe('brainstorming:lint:systematic-debugging');
|
||||
});
|
||||
|
||||
it('skips the sync script entirely when no skills are selected', async () => {
|
||||
const state = makeState(tmp, []);
|
||||
const p = buildPrompter();
|
||||
const config = makeConfigService();
|
||||
|
||||
await finalizeStage(p, state, config);
|
||||
|
||||
expect(findSkillsSyncCall()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('warns the user when the sync script exits non-zero', async () => {
|
||||
spawnSyncMock.mockReturnValue({
|
||||
status: 1,
|
||||
stdout: '',
|
||||
stderr: 'git clone failed: connection refused',
|
||||
});
|
||||
|
||||
const state = makeState(tmp, ['brainstorming']);
|
||||
const p = buildPrompter();
|
||||
const config = makeConfigService();
|
||||
|
||||
await finalizeStage(p, state, config);
|
||||
|
||||
expect(p.warn).toHaveBeenCalledWith(expect.stringContaining('git clone failed'));
|
||||
expect(p.warn).toHaveBeenCalledWith(expect.stringContaining('mosaic sync'));
|
||||
});
|
||||
|
||||
it('warns the user when the sync script is missing', async () => {
|
||||
// Remove the script to simulate a missing installation
|
||||
rmSync(syncScript);
|
||||
|
||||
const state = makeState(tmp, ['brainstorming']);
|
||||
const p = buildPrompter();
|
||||
const config = makeConfigService();
|
||||
|
||||
await finalizeStage(p, state, config);
|
||||
|
||||
// spawnSync should NOT have been called for the skills script
|
||||
expect(findSkillsSyncCall()).toBeUndefined();
|
||||
expect(p.warn).toHaveBeenCalledWith(expect.stringContaining('not found'));
|
||||
});
|
||||
|
||||
it('includes skills count in the summary when install succeeds', async () => {
|
||||
const state = makeState(tmp, ['brainstorming', 'lint']);
|
||||
const p = buildPrompter();
|
||||
const config = makeConfigService();
|
||||
|
||||
await finalizeStage(p, state, config);
|
||||
|
||||
const noteMock = p.note as ReturnType<typeof vi.fn>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const summaryCall = (noteMock.mock.calls as any[][]).find(
|
||||
([, title]) => title === 'Installation Summary',
|
||||
);
|
||||
expect(summaryCall).toBeDefined();
|
||||
expect(summaryCall![0] as string).toContain('2 installed');
|
||||
});
|
||||
});
|
||||
@@ -25,68 +25,14 @@ function linkRuntimeAssets(mosaicHome: string, skipClaudeHooks: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
interface SyncSkillsResult {
|
||||
success: boolean;
|
||||
installedCount: number;
|
||||
failureReason?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync skills from the catalog and link only the user-selected subset.
|
||||
*
|
||||
* When `selectedSkills` is non-empty the script receives the list via
|
||||
* `MOSAIC_INSTALL_SKILLS` (colon-separated) so it can skip unlisted skills
|
||||
* during the linking phase. An empty selection is a no-op.
|
||||
*
|
||||
* Failure modes surfaced here:
|
||||
* - Script not found → tells the user explicitly
|
||||
* - Script exits non-zero → stderr is captured and reported
|
||||
* - Catalog directory missing → detected before exec, reported clearly
|
||||
*/
|
||||
function syncSkills(mosaicHome: string, selectedSkills: string[]): SyncSkillsResult {
|
||||
if (selectedSkills.length === 0) {
|
||||
return { success: true, installedCount: 0 };
|
||||
}
|
||||
|
||||
function syncSkills(mosaicHome: string): void {
|
||||
const script = join(mosaicHome, 'bin', 'mosaic-sync-skills');
|
||||
if (!existsSync(script)) {
|
||||
return {
|
||||
success: false,
|
||||
installedCount: 0,
|
||||
failureReason: `Skills sync script not found at ${script} — run 'mosaic sync' after installation.`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const result = spawnSync('bash', [script], {
|
||||
timeout: 60000,
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8',
|
||||
env: {
|
||||
...process.env,
|
||||
MOSAIC_HOME: mosaicHome,
|
||||
MOSAIC_INSTALL_SKILLS: selectedSkills.join(':'),
|
||||
},
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
const stderr = (result.stderr ?? '').trim();
|
||||
return {
|
||||
success: false,
|
||||
installedCount: 0,
|
||||
failureReason: stderr
|
||||
? `Skills sync failed: ${stderr}`
|
||||
: `Skills sync script exited with code ${(result.status ?? 'unknown').toString()}`,
|
||||
};
|
||||
if (existsSync(script)) {
|
||||
try {
|
||||
spawnSync('bash', [script], { timeout: 60000, stdio: 'pipe' });
|
||||
} catch {
|
||||
// Non-fatal
|
||||
}
|
||||
|
||||
return { success: true, installedCount: selectedSkills.length };
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
installedCount: 0,
|
||||
failureReason: `Skills sync threw: ${err instanceof Error ? err.message : String(err)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +124,10 @@ export async function finalizeStage(
|
||||
const skipClaudeHooks = state.hooks?.accepted === false;
|
||||
linkRuntimeAssets(state.mosaicHome, skipClaudeHooks);
|
||||
|
||||
// 4. Sync skills (only installs the user-selected subset)
|
||||
let skillsResult: SyncSkillsResult = { success: true, installedCount: 0 };
|
||||
// 4. Sync skills
|
||||
if (state.selectedSkills.length > 0) {
|
||||
spin.update(`Installing ${state.selectedSkills.length.toString()} selected skill(s)...`);
|
||||
skillsResult = syncSkills(state.mosaicHome, state.selectedSkills);
|
||||
spin.update('Syncing skills...');
|
||||
syncSkills(state.mosaicHome);
|
||||
}
|
||||
|
||||
// 5. Run doctor
|
||||
@@ -191,27 +136,15 @@ export async function finalizeStage(
|
||||
|
||||
spin.stop('Installation complete');
|
||||
|
||||
// Report skill install failure clearly (non-fatal but user should know)
|
||||
if (!skillsResult.success && skillsResult.failureReason) {
|
||||
p.warn(skillsResult.failureReason);
|
||||
p.warn("Run 'mosaic sync' manually after installation to install skills.");
|
||||
}
|
||||
|
||||
// 6. PATH setup
|
||||
const pathAction = setupPath(state.mosaicHome, p);
|
||||
|
||||
// 7. Summary
|
||||
const skillsSummary = skillsResult.success
|
||||
? skillsResult.installedCount > 0
|
||||
? `${skillsResult.installedCount.toString()} installed`
|
||||
: 'none selected'
|
||||
: `install failed — ${skillsResult.failureReason ?? 'unknown error'}`;
|
||||
|
||||
const summary: string[] = [
|
||||
`Agent: ${state.soul.agentName ?? 'Assistant'}`,
|
||||
`Style: ${state.soul.communicationStyle ?? 'direct'}`,
|
||||
`Runtimes: ${state.runtimes.detected.join(', ') || 'none detected'}`,
|
||||
`Skills: ${skillsSummary}`,
|
||||
`Skills: ${state.selectedSkills.length.toString()} selected`,
|
||||
`Config: ${state.mosaicHome}`,
|
||||
];
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ export async function gatewayBootstrapStage(
|
||||
host,
|
||||
port,
|
||||
tier: 'local',
|
||||
corsOrigin: `http://${host}:3000`,
|
||||
corsOrigin: 'http://localhost:3000',
|
||||
}),
|
||||
admin: { name, email, password },
|
||||
};
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { deriveCorsOrigin } from './gateway-config.js';
|
||||
|
||||
describe('deriveCorsOrigin', () => {
|
||||
describe('localhost / loopback — always http', () => {
|
||||
it('localhost port 3000 → http://localhost:3000', () => {
|
||||
expect(deriveCorsOrigin('localhost', 3000)).toBe('http://localhost:3000');
|
||||
});
|
||||
|
||||
it('127.0.0.1 port 3000 → http://127.0.0.1:3000', () => {
|
||||
expect(deriveCorsOrigin('127.0.0.1', 3000)).toBe('http://127.0.0.1:3000');
|
||||
});
|
||||
|
||||
it('localhost port 80 omits port suffix', () => {
|
||||
expect(deriveCorsOrigin('localhost', 80)).toBe('http://localhost');
|
||||
});
|
||||
|
||||
it('localhost port 443 still uses http (loopback overrides), includes port', () => {
|
||||
// 443 is the https default port, but since localhost forces http, the port
|
||||
// is NOT the default for http (80), so it must be included.
|
||||
expect(deriveCorsOrigin('localhost', 443)).toBe('http://localhost:443');
|
||||
});
|
||||
|
||||
it('useHttps=false on localhost keeps http', () => {
|
||||
expect(deriveCorsOrigin('localhost', 3000, false)).toBe('http://localhost:3000');
|
||||
});
|
||||
|
||||
it('useHttps=true on localhost still uses http (loopback wins)', () => {
|
||||
// Passing useHttps=true for localhost is unusual but the function honours
|
||||
// the explicit override — loopback detection only applies when useHttps is
|
||||
// undefined (auto-detect path).
|
||||
expect(deriveCorsOrigin('localhost', 3000, true)).toBe('https://localhost:3000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remote hostname — defaults to https', () => {
|
||||
it('example.com port 3000 → https://example.com:3000', () => {
|
||||
expect(deriveCorsOrigin('example.com', 3000)).toBe('https://example.com:3000');
|
||||
});
|
||||
|
||||
it('example.com port 443 omits port suffix', () => {
|
||||
expect(deriveCorsOrigin('example.com', 443)).toBe('https://example.com');
|
||||
});
|
||||
|
||||
it('example.com port 80 → https://example.com:80 (non-default port for https)', () => {
|
||||
expect(deriveCorsOrigin('example.com', 80)).toBe('https://example.com:80');
|
||||
});
|
||||
|
||||
it('useHttps=false on remote host uses http', () => {
|
||||
expect(deriveCorsOrigin('example.com', 3000, false)).toBe('http://example.com:3000');
|
||||
});
|
||||
|
||||
it('useHttps=false on remote host, port 80 omits suffix', () => {
|
||||
expect(deriveCorsOrigin('example.com', 80, false)).toBe('http://example.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('subdomain and non-standard hostnames', () => {
|
||||
it('sub.domain.example.com defaults to https', () => {
|
||||
expect(deriveCorsOrigin('sub.domain.example.com', 3000)).toBe(
|
||||
'https://sub.domain.example.com:3000',
|
||||
);
|
||||
});
|
||||
|
||||
it('myserver.local defaults to https (not loopback)', () => {
|
||||
expect(deriveCorsOrigin('myserver.local', 8080)).toBe('https://myserver.local:8080');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -26,25 +26,6 @@ function isHeadless(): boolean {
|
||||
return process.env['MOSAIC_ASSUME_YES'] === '1' || !process.stdin.isTTY;
|
||||
}
|
||||
|
||||
// ── CORS derivation ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Derive a full CORS origin URL from a user-provided hostname + web UI port.
|
||||
*
|
||||
* Rules:
|
||||
* - "localhost" and "127.0.0.1" always use http (never https)
|
||||
* - Everything else uses https by default; pass useHttps=false to override
|
||||
* - Standard ports (80 for http, 443 for https) are omitted from the origin
|
||||
*/
|
||||
export function deriveCorsOrigin(hostname: string, webUiPort: number, useHttps?: boolean): string {
|
||||
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';
|
||||
const proto =
|
||||
useHttps !== undefined ? (useHttps ? 'https' : 'http') : isLocalhost ? 'http' : 'https';
|
||||
const defaultPort = proto === 'https' ? 443 : 80;
|
||||
const portSuffix = webUiPort === defaultPort ? '' : `:${webUiPort.toString()}`;
|
||||
return `${proto}://${hostname}${portSuffix}`;
|
||||
}
|
||||
|
||||
// ── .env helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
function readEnvVarFromFile(envFile: string, key: string): string | null {
|
||||
@@ -96,10 +77,6 @@ async function promptTier(p: WizardPrompter): Promise<GatewayStorageTier> {
|
||||
async function promptPort(p: WizardPrompter, defaultPort: number): Promise<number> {
|
||||
const raw = await p.text({
|
||||
message: 'Gateway port',
|
||||
// initialValue prefills the input buffer so the user sees 14242 and can
|
||||
// press Enter to accept it. defaultValue is only used when the user submits
|
||||
// an empty string, which never shows in the field.
|
||||
initialValue: defaultPort.toString(),
|
||||
defaultValue: defaultPort.toString(),
|
||||
validate: (v) => {
|
||||
const n = parseInt(v, 10);
|
||||
@@ -247,9 +224,7 @@ export async function gatewayConfigStage(
|
||||
host: existing.host,
|
||||
port: existing.port,
|
||||
tier: 'local',
|
||||
corsOrigin:
|
||||
readEnvVarFromFile(ENV_FILE, 'GATEWAY_CORS_ORIGIN') ??
|
||||
deriveCorsOrigin('localhost', 3000),
|
||||
corsOrigin: 'http://localhost:3000',
|
||||
regeneratedConfig: false,
|
||||
};
|
||||
return { ready: true, host: existing.host, port: existing.port };
|
||||
@@ -302,8 +277,7 @@ export async function gatewayConfigStage(
|
||||
host,
|
||||
port,
|
||||
tier: 'local',
|
||||
corsOrigin:
|
||||
readEnvVarFromFile(ENV_FILE, 'GATEWAY_CORS_ORIGIN') ?? deriveCorsOrigin('localhost', 3000),
|
||||
corsOrigin: 'http://localhost:3000',
|
||||
regeneratedConfig: false,
|
||||
};
|
||||
} else {
|
||||
@@ -417,7 +391,6 @@ async function collectAndWriteConfig(
|
||||
let valkeyUrl: string | undefined;
|
||||
let anthropicKey: string;
|
||||
let corsOrigin: string;
|
||||
let hostname: string;
|
||||
|
||||
if (isHeadless()) {
|
||||
p.log('Headless mode detected — reading configuration from environment variables.');
|
||||
@@ -431,13 +404,7 @@ async function collectAndWriteConfig(
|
||||
databaseUrl = process.env['MOSAIC_DATABASE_URL'];
|
||||
valkeyUrl = process.env['MOSAIC_VALKEY_URL'];
|
||||
anthropicKey = process.env['MOSAIC_ANTHROPIC_API_KEY'] ?? '';
|
||||
|
||||
// MOSAIC_CORS_ORIGIN is the full override (e.g. from CI).
|
||||
// MOSAIC_HOSTNAME is the user-friendly alternative — derive from it.
|
||||
const corsOverride = process.env['MOSAIC_CORS_ORIGIN'];
|
||||
const hostnameEnv = process.env['MOSAIC_HOSTNAME'] ?? 'localhost';
|
||||
hostname = hostnameEnv;
|
||||
corsOrigin = corsOverride ?? deriveCorsOrigin(hostnameEnv, 3000);
|
||||
corsOrigin = process.env['MOSAIC_CORS_ORIGIN'] ?? 'http://localhost:3000';
|
||||
|
||||
if (tier === 'team') {
|
||||
const missing: string[] = [];
|
||||
@@ -456,12 +423,10 @@ async function collectAndWriteConfig(
|
||||
if (tier === 'team') {
|
||||
databaseUrl = await p.text({
|
||||
message: 'DATABASE_URL',
|
||||
initialValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic',
|
||||
defaultValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic',
|
||||
});
|
||||
valkeyUrl = await p.text({
|
||||
message: 'VALKEY_URL',
|
||||
initialValue: 'redis://localhost:6380',
|
||||
defaultValue: 'redis://localhost:6380',
|
||||
});
|
||||
}
|
||||
@@ -471,24 +436,10 @@ async function collectAndWriteConfig(
|
||||
defaultValue: '',
|
||||
});
|
||||
|
||||
hostname = await p.text({
|
||||
message: 'Web UI hostname (for browser access)',
|
||||
initialValue: 'localhost',
|
||||
defaultValue: 'localhost',
|
||||
placeholder: 'e.g. localhost or myserver.example.com',
|
||||
corsOrigin = await p.text({
|
||||
message: 'CORS origin',
|
||||
defaultValue: 'http://localhost:3000',
|
||||
});
|
||||
|
||||
// For non-localhost, ask if HTTPS is in use (defaults to yes for remote hosts)
|
||||
let useHttps: boolean | undefined;
|
||||
if (hostname !== 'localhost' && hostname !== '127.0.0.1') {
|
||||
useHttps = await p.confirm({
|
||||
message: 'Is HTTPS enabled for the web UI?',
|
||||
initialValue: true,
|
||||
});
|
||||
}
|
||||
|
||||
corsOrigin = deriveCorsOrigin(hostname, 3000, useHttps);
|
||||
p.log(`CORS origin set to: ${corsOrigin}`);
|
||||
}
|
||||
|
||||
const authSecret = preservedAuthSecret ?? randomBytes(32).toString('hex');
|
||||
@@ -542,7 +493,6 @@ async function collectAndWriteConfig(
|
||||
valkeyUrl,
|
||||
anthropicKey: anthropicKey || undefined,
|
||||
corsOrigin,
|
||||
hostname,
|
||||
regeneratedConfig: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function welcomeStage(p: WizardPrompter, _state: WizardState): Prom
|
||||
p.note(
|
||||
`Mosaic is an agent framework that gives AI coding assistants\n` +
|
||||
`a persistent identity, shared skills, and structured workflows.\n\n` +
|
||||
`It works with Claude Code, Codex, OpenCode, and Pi SDK.\n\n` +
|
||||
`It works with Claude Code, Codex, and OpenCode.\n\n` +
|
||||
`All config is stored locally in ~/.config/mosaic/.\n` +
|
||||
`No data is sent anywhere. No accounts required.`,
|
||||
'What is Mosaic?',
|
||||
|
||||
@@ -62,11 +62,6 @@ export interface GatewayState {
|
||||
valkeyUrl?: string;
|
||||
anthropicKey?: string;
|
||||
corsOrigin: string;
|
||||
/**
|
||||
* Raw hostname the user entered (e.g. "localhost", "myserver.example.com").
|
||||
* The full CORS origin (`corsOrigin`) is derived from this + protocol + webUiPort.
|
||||
*/
|
||||
hostname?: string;
|
||||
/** True when .env + mosaic.config.json were (re)generated in this run. */
|
||||
regeneratedConfig?: boolean;
|
||||
admin?: GatewayAdminState;
|
||||
|
||||
@@ -144,8 +144,8 @@ export async function runWizard(options: WizardOptions): Promise<void> {
|
||||
host: configResult.host,
|
||||
port: configResult.port,
|
||||
});
|
||||
if (!bootstrapResult.completed) {
|
||||
prompter.warn('Admin bootstrap failed — aborting wizard.');
|
||||
if (!bootstrapResult.completed && headlessRun) {
|
||||
prompter.warn('Admin bootstrap failed in headless mode — aborting wizard.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
377
pnpm-lock.yaml
generated
377
pnpm-lock.yaml
generated
@@ -174,39 +174,21 @@ importers:
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
devDependencies:
|
||||
'@nestjs/testing':
|
||||
specifier: ^11.1.18
|
||||
version: 11.1.18(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)
|
||||
'@swc/core':
|
||||
specifier: ^1.15.24
|
||||
version: 1.15.24(@swc/helpers@0.5.21)
|
||||
'@swc/helpers':
|
||||
specifier: ^0.5.21
|
||||
version: 0.5.21
|
||||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.15
|
||||
'@types/node-cron':
|
||||
specifier: ^3.0.11
|
||||
version: 3.0.11
|
||||
'@types/supertest':
|
||||
specifier: ^7.2.0
|
||||
version: 7.2.0
|
||||
'@types/uuid':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
supertest:
|
||||
specifier: ^7.2.2
|
||||
version: 7.2.2
|
||||
tsx:
|
||||
specifier: ^4.0.0
|
||||
version: 4.21.0
|
||||
typescript:
|
||||
specifier: ^5.8.0
|
||||
version: 5.9.3
|
||||
unplugin-swc:
|
||||
specifier: ^1.5.9
|
||||
version: 1.5.9(@swc/core@1.15.24(@swc/helpers@0.5.21))(rollup@4.59.0)
|
||||
vitest:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.9(@types/node@22.19.15)(jsdom@29.0.0(@noble/hashes@2.0.1))(lightningcss@1.31.1)
|
||||
@@ -2327,19 +2309,6 @@ packages:
|
||||
'@nestjs/websockets': ^11.0.0
|
||||
rxjs: ^7.1.0
|
||||
|
||||
'@nestjs/testing@11.1.18':
|
||||
resolution: {integrity: sha512-frzwNlpBgtAzI3hp/qo57DZoRO4RMTH1wST3QUYEhRTHyfPkLpzkWz3jV/mhApXjD0yT56Ptlzn6zuYPLh87Lw==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^11.0.0
|
||||
'@nestjs/core': ^11.0.0
|
||||
'@nestjs/microservices': ^11.0.0
|
||||
'@nestjs/platform-express': ^11.0.0
|
||||
peerDependenciesMeta:
|
||||
'@nestjs/microservices':
|
||||
optional: true
|
||||
'@nestjs/platform-express':
|
||||
optional: true
|
||||
|
||||
'@nestjs/throttler@6.5.0':
|
||||
resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==}
|
||||
peerDependencies:
|
||||
@@ -2414,10 +2383,6 @@ packages:
|
||||
resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
'@noble/hashes@1.8.0':
|
||||
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
|
||||
'@noble/hashes@2.0.1':
|
||||
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
@@ -3042,9 +3007,6 @@ packages:
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.1.0
|
||||
|
||||
'@paralleldrive/cuid2@2.3.1':
|
||||
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
|
||||
|
||||
'@pinojs/redact@0.4.0':
|
||||
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
||||
|
||||
@@ -3087,15 +3049,6 @@ packages:
|
||||
'@protobufjs/utf8@1.1.0':
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
|
||||
'@rollup/pluginutils@5.3.0':
|
||||
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
|
||||
cpu: [arm]
|
||||
@@ -3437,99 +3390,9 @@ packages:
|
||||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@swc/core-darwin-arm64@1.15.24':
|
||||
resolution: {integrity: sha512-uM5ZGfFXjtvtJ+fe448PVBEbn/CSxS3UAyLj3O9xOqKIWy3S6hPTXSPbszxkSsGDYKi+YFhzAsR4r/eXLxEQ0g==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-darwin-x64@1.15.24':
|
||||
resolution: {integrity: sha512-fMIb/Zfn929pw25VMBhV7Ji2Dl+lCWtUPNdYJQYOke+00E5fcQ9ynxtP8+qhUo/HZc+mYQb1gJxwHM9vty+lXg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.15.24':
|
||||
resolution: {integrity: sha512-vOkjsyjjxnoYx3hMEWcGxQrMgnNrRm6WAegBXrN8foHtDAR+zpdhpGF5a4lj1bNPgXAvmysjui8cM1ov/Clkaw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.15.24':
|
||||
resolution: {integrity: sha512-h/oNu+upkXJ6Cicnq7YGVj9PkdfarLCdQa8l/FlHYvfv8CEiMaeeTnpLU7gSBH/rGxosM6Qkfa/J9mThGF9CLA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.15.24':
|
||||
resolution: {integrity: sha512-ZpF/pRe1guk6sKzQI9D1jAORtjTdNlyeXn9GDz8ophof/w2WhojRblvSDJaGe7rJjcPN8AaOkhwdRUh7q8oYIg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-ppc64-gnu@1.15.24':
|
||||
resolution: {integrity: sha512-QZEsZfisHTSJlmyChgDFNmKPb3W6Lhbfo/O76HhIngfEdnQNmukS38/VSe1feho+xkV5A5hETyCbx3sALBZKAQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-s390x-gnu@1.15.24':
|
||||
resolution: {integrity: sha512-DLdJKVsJgglqQrJBuoUYNmzm3leI7kUZhLbZGHv42onfKsGf6JDS3+bzCUQfte/XOqDjh/tmmn1DR/CF/tCJFw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.15.24':
|
||||
resolution: {integrity: sha512-IpLYfposPA/XLxYOKpRfeccl1p5dDa3+okZDHHTchBkXEaVCnq5MADPmIWwIYj1tudt7hORsEHccG5no6IUQRw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-musl@1.15.24':
|
||||
resolution: {integrity: sha512-JHy3fMSc0t/EPWgo74+OK5TGr51aElnzqfUPaiRf2qJ/BfX5CUCfMiWVBuhI7qmVMBnk1jTRnL/xZnOSHDPLYg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.15.24':
|
||||
resolution: {integrity: sha512-Txj+qUH1z2bUd1P3JvwByfjKFti3cptlAxhWgmunBUUxy/IW3CXLZ6l6Gk4liANadKkU71nIU1X30Z5vpMT3BA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.15.24':
|
||||
resolution: {integrity: sha512-15D/nl3XwrhFpMv+MADFOiVwv3FvH9j8c6Rf8EXBT3Q5LoMh8YnDnSgPYqw1JzPnksvsBX6QPXLiPqmcR/Z4qQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.15.24':
|
||||
resolution: {integrity: sha512-PR0PlTlPra2JbaDphrOAzm6s0v9rA0F17YzB+XbWD95B4g2cWcZY9LAeTa4xll70VLw9Jr7xBrlohqlQmelMFQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core@1.15.24':
|
||||
resolution: {integrity: sha512-5Hj8aNasue7yusUt8LGCUe/AjM7RMAce8ZoyDyiFwx7Al+GbYKL+yE7g4sJk8vEr1dKIkTRARkNIJENc4CjkBQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@swc/helpers': '>=0.5.17'
|
||||
peerDependenciesMeta:
|
||||
'@swc/helpers':
|
||||
optional: true
|
||||
|
||||
'@swc/counter@0.1.3':
|
||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
'@swc/helpers@0.5.21':
|
||||
resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==}
|
||||
|
||||
'@swc/types@0.1.26':
|
||||
resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==}
|
||||
|
||||
'@tailwindcss/node@4.2.1':
|
||||
resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==}
|
||||
|
||||
@@ -3643,9 +3506,6 @@ packages:
|
||||
'@types/connect@3.4.38':
|
||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||
|
||||
'@types/cookiejar@2.1.5':
|
||||
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
|
||||
|
||||
@@ -3676,9 +3536,6 @@ packages:
|
||||
'@types/memcached@2.2.10':
|
||||
resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==}
|
||||
|
||||
'@types/methods@1.1.4':
|
||||
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
|
||||
|
||||
'@types/mime-types@2.1.4':
|
||||
resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
|
||||
|
||||
@@ -3723,12 +3580,6 @@ packages:
|
||||
'@types/retry@0.12.0':
|
||||
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
|
||||
|
||||
'@types/superagent@8.1.9':
|
||||
resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==}
|
||||
|
||||
'@types/supertest@7.2.0':
|
||||
resolution: {integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==}
|
||||
|
||||
'@types/tedious@4.0.14':
|
||||
resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
|
||||
|
||||
@@ -3937,9 +3788,6 @@ packages:
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
asap@2.0.6:
|
||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4281,9 +4129,6 @@ packages:
|
||||
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
component-emitter@1.3.1:
|
||||
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
@@ -4315,9 +4160,6 @@ packages:
|
||||
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cookiejar@2.1.4:
|
||||
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
@@ -4426,9 +4268,6 @@ packages:
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
dezalgo@1.0.4:
|
||||
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
|
||||
|
||||
diff@8.0.3:
|
||||
resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
@@ -4734,9 +4573,6 @@ packages:
|
||||
estree-util-is-identifier-name@3.0.0:
|
||||
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
@@ -4915,10 +4751,6 @@ packages:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
formidable@3.5.4:
|
||||
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
forwarded-parse@2.1.2:
|
||||
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
|
||||
|
||||
@@ -5492,10 +5324,6 @@ packages:
|
||||
resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==}
|
||||
engines: {node: '>=13.2.0'}
|
||||
|
||||
load-tsconfig@0.2.5:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5634,10 +5462,6 @@ packages:
|
||||
merge-stream@2.0.0:
|
||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||
|
||||
methods@1.1.2:
|
||||
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
micromark-core-commonmark@2.0.3:
|
||||
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
||||
|
||||
@@ -5721,11 +5545,6 @@ packages:
|
||||
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
mime@2.6.0:
|
||||
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
hasBin: true
|
||||
|
||||
mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -6683,14 +6502,6 @@ packages:
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
|
||||
superagent@10.3.0:
|
||||
resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
|
||||
supertest@7.2.2:
|
||||
resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -6947,15 +6758,6 @@ packages:
|
||||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
unplugin-swc@1.5.9:
|
||||
resolution: {integrity: sha512-RKwK3yf0M+MN17xZfF14bdKqfx0zMXYdtOdxLiE6jHAoidupKq3jGdJYANyIM1X/VmABhh1WpdO+/f4+Ol89+g==}
|
||||
peerDependencies:
|
||||
'@swc/core': ^1.2.108
|
||||
|
||||
unplugin@2.3.11:
|
||||
resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
@@ -7068,9 +6870,6 @@ packages:
|
||||
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
whatwg-mimetype@5.0.0:
|
||||
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
|
||||
engines: {node: '>=20'}
|
||||
@@ -8963,12 +8762,6 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@nestjs/testing@11.1.18(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@nestjs/throttler@6.5.0(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(reflect-metadata@0.2.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
@@ -9015,8 +8808,6 @@ snapshots:
|
||||
|
||||
'@noble/ciphers@2.1.1': {}
|
||||
|
||||
'@noble/hashes@1.8.0': {}
|
||||
|
||||
'@noble/hashes@2.0.1': {}
|
||||
|
||||
'@nuxt/opencollective@0.4.1':
|
||||
@@ -9931,10 +9722,6 @@ snapshots:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0)
|
||||
|
||||
'@paralleldrive/cuid2@2.3.1':
|
||||
dependencies:
|
||||
'@noble/hashes': 1.8.0
|
||||
|
||||
'@pinojs/redact@0.4.0': {}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
@@ -9967,14 +9754,6 @@ snapshots:
|
||||
|
||||
'@protobufjs/utf8@1.1.0': {}
|
||||
|
||||
'@rollup/pluginutils@5.3.0(rollup@4.59.0)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 4.0.3
|
||||
optionalDependencies:
|
||||
rollup: 4.59.0
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||
optional: true
|
||||
|
||||
@@ -10372,75 +10151,10 @@ snapshots:
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@swc/core-darwin-arm64@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-darwin-x64@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-ppc64-gnu@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-s390x-gnu@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-musl@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.15.24':
|
||||
optional: true
|
||||
|
||||
'@swc/core@1.15.24(@swc/helpers@0.5.21)':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/types': 0.1.26
|
||||
optionalDependencies:
|
||||
'@swc/core-darwin-arm64': 1.15.24
|
||||
'@swc/core-darwin-x64': 1.15.24
|
||||
'@swc/core-linux-arm-gnueabihf': 1.15.24
|
||||
'@swc/core-linux-arm64-gnu': 1.15.24
|
||||
'@swc/core-linux-arm64-musl': 1.15.24
|
||||
'@swc/core-linux-ppc64-gnu': 1.15.24
|
||||
'@swc/core-linux-s390x-gnu': 1.15.24
|
||||
'@swc/core-linux-x64-gnu': 1.15.24
|
||||
'@swc/core-linux-x64-musl': 1.15.24
|
||||
'@swc/core-win32-arm64-msvc': 1.15.24
|
||||
'@swc/core-win32-ia32-msvc': 1.15.24
|
||||
'@swc/core-win32-x64-msvc': 1.15.24
|
||||
'@swc/helpers': 0.5.21
|
||||
|
||||
'@swc/counter@0.1.3': {}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@swc/helpers@0.5.21':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@swc/types@0.1.26':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
|
||||
'@tailwindcss/node@4.2.1':
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
@@ -10538,8 +10252,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
|
||||
'@types/cookiejar@2.1.5': {}
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
@@ -10572,8 +10284,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
|
||||
'@types/methods@1.1.4': {}
|
||||
|
||||
'@types/mime-types@2.1.4': {}
|
||||
|
||||
'@types/ms@2.1.0': {}
|
||||
@@ -10623,18 +10333,6 @@ snapshots:
|
||||
|
||||
'@types/retry@0.12.0': {}
|
||||
|
||||
'@types/superagent@8.1.9':
|
||||
dependencies:
|
||||
'@types/cookiejar': 2.1.5
|
||||
'@types/methods': 1.1.4
|
||||
'@types/node': 22.19.15
|
||||
form-data: 4.0.5
|
||||
|
||||
'@types/supertest@7.2.0':
|
||||
dependencies:
|
||||
'@types/methods': 1.1.4
|
||||
'@types/superagent': 8.1.9
|
||||
|
||||
'@types/tedious@4.0.14':
|
||||
dependencies:
|
||||
'@types/node': 22.19.15
|
||||
@@ -10889,15 +10587,14 @@ snapshots:
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
asap@2.0.6: {}
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
ast-types@0.13.4:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
asynckit@0.4.0:
|
||||
optional: true
|
||||
|
||||
atomic-sleep@1.0.0: {}
|
||||
|
||||
@@ -11194,6 +10891,7 @@ snapshots:
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
optional: true
|
||||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
|
||||
@@ -11201,8 +10899,6 @@ snapshots:
|
||||
|
||||
commander@14.0.3: {}
|
||||
|
||||
component-emitter@1.3.1: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
consola@3.4.2: {}
|
||||
@@ -11219,8 +10915,6 @@ snapshots:
|
||||
|
||||
cookie@1.1.1: {}
|
||||
|
||||
cookiejar@2.1.4: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
cors@2.8.6:
|
||||
@@ -11300,7 +10994,8 @@ snapshots:
|
||||
escodegen: 2.1.0
|
||||
esprima: 4.0.1
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
delayed-stream@1.0.0:
|
||||
optional: true
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
@@ -11314,11 +11009,6 @@ snapshots:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
dezalgo@1.0.4:
|
||||
dependencies:
|
||||
asap: 2.0.6
|
||||
wrappy: 1.0.2
|
||||
|
||||
diff@8.0.3: {}
|
||||
|
||||
discord-api-types@0.38.42: {}
|
||||
@@ -11470,6 +11160,7 @@ snapshots:
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.2
|
||||
optional: true
|
||||
|
||||
es-toolkit@1.45.1: {}
|
||||
|
||||
@@ -11677,8 +11368,6 @@ snapshots:
|
||||
|
||||
estree-util-is-identifier-name@3.0.0: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -11929,17 +11618,12 @@ snapshots:
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.2
|
||||
mime-types: 2.1.35
|
||||
optional: true
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
|
||||
formidable@3.5.4:
|
||||
dependencies:
|
||||
'@paralleldrive/cuid2': 2.3.1
|
||||
dezalgo: 1.0.4
|
||||
once: 1.4.0
|
||||
|
||||
forwarded-parse@2.1.2: {}
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
@@ -12112,6 +11796,7 @@ snapshots:
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
optional: true
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
@@ -12583,8 +12268,6 @@ snapshots:
|
||||
|
||||
load-esm@1.0.3: {}
|
||||
|
||||
load-tsconfig@0.2.5: {}
|
||||
|
||||
locate-path@6.0.0:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
@@ -12783,8 +12466,6 @@ snapshots:
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
|
||||
methods@1.1.2: {}
|
||||
|
||||
micromark-core-commonmark@2.0.3:
|
||||
dependencies:
|
||||
decode-named-character-reference: 1.3.0
|
||||
@@ -12935,8 +12616,6 @@ snapshots:
|
||||
dependencies:
|
||||
mime-db: 1.54.0
|
||||
|
||||
mime@2.6.0: {}
|
||||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
mimic-fn@4.0.0: {}
|
||||
@@ -14017,28 +13696,6 @@ snapshots:
|
||||
client-only: 0.0.1
|
||||
react: 19.2.4
|
||||
|
||||
superagent@10.3.0:
|
||||
dependencies:
|
||||
component-emitter: 1.3.1
|
||||
cookiejar: 2.1.4
|
||||
debug: 4.4.3
|
||||
fast-safe-stringify: 2.1.1
|
||||
form-data: 4.0.5
|
||||
formidable: 3.5.4
|
||||
methods: 1.1.2
|
||||
mime: 2.6.0
|
||||
qs: 6.15.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
supertest@7.2.2:
|
||||
dependencies:
|
||||
cookie-signature: 1.2.2
|
||||
methods: 1.1.2
|
||||
superagent: 10.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
@@ -14294,22 +13951,6 @@ snapshots:
|
||||
|
||||
unpipe@1.0.0: {}
|
||||
|
||||
unplugin-swc@1.5.9(@swc/core@1.15.24(@swc/helpers@0.5.21))(rollup@4.59.0):
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.59.0)
|
||||
'@swc/core': 1.15.24(@swc/helpers@0.5.21)
|
||||
load-tsconfig: 0.2.5
|
||||
unplugin: 2.3.11
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
unplugin@2.3.11:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
acorn: 8.16.0
|
||||
picomatch: 4.0.3
|
||||
webpack-virtual-modules: 0.6.2
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
@@ -14476,8 +14117,6 @@ snapshots:
|
||||
|
||||
webidl-conversions@8.0.1: {}
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
||||
whatwg-mimetype@5.0.0: {}
|
||||
|
||||
whatwg-url@14.2.0:
|
||||
|
||||
Reference in New Issue
Block a user