@polymorphism-tech/morph-spec 8.38.0 → 8.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +20 -9
- package/README.md +2 -2
- package/bin/morph-spec.js +60 -43
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +4 -3
- package/framework/MORPH.md +10 -9
- package/framework/agents.json +43 -26
- package/framework/docs/CLI.md +7 -5
- package/framework/evals/rubrics/code-quality.md +4 -4
- package/framework/evals/rubrics/condensed.md +2 -2
- package/framework/hooks/README.md +1 -1
- package/framework/hooks/claude-code/core/telemetry-log.js +39 -8
- package/framework/hooks/claude-code/core/worktree-create.js +22 -6
- package/framework/hooks/shared/git-paths.js +38 -5
- package/framework/mcp/registry.json +58 -66
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +9 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +59 -25
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +48 -24
- package/framework/skills/core/morph-media/SKILL.md +2 -2
- package/framework/skills/core/morph-plan/SKILL.md +10 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +7 -6
- package/framework/standards/PROVENANCE.json +4 -4
- package/framework/standards/STANDARDS.json +9 -9
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/architecture/code-comments.md +52 -16
- package/framework/standards/backend/dotnet/testing.md +4 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/design-system/ai-image-generation.md +42 -28
- package/framework/standards/frontend/design-system/ai-video-generation.md +24 -9
- package/framework/standards/frontend/nextjs/testing.md +4 -2
- package/framework/standards/integration/mcp/mcp-tools.md +64 -126
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/project/doctor.js +12 -6
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +5 -0
- package/src/commands/project/worktree.js +60 -4
- package/src/commands/state/gate-check.js +5 -0
- package/src/commands/tasks/dag.js +27 -4
- package/src/commands/validation/verify.js +22 -38
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +59 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +2 -0
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +22 -0
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +13 -0
- package/src/lib/tasks/tasks-json-parser.js +21 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
- package/src/lib/worktree/env-allocator.js +9 -3
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +2 -4
- package/src/utils/agents-installer.js +108 -13
- package/src/utils/banner.js +1 -1
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// MORPH-SPEC - Neon Auth Configuration Template
|
|
3
|
-
// Configuracao de autenticacao com Neon Auth (Better Auth) para .NET
|
|
4
|
-
//
|
|
5
|
-
// Neon Auth tokens are signed with EdDSA (Ed25519, JWK kty "OKP"). There is no
|
|
6
|
-
// OIDC discovery document (/.well-known/openid-configuration) — only the raw
|
|
7
|
-
// /.well-known/jwks.json — and Microsoft.IdentityModel.Tokens cannot verify
|
|
8
|
-
// OKP/EdDSA signatures on its own. See:
|
|
9
|
-
// .morph/framework/standards/backend/integrations/neon-auth/neon-auth.md
|
|
10
|
-
// → "## .NET Backend Integration" for the full reference and rationale.
|
|
11
|
-
//
|
|
12
|
-
// NuGet (add both):
|
|
13
|
-
// <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
|
|
14
|
-
// <PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" /> <!-- pin >= 2.3.1, CVE-2024-30172 -->
|
|
15
|
-
// ==============================================================================
|
|
16
|
-
|
|
1
|
+
// Neon Auth signs with EdDSA (Ed25519), which Microsoft.IdentityModel cannot verify: needs BouncyCastle.Cryptography >= 2.3.1 (CVE-2024-30172).
|
|
17
2
|
using System.Security.Claims;
|
|
18
3
|
using System.Text;
|
|
19
4
|
using Microsoft.AspNetCore.Authentication;
|
|
@@ -26,32 +11,14 @@ using Org.BouncyCastle.Crypto.Signers;
|
|
|
26
11
|
|
|
27
12
|
namespace {{Namespace}}.Infrastructure.Auth;
|
|
28
13
|
|
|
29
|
-
// ==============================================================================
|
|
30
|
-
// OPTIONS
|
|
31
|
-
// ==============================================================================
|
|
32
|
-
|
|
33
14
|
public class NeonAuthOptions
|
|
34
15
|
{
|
|
35
16
|
public const string SectionName = "NeonAuth";
|
|
36
17
|
|
|
37
|
-
|
|
38
|
-
/// Neon Auth Base URL (e.g., https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth).
|
|
39
|
-
/// MUST be present in appsettings.Development.json — it's a public value, not a secret.
|
|
40
|
-
/// Missing it produces the same symptom as a broken signature validator: every request
|
|
41
|
-
/// gets a bare 401 with nothing useful in the logs.
|
|
42
|
-
/// </summary>
|
|
18
|
+
// Public value, commit it in appsettings.Development.json: when missing, every request is a bare 401 with nothing in the logs.
|
|
43
19
|
public string BaseUrl { get; set; } = string.Empty;
|
|
44
20
|
}
|
|
45
21
|
|
|
46
|
-
// ==============================================================================
|
|
47
|
-
// JWKS PROVIDER (cached — Neon Auth has no OIDC discovery to piggyback on)
|
|
48
|
-
// ==============================================================================
|
|
49
|
-
|
|
50
|
-
/// <summary>
|
|
51
|
-
/// Fetches and caches Neon Auth's raw JWKS. Cached for 15 minutes; a forced refresh
|
|
52
|
-
/// happens once when an unknown `kid` is seen, to survive key rotation without
|
|
53
|
-
/// waiting out the TTL.
|
|
54
|
-
/// </summary>
|
|
55
22
|
public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAuthOptions> options)
|
|
56
23
|
{
|
|
57
24
|
private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(15);
|
|
@@ -65,7 +32,7 @@ public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAut
|
|
|
65
32
|
var key = keySet.Keys.FirstOrDefault(k => k.Kid == kid);
|
|
66
33
|
if (key is not null) return key;
|
|
67
34
|
|
|
68
|
-
// Unknown kid
|
|
35
|
+
// Unknown kid may be a key rotation: refresh once instead of waiting out the TTL.
|
|
69
36
|
keySet = await GetKeySetAsync(forceRefresh: true, cancellationToken);
|
|
70
37
|
return keySet.Keys.FirstOrDefault(k => k.Kid == kid);
|
|
71
38
|
}
|
|
@@ -94,18 +61,8 @@ public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAut
|
|
|
94
61
|
}
|
|
95
62
|
}
|
|
96
63
|
|
|
97
|
-
// ==============================================================================
|
|
98
|
-
// SERVICE EXTENSIONS
|
|
99
|
-
// ==============================================================================
|
|
100
|
-
|
|
101
64
|
public static class NeonAuthServiceExtensions
|
|
102
65
|
{
|
|
103
|
-
/// <summary>
|
|
104
|
-
/// Adds Neon Auth JWT Bearer authentication to the project. Validates EdDSA-signed
|
|
105
|
-
/// JWTs issued by Neon Auth via a manually-fetched, cached JWKS — see
|
|
106
|
-
/// NeonAuthJwksProvider above and the standard's "## .NET Backend Integration" section
|
|
107
|
-
/// for why options.Authority / the default IssuerSigningKeyResolver do not work here.
|
|
108
|
-
/// </summary>
|
|
109
66
|
public static IServiceCollection AddNeonAuthentication(
|
|
110
67
|
this IServiceCollection services,
|
|
111
68
|
IConfiguration configuration)
|
|
@@ -116,9 +73,7 @@ public static class NeonAuthServiceExtensions
|
|
|
116
73
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
117
74
|
.AddJwtBearer(options =>
|
|
118
75
|
{
|
|
119
|
-
//
|
|
120
|
-
// /.well-known/openid-configuration, so Authority-driven metadata refresh
|
|
121
|
-
// has nothing valid to fetch. ValidIssuer is set explicitly instead.
|
|
76
|
+
// No options.Authority: Neon Auth serves no /.well-known/openid-configuration to fetch.
|
|
122
77
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
123
78
|
{
|
|
124
79
|
ValidateIssuer = true,
|
|
@@ -126,16 +81,12 @@ public static class NeonAuthServiceExtensions
|
|
|
126
81
|
ValidateAudience = false,
|
|
127
82
|
ValidateLifetime = true,
|
|
128
83
|
NameClaimType = "sub",
|
|
129
|
-
//
|
|
130
|
-
// SignatureValidator below (fetched from our own cached JWKS) —
|
|
131
|
-
// this flag doesn't gate that path.
|
|
84
|
+
// The key is trusted inside SignatureValidator below, not by this flag.
|
|
132
85
|
ValidateIssuerSigningKey = false,
|
|
133
86
|
};
|
|
134
87
|
});
|
|
135
88
|
|
|
136
|
-
//
|
|
137
|
-
// already run, so this only *adds* the SignatureValidator to the
|
|
138
|
-
// TokenValidationParameters instance built above.
|
|
89
|
+
// Separate pass because the validator needs NeonAuthJwksProvider from DI.
|
|
139
90
|
services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
|
|
140
91
|
.Configure<NeonAuthJwksProvider>((options, jwks) =>
|
|
141
92
|
{
|
|
@@ -143,8 +94,7 @@ public static class NeonAuthServiceExtensions
|
|
|
143
94
|
{
|
|
144
95
|
var jwt = new JsonWebToken(token);
|
|
145
96
|
|
|
146
|
-
// Pin the algorithm
|
|
147
|
-
// alg-substitution attacks.
|
|
97
|
+
// Pin the algorithm against alg-substitution attacks.
|
|
148
98
|
if (!string.Equals(jwt.Alg, "EdDSA", StringComparison.Ordinal))
|
|
149
99
|
throw new SecurityTokenInvalidSignatureException(
|
|
150
100
|
$"Unsupported alg '{jwt.Alg}' — Neon Auth tokens must be EdDSA.");
|
|
@@ -153,9 +103,7 @@ public static class NeonAuthServiceExtensions
|
|
|
153
103
|
if (string.IsNullOrEmpty(kid))
|
|
154
104
|
throw new SecurityTokenInvalidSignatureException("Token is missing 'kid'.");
|
|
155
105
|
|
|
156
|
-
//
|
|
157
|
-
// path. SignatureValidator has no async overload — only a cold-start /
|
|
158
|
-
// key-rotation miss ever awaits real I/O here.
|
|
106
|
+
// SignatureValidator has no async overload; the JWKS is cached, so only a cold miss blocks on I/O.
|
|
159
107
|
var jwk = jwks.GetKeyAsync(kid, CancellationToken.None).GetAwaiter().GetResult()
|
|
160
108
|
?? throw new SecurityTokenInvalidSignatureException(
|
|
161
109
|
$"No JWKS key found for kid '{kid}'.");
|
|
@@ -181,10 +129,7 @@ public static class NeonAuthServiceExtensions
|
|
|
181
129
|
throw new SecurityTokenInvalidSignatureException(
|
|
182
130
|
"Ed25519 signature verification failed.");
|
|
183
131
|
|
|
184
|
-
//
|
|
185
|
-
// Core 8+ uses JsonWebTokenHandler internally. Returning a
|
|
186
|
-
// System.IdentityModel.Tokens.Jwt.JwtSecurityToken here fails with
|
|
187
|
-
// IDX10506 ("SignatureValidator returned a token of an unexpected type").
|
|
132
|
+
// Must be JsonWebToken: returning a JwtSecurityToken fails with IDX10506 on ASP.NET Core 8+.
|
|
188
133
|
return jwt;
|
|
189
134
|
};
|
|
190
135
|
});
|
|
@@ -195,10 +140,6 @@ public static class NeonAuthServiceExtensions
|
|
|
195
140
|
}
|
|
196
141
|
}
|
|
197
142
|
|
|
198
|
-
// ==============================================================================
|
|
199
|
-
// USER CONTEXT SERVICE
|
|
200
|
-
// ==============================================================================
|
|
201
|
-
|
|
202
143
|
public interface ICurrentUserService
|
|
203
144
|
{
|
|
204
145
|
string? UserId { get; }
|
|
@@ -218,10 +159,6 @@ public class CurrentUserService(IHttpContextAccessor httpContextAccessor) : ICur
|
|
|
218
159
|
httpContextAccessor.HttpContext?.User.Identity?.IsAuthenticated ?? false;
|
|
219
160
|
}
|
|
220
161
|
|
|
221
|
-
// ==============================================================================
|
|
222
|
-
// MIDDLEWARE PIPELINE
|
|
223
|
-
// ==============================================================================
|
|
224
|
-
|
|
225
162
|
public static class NeonAuthMiddlewareExtensions
|
|
226
163
|
{
|
|
227
164
|
public static IApplicationBuilder UseNeonAuthentication(this IApplicationBuilder app)
|
|
@@ -231,36 +168,3 @@ public static class NeonAuthMiddlewareExtensions
|
|
|
231
168
|
return app;
|
|
232
169
|
}
|
|
233
170
|
}
|
|
234
|
-
|
|
235
|
-
// ==============================================================================
|
|
236
|
-
// APPSETTINGS.DEVELOPMENT.JSON — mandatory, public value, safe to commit
|
|
237
|
-
// ==============================================================================
|
|
238
|
-
|
|
239
|
-
/*
|
|
240
|
-
{
|
|
241
|
-
"NeonAuth": {
|
|
242
|
-
"BaseUrl": "https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth"
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
*/
|
|
246
|
-
|
|
247
|
-
// ==============================================================================
|
|
248
|
-
// PROGRAM.CS EXAMPLE
|
|
249
|
-
// ==============================================================================
|
|
250
|
-
|
|
251
|
-
/*
|
|
252
|
-
var builder = WebApplication.CreateBuilder(args);
|
|
253
|
-
|
|
254
|
-
// Add Neon Auth authentication (registers the JWKS provider + EdDSA signature validator)
|
|
255
|
-
builder.Services.AddNeonAuthentication(builder.Configuration);
|
|
256
|
-
builder.Services.AddHttpContextAccessor();
|
|
257
|
-
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
|
|
258
|
-
|
|
259
|
-
var app = builder.Build();
|
|
260
|
-
|
|
261
|
-
app.UseNeonAuthentication();
|
|
262
|
-
|
|
263
|
-
app.MapControllers().RequireAuthorization();
|
|
264
|
-
|
|
265
|
-
app.Run();
|
|
266
|
-
*/
|
|
@@ -1,120 +1,97 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// CSS/contrast validators are visual — report as informational
|
|
99
|
-
results.issues.push({ level: 'info', message: `${validator} requires manual review` });
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
default:
|
|
104
|
-
results.issues.push({ level: 'warning', message: `Unknown validator: ${validator}` });
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ── Output ────────────────────────────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
if (isJson) {
|
|
111
|
-
console.log(JSON.stringify(results, null, 2));
|
|
112
|
-
} else {
|
|
113
|
-
const icon = results.passed ? '✅' : '❌';
|
|
114
|
-
console.log(`${icon} Validator: ${validator} — ${results.passed ? 'PASSED' : `FAILED (${results.errors} errors)`}`);
|
|
115
|
-
for (const issue of results.issues) {
|
|
116
|
-
console.log(` [${issue.level}] ${issue.message}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
process.exit(results.passed ? 0 : 1);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const validator = process.argv[2];
|
|
4
|
+
const isJson = process.argv.includes('--json');
|
|
5
|
+
|
|
6
|
+
if (!validator) {
|
|
7
|
+
console.error('Usage: node bin/validate.js <validator> [--json]');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { execSync } = await import('child_process');
|
|
12
|
+
const { existsSync } = await import('fs');
|
|
13
|
+
const { join } = await import('path');
|
|
14
|
+
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
|
|
17
|
+
const results = {
|
|
18
|
+
validator,
|
|
19
|
+
errors: 0,
|
|
20
|
+
warnings: 0,
|
|
21
|
+
passed: true,
|
|
22
|
+
issues: [],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function runCommand(cmd, options = {}) {
|
|
26
|
+
try {
|
|
27
|
+
const stdout = execSync(cmd, { cwd, stdio: ['pipe', 'pipe', 'pipe'], ...options }).toString();
|
|
28
|
+
return { success: true, stdout, stderr: '' };
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
stdout: err.stdout?.toString() || '',
|
|
33
|
+
stderr: err.stderr?.toString() || err.message,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const hasFrontend = existsSync(join(cwd, 'src/frontend')) || existsSync(join(cwd, 'frontend'));
|
|
39
|
+
const hasBackend = existsSync(join(cwd, 'src/backend')) || existsSync(join(cwd, 'backend'));
|
|
40
|
+
const hasSln = existsSync(join(cwd, 'src/backend/backend.sln')) ||
|
|
41
|
+
existsSync(join(cwd, 'src/backend/Backend.sln'));
|
|
42
|
+
|
|
43
|
+
const frontendDir = existsSync(join(cwd, 'src/frontend')) ? 'src/frontend' : 'frontend';
|
|
44
|
+
const backendDir = existsSync(join(cwd, 'src/backend')) ? 'src/backend' : 'backend';
|
|
45
|
+
|
|
46
|
+
switch (validator) {
|
|
47
|
+
case 'architecture': {
|
|
48
|
+
if (hasBackend && hasSln) {
|
|
49
|
+
const slnFile = hasSln ? (
|
|
50
|
+
existsSync(join(cwd, 'src/backend/backend.sln')) ? 'backend.sln' : 'Backend.sln'
|
|
51
|
+
) : '.';
|
|
52
|
+
const result = runCommand(`dotnet build ${slnFile} --no-restore -warnaserror`, { cwd: join(cwd, backendDir) });
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
results.errors++;
|
|
55
|
+
results.passed = false;
|
|
56
|
+
results.issues.push({ level: 'error', message: `dotnet build failed: ${result.stderr.split('\n')[0]}` });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case 'nextjs-component':
|
|
63
|
+
case 'packages':
|
|
64
|
+
case 'design-system': {
|
|
65
|
+
if (hasFrontend) {
|
|
66
|
+
const result = runCommand('npx tsc --noEmit', { cwd: join(cwd, frontendDir) });
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
results.errors++;
|
|
69
|
+
results.passed = false;
|
|
70
|
+
results.issues.push({ level: 'error', message: `TypeScript errors found. Run: npx tsc --noEmit` });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case 'ui-contrast':
|
|
77
|
+
case 'css': {
|
|
78
|
+
results.issues.push({ level: 'info', message: `${validator} requires manual review` });
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
default:
|
|
83
|
+
results.issues.push({ level: 'warning', message: `Unknown validator: ${validator}` });
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (isJson) {
|
|
88
|
+
console.log(JSON.stringify(results, null, 2));
|
|
89
|
+
} else {
|
|
90
|
+
const icon = results.passed ? '✅' : '❌';
|
|
91
|
+
console.log(`${icon} Validator: ${validator} — ${results.passed ? 'PASSED' : `FAILED (${results.errors} errors)`}`);
|
|
92
|
+
for (const issue of results.issues) {
|
|
93
|
+
console.log(` [${issue.level}] ${issue.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
process.exit(results.passed ? 0 : 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.41.0",
|
|
4
4
|
"description": "MORPH-SPEC: NLH (Natural Language Harness) for spec-driven development with Claude Code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"morph",
|
|
21
21
|
"polymorphism"
|
|
22
22
|
],
|
|
23
|
-
"main": "src/index.js",
|
|
24
23
|
"bin": {
|
|
25
24
|
"morph-spec": "bin/morph-spec.js"
|
|
26
25
|
},
|
|
@@ -49,6 +48,7 @@
|
|
|
49
48
|
"freshness": "node scripts/check-freshness.js",
|
|
50
49
|
"verify": "npm run generate:check && npm run generate-standards:check && npm run validate && npm run freshness && npm test",
|
|
51
50
|
"test": "node scripts/run-tests.mjs",
|
|
51
|
+
"test:changed": "node scripts/test-changed.mjs",
|
|
52
52
|
"test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node scripts/run-tests.mjs",
|
|
53
53
|
"test:coverage:summary": "c8 --reporter=text-summary node scripts/run-tests.mjs",
|
|
54
54
|
"docs": "jsdoc -c jsdoc.json",
|
|
@@ -1,53 +1,80 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { resolve, dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { registerProject } from '../../lib/projects/registry.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
/** Where the hash of the package.json that produced node_modules is kept. */
|
|
12
|
+
export const DEPS_STAMP = '.morph-deps-stamp';
|
|
13
|
+
|
|
14
|
+
/** Hash of the dashboard's package.json — the dependency set it declares. */
|
|
15
|
+
export function depsHash(dashboardDir) {
|
|
16
|
+
return createHash('sha256').update(readFileSync(join(dashboardDir, 'package.json'))).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Does the dashboard need an `npm install`? Not only when node_modules is
|
|
21
|
+
* missing: an install made before a dependency was added fails at import time,
|
|
22
|
+
* so node_modules must have been produced by THIS package.json.
|
|
23
|
+
*/
|
|
24
|
+
export function needsInstall(dashboardDir) {
|
|
25
|
+
const stamp = join(dashboardDir, 'node_modules', DEPS_STAMP);
|
|
26
|
+
if (!existsSync(stamp)) return true;
|
|
27
|
+
try {
|
|
28
|
+
return readFileSync(stamp, 'utf8').trim() !== depsHash(dashboardDir);
|
|
29
|
+
} catch {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function dashboardCommand(options = {}) {
|
|
35
|
+
const port = options.port || '3200';
|
|
36
|
+
const dashboardDir = resolve(__dirname, '..', '..', 'dashboard');
|
|
37
|
+
|
|
38
|
+
if (needsInstall(dashboardDir)) {
|
|
39
|
+
console.log(chalk.yellow('Installing dashboard dependencies...'));
|
|
40
|
+
const install = spawn('npm', ['install'], {
|
|
41
|
+
cwd: dashboardDir,
|
|
42
|
+
stdio: 'inherit',
|
|
43
|
+
shell: true,
|
|
44
|
+
});
|
|
45
|
+
await new Promise((res, rej) => {
|
|
46
|
+
install.on('close', (code) => (code === 0 ? res() : rej(new Error(`npm install failed with code ${code}`))));
|
|
47
|
+
});
|
|
48
|
+
writeFileSync(join(dashboardDir, 'node_modules', DEPS_STAMP), depsHash(dashboardDir));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Opening the dashboard in a project puts it in the portfolio.
|
|
52
|
+
registerProject(process.cwd(), 'dashboard');
|
|
53
|
+
|
|
54
|
+
console.log(chalk.cyan(`\n Starting morph-spec dashboard at http://localhost:${port}\n`));
|
|
55
|
+
|
|
56
|
+
const env = {
|
|
57
|
+
...process.env,
|
|
58
|
+
MORPH_PROJECT_DIR: process.cwd(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const viteArgs = ['vite', '--port', port];
|
|
62
|
+
if (options.open !== false) viteArgs.push('--open');
|
|
63
|
+
|
|
64
|
+
const vite = spawn('npx', viteArgs, {
|
|
65
|
+
cwd: dashboardDir,
|
|
66
|
+
stdio: 'inherit',
|
|
67
|
+
shell: true,
|
|
68
|
+
env,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
vite.on('error', (err) => {
|
|
72
|
+
console.error(chalk.red(`Failed to start dashboard: ${err.message}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
process.on('SIGINT', () => {
|
|
77
|
+
vite.kill();
|
|
78
|
+
process.exit(0);
|
|
79
|
+
});
|
|
80
|
+
}
|