okengine 0.1.4
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/AGENTS.md +91 -0
- package/README.md +327 -0
- package/docs/spec/complete-example.md +1188 -0
- package/docs/spec/console.md +739 -0
- package/docs/spec/four-applications.md +1188 -0
- package/docs/spec/unified-theory.md +489 -0
- package/package.json +152 -0
- package/spec/manifest.v1.schema.json +624 -0
- package/src/auth/api-keys.ts +168 -0
- package/src/auth/attenuation.ts +116 -0
- package/src/auth/auth.test.ts +273 -0
- package/src/auth/cross-plane.ts +98 -0
- package/src/auth/index.ts +112 -0
- package/src/auth/invites.ts +66 -0
- package/src/auth/invoke-as.ts +49 -0
- package/src/auth/operator.ts +178 -0
- package/src/auth/planes.ts +102 -0
- package/src/auth/plugin.ts +44 -0
- package/src/auth/roles.ts +84 -0
- package/src/auth/sessions.ts +418 -0
- package/src/auth/tables.ts +131 -0
- package/src/cli/args.ts +50 -0
- package/src/cli/branch.ts +75 -0
- package/src/cli/build.ts +86 -0
- package/src/cli/client-add.test.ts +140 -0
- package/src/cli/client-add.ts +164 -0
- package/src/cli/completion.test.ts +58 -0
- package/src/cli/completion.ts +296 -0
- package/src/cli/dev-app-runner.ts +77 -0
- package/src/cli/dev.test.ts +435 -0
- package/src/cli/dev.ts +594 -0
- package/src/cli/doc-drift.test.ts +146 -0
- package/src/cli/doc-drift.ts +397 -0
- package/src/cli/docker-cli.test.ts +221 -0
- package/src/cli/docker.ts +127 -0
- package/src/cli/doctor-diff-examples.ts +92 -0
- package/src/cli/doctor-diff.test.ts +91 -0
- package/src/cli/doctor-diff.ts +274 -0
- package/src/cli/doctor-pii.test.ts +98 -0
- package/src/cli/doctor-pii.ts +151 -0
- package/src/cli/doctor.test.ts +150 -0
- package/src/cli/doctor.ts +223 -0
- package/src/cli/eval.ts +122 -0
- package/src/cli/exit.ts +19 -0
- package/src/cli/gates-list.test.ts +47 -0
- package/src/cli/gates-list.ts +84 -0
- package/src/cli/images.ts +277 -0
- package/src/cli/index.ts +99 -0
- package/src/cli/json-out.test.ts +129 -0
- package/src/cli/load-config.ts +71 -0
- package/src/cli/mcp-from-console.ts +50 -0
- package/src/cli/privacy-erase.ts +73 -0
- package/src/cli/registry.ts +455 -0
- package/src/cli/safe-defaults.test.ts +92 -0
- package/src/cli/schema.ts +196 -0
- package/src/cli/stack.ts +82 -0
- package/src/cli/start.ts +106 -0
- package/src/cli/upgrade.ts +105 -0
- package/src/cli/vault-cmd.ts +140 -0
- package/src/client/budget-entry.ts +34 -0
- package/src/client/budget.test.ts +17 -0
- package/src/client/create.test.ts +159 -0
- package/src/client/create.ts +152 -0
- package/src/client/errors.ts +60 -0
- package/src/client/index.ts +35 -0
- package/src/client/notes-contract.test.ts +289 -0
- package/src/client/transport.test.ts +123 -0
- package/src/client/transport.ts +254 -0
- package/src/client/types.ts +282 -0
- package/src/compiler/aot.test.ts +248 -0
- package/src/compiler/aot.ts +182 -0
- package/src/compiler/differential.test.ts +241 -0
- package/src/compiler/dynamic.ts +54 -0
- package/src/compiler/effects-infer.ts +541 -0
- package/src/compiler/emit.ts +40 -0
- package/src/compiler/extract.test.ts +177 -0
- package/src/compiler/extract.ts +1368 -0
- package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
- package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
- package/src/compiler/fixtures/skyport/src/app.ts +10 -0
- package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
- package/src/compiler/fixtures/skyport/src/core.ts +3 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
- package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
- package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
- package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
- package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
- package/src/compiler/fixtures/skyport.expected.json +241 -0
- package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
- package/src/compiler/http-parse.ts +214 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/response.ts +89 -0
- package/src/compiler/sucrose.ts +238 -0
- package/src/config/index.ts +167 -0
- package/src/console/budget.test.ts +73 -0
- package/src/console/index.ts +30 -0
- package/src/console/server/access.test.ts +370 -0
- package/src/console/server/access.ts +777 -0
- package/src/console/server/ai.test.ts +172 -0
- package/src/console/server/ai.ts +632 -0
- package/src/console/server/app.ts +294 -0
- package/src/console/server/auth-rate.test.ts +81 -0
- package/src/console/server/auth-rate.ts +72 -0
- package/src/console/server/bind.ts +25 -0
- package/src/console/server/channels.test.ts +179 -0
- package/src/console/server/channels.ts +469 -0
- package/src/console/server/claim.test.ts +66 -0
- package/src/console/server/claim.ts +134 -0
- package/src/console/server/clock.test.ts +239 -0
- package/src/console/server/clock.ts +390 -0
- package/src/console/server/console.test.ts +338 -0
- package/src/console/server/diff.test.ts +299 -0
- package/src/console/server/diff.ts +352 -0
- package/src/console/server/dry-run.audit.test.ts +118 -0
- package/src/console/server/flows-invoke.test.ts +148 -0
- package/src/console/server/flows.ts +3633 -0
- package/src/console/server/gates.test.ts +360 -0
- package/src/console/server/gates.ts +869 -0
- package/src/console/server/index.ts +141 -0
- package/src/console/server/live.test.ts +30 -0
- package/src/console/server/live.ts +109 -0
- package/src/console/server/plugin.ts +50 -0
- package/src/console/server/plugins.test.ts +131 -0
- package/src/console/server/plugins.ts +429 -0
- package/src/console/server/runs-pii.ts +98 -0
- package/src/console/server/security-headers.ts +97 -0
- package/src/console/server/security.gate.test.ts +286 -0
- package/src/console/server/serve.ts +319 -0
- package/src/console/server/signals.test.ts +88 -0
- package/src/console/server/signals.ts +282 -0
- package/src/console/server/state.ts +982 -0
- package/src/console/server/store.test.ts +233 -0
- package/src/console/server/store.ts +861 -0
- package/src/console/server/structural.ts +95 -0
- package/src/console/server/vault.test.ts +251 -0
- package/src/console/server/vault.ts +430 -0
- package/src/console/ui/access/AccessA11yView.tsx +237 -0
- package/src/console/ui/access/a11y.test.tsx +122 -0
- package/src/console/ui/access/acknowledgement.test.ts +13 -0
- package/src/console/ui/access/acknowledgement.ts +35 -0
- package/src/console/ui/access/blast-radius.test.ts +26 -0
- package/src/console/ui/access/blast-radius.ts +40 -0
- package/src/console/ui/access/confirmation.test.ts +38 -0
- package/src/console/ui/access/confirmation.ts +36 -0
- package/src/console/ui/access/fixture.ts +203 -0
- package/src/console/ui/access/grantable.test.ts +17 -0
- package/src/console/ui/access/grantable.ts +36 -0
- package/src/console/ui/access/hygiene.test.ts +12 -0
- package/src/console/ui/access/hygiene.ts +39 -0
- package/src/console/ui/access/index.ts +54 -0
- package/src/console/ui/access/provenance.test.ts +12 -0
- package/src/console/ui/access/provenance.ts +29 -0
- package/src/console/ui/access/search.test.ts +17 -0
- package/src/console/ui/access/search.ts +72 -0
- package/src/console/ui/access/types.ts +111 -0
- package/src/console/ui/ai/AiA11yView.tsx +265 -0
- package/src/console/ui/ai/a11y.test.tsx +99 -0
- package/src/console/ui/ai/findings.ts +41 -0
- package/src/console/ui/ai/fixture.ts +182 -0
- package/src/console/ui/ai/format.ts +90 -0
- package/src/console/ui/ai/group.test.ts +37 -0
- package/src/console/ui/ai/group.ts +88 -0
- package/src/console/ui/ai/index.ts +66 -0
- package/src/console/ui/ai/promotion.test.ts +77 -0
- package/src/console/ui/ai/promotion.ts +150 -0
- package/src/console/ui/ai/search.test.ts +40 -0
- package/src/console/ui/ai/search.ts +89 -0
- package/src/console/ui/ai/types.ts +139 -0
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
- package/src/console/ui/architecture/a11y.test.tsx +97 -0
- package/src/console/ui/architecture/boundary.ts +42 -0
- package/src/console/ui/architecture/declared.ts +165 -0
- package/src/console/ui/architecture/fixture.ts +74 -0
- package/src/console/ui/architecture/index.ts +84 -0
- package/src/console/ui/architecture/layers.ts +64 -0
- package/src/console/ui/architecture/layout.ts +68 -0
- package/src/console/ui/architecture/pathologies.ts +260 -0
- package/src/console/ui/architecture/search.ts +145 -0
- package/src/console/ui/architecture/traffic.ts +102 -0
- package/src/console/ui/architecture/types.ts +144 -0
- package/src/console/ui/architecture/view.test.ts +165 -0
- package/src/console/ui/architecture/view.ts +516 -0
- package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
- package/src/console/ui/channels/a11y.test.tsx +96 -0
- package/src/console/ui/channels/confirmation.test.ts +37 -0
- package/src/console/ui/channels/confirmation.ts +27 -0
- package/src/console/ui/channels/fallback.test.ts +15 -0
- package/src/console/ui/channels/fallback.ts +20 -0
- package/src/console/ui/channels/findings.ts +38 -0
- package/src/console/ui/channels/fixture.ts +134 -0
- package/src/console/ui/channels/group.ts +25 -0
- package/src/console/ui/channels/index.ts +57 -0
- package/src/console/ui/channels/locale.test.ts +17 -0
- package/src/console/ui/channels/locale.ts +31 -0
- package/src/console/ui/channels/mask.test.ts +14 -0
- package/src/console/ui/channels/mask.ts +12 -0
- package/src/console/ui/channels/search.ts +58 -0
- package/src/console/ui/channels/taxonomy.test.ts +17 -0
- package/src/console/ui/channels/taxonomy.ts +49 -0
- package/src/console/ui/channels/types.ts +117 -0
- package/src/console/ui/clock/ClockA11yView.tsx +182 -0
- package/src/console/ui/clock/a11y.test.tsx +99 -0
- package/src/console/ui/clock/confirmation.test.ts +32 -0
- package/src/console/ui/clock/confirmation.ts +34 -0
- package/src/console/ui/clock/findings.ts +40 -0
- package/src/console/ui/clock/fixture.ts +111 -0
- package/src/console/ui/clock/health.test.ts +25 -0
- package/src/console/ui/clock/health.ts +61 -0
- package/src/console/ui/clock/index.ts +49 -0
- package/src/console/ui/clock/search.test.ts +39 -0
- package/src/console/ui/clock/search.ts +76 -0
- package/src/console/ui/clock/timeline.test.ts +44 -0
- package/src/console/ui/clock/timeline.ts +41 -0
- package/src/console/ui/clock/types.ts +78 -0
- package/src/console/ui/clock/waiting-on.test.ts +34 -0
- package/src/console/ui/clock/waiting-on.ts +83 -0
- package/src/console/ui/confirmation.unify.test.ts +92 -0
- package/src/console/ui/diff/DiffA11yView.tsx +85 -0
- package/src/console/ui/diff/a11y.test.tsx +99 -0
- package/src/console/ui/diff/fixture.ts +85 -0
- package/src/console/ui/diff/group.test.ts +58 -0
- package/src/console/ui/diff/group.ts +97 -0
- package/src/console/ui/diff/index.ts +38 -0
- package/src/console/ui/diff/search.test.ts +32 -0
- package/src/console/ui/diff/search.ts +75 -0
- package/src/console/ui/diff/summary.ts +74 -0
- package/src/console/ui/diff/types.ts +48 -0
- package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
- package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
- package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
- package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
- package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
- package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
- package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
- package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
- package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
- package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
- package/src/console/ui/dist/index.html +14 -0
- package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
- package/src/console/ui/flows/a11y.test.tsx +105 -0
- package/src/console/ui/flows/buffer.test.ts +91 -0
- package/src/console/ui/flows/buffer.ts +118 -0
- package/src/console/ui/flows/confirmation.ts +131 -0
- package/src/console/ui/flows/contract.test.ts +89 -0
- package/src/console/ui/flows/contract.ts +353 -0
- package/src/console/ui/flows/fixture.ts +81 -0
- package/src/console/ui/flows/graph.test.ts +101 -0
- package/src/console/ui/flows/graph.ts +549 -0
- package/src/console/ui/flows/index.ts +87 -0
- package/src/console/ui/flows/save-as-test.test.ts +47 -0
- package/src/console/ui/flows/save-as-test.ts +99 -0
- package/src/console/ui/flows/search.ts +255 -0
- package/src/console/ui/flows/tiers.ts +140 -0
- package/src/console/ui/gates/GatesA11yView.tsx +192 -0
- package/src/console/ui/gates/a11y.test.tsx +116 -0
- package/src/console/ui/gates/audit.test.ts +40 -0
- package/src/console/ui/gates/audit.ts +63 -0
- package/src/console/ui/gates/denial.test.ts +36 -0
- package/src/console/ui/gates/denial.ts +51 -0
- package/src/console/ui/gates/findings.ts +19 -0
- package/src/console/ui/gates/fixture.ts +189 -0
- package/src/console/ui/gates/group.test.ts +30 -0
- package/src/console/ui/gates/group.ts +102 -0
- package/src/console/ui/gates/index.ts +47 -0
- package/src/console/ui/gates/search.test.ts +49 -0
- package/src/console/ui/gates/search.ts +115 -0
- package/src/console/ui/gates/types.ts +121 -0
- package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
- package/src/console/ui/overview/a11y.test.tsx +113 -0
- package/src/console/ui/overview/busiest.ts +26 -0
- package/src/console/ui/overview/compose.ts +133 -0
- package/src/console/ui/overview/cost.test.ts +39 -0
- package/src/console/ui/overview/cost.ts +138 -0
- package/src/console/ui/overview/fixture.ts +161 -0
- package/src/console/ui/overview/golden.ts +57 -0
- package/src/console/ui/overview/index.ts +59 -0
- package/src/console/ui/overview/rank.test.ts +136 -0
- package/src/console/ui/overview/rank.ts +210 -0
- package/src/console/ui/overview/slo.test.ts +73 -0
- package/src/console/ui/overview/slo.ts +229 -0
- package/src/console/ui/overview/types.ts +121 -0
- package/src/console/ui/overview/verdict.ts +131 -0
- package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
- package/src/console/ui/plugins/a11y.test.tsx +97 -0
- package/src/console/ui/plugins/command.test.ts +22 -0
- package/src/console/ui/plugins/command.ts +27 -0
- package/src/console/ui/plugins/findings.ts +39 -0
- package/src/console/ui/plugins/fixture.ts +233 -0
- package/src/console/ui/plugins/group.test.ts +38 -0
- package/src/console/ui/plugins/group.ts +67 -0
- package/src/console/ui/plugins/index.ts +40 -0
- package/src/console/ui/plugins/search.ts +67 -0
- package/src/console/ui/plugins/types.ts +115 -0
- package/src/console/ui/runs/RunsA11yView.tsx +267 -0
- package/src/console/ui/runs/a11y.test.tsx +99 -0
- package/src/console/ui/runs/explain.test.ts +23 -0
- package/src/console/ui/runs/explain.ts +32 -0
- package/src/console/ui/runs/fixture.ts +189 -0
- package/src/console/ui/runs/group.test.ts +24 -0
- package/src/console/ui/runs/group.ts +85 -0
- package/src/console/ui/runs/histogram.test.ts +31 -0
- package/src/console/ui/runs/histogram.ts +85 -0
- package/src/console/ui/runs/index.ts +75 -0
- package/src/console/ui/runs/project.ts +122 -0
- package/src/console/ui/runs/query.test.ts +50 -0
- package/src/console/ui/runs/query.ts +283 -0
- package/src/console/ui/runs/search.test.ts +45 -0
- package/src/console/ui/runs/search.ts +141 -0
- package/src/console/ui/runs/trace-link.test.ts +30 -0
- package/src/console/ui/runs/trace-link.ts +99 -0
- package/src/console/ui/runs/types.ts +137 -0
- package/src/console/ui/shell/App.tsx +56 -0
- package/src/console/ui/shell/client.ts +1715 -0
- package/src/console/ui/shell/components/ui.tsx +110 -0
- package/src/console/ui/shell/index.html +12 -0
- package/src/console/ui/shell/layout/Shell.tsx +95 -0
- package/src/console/ui/shell/main.tsx +281 -0
- package/src/console/ui/shell/panels/Access.tsx +5 -0
- package/src/console/ui/shell/panels/Ai.tsx +5 -0
- package/src/console/ui/shell/panels/Architecture.tsx +5 -0
- package/src/console/ui/shell/panels/Channels.tsx +5 -0
- package/src/console/ui/shell/panels/Clock.tsx +5 -0
- package/src/console/ui/shell/panels/Diff.tsx +5 -0
- package/src/console/ui/shell/panels/Flows.tsx +5 -0
- package/src/console/ui/shell/panels/Gates.tsx +5 -0
- package/src/console/ui/shell/panels/Overview.tsx +6 -0
- package/src/console/ui/shell/panels/Plugins.tsx +5 -0
- package/src/console/ui/shell/panels/Runs.tsx +5 -0
- package/src/console/ui/shell/panels/Signals.tsx +5 -0
- package/src/console/ui/shell/panels/Store.tsx +5 -0
- package/src/console/ui/shell/panels/Traces.tsx +5 -0
- package/src/console/ui/shell/panels/Vault.tsx +5 -0
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
- package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
- package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
- package/src/console/ui/shell/setup/Wizard.tsx +189 -0
- package/src/console/ui/shell/styles.css +75 -0
- package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
- package/src/console/ui/signals/a11y.test.tsx +99 -0
- package/src/console/ui/signals/confirmation.test.ts +39 -0
- package/src/console/ui/signals/confirmation.ts +63 -0
- package/src/console/ui/signals/dry-run.test.ts +18 -0
- package/src/console/ui/signals/dry-run.ts +36 -0
- package/src/console/ui/signals/durable.test.ts +22 -0
- package/src/console/ui/signals/durable.ts +41 -0
- package/src/console/ui/signals/findings.ts +33 -0
- package/src/console/ui/signals/fixture.ts +242 -0
- package/src/console/ui/signals/group.test.ts +32 -0
- package/src/console/ui/signals/group.ts +49 -0
- package/src/console/ui/signals/index.ts +52 -0
- package/src/console/ui/signals/monitor.test.ts +30 -0
- package/src/console/ui/signals/monitor.ts +79 -0
- package/src/console/ui/signals/schema-form.test.ts +34 -0
- package/src/console/ui/signals/schema-form.ts +112 -0
- package/src/console/ui/signals/search.ts +102 -0
- package/src/console/ui/signals/types.ts +71 -0
- package/src/console/ui/store/StoreA11yView.tsx +199 -0
- package/src/console/ui/store/a11y.test.tsx +99 -0
- package/src/console/ui/store/cache-view.test.ts +13 -0
- package/src/console/ui/store/cache-view.ts +36 -0
- package/src/console/ui/store/confirmation.test.ts +38 -0
- package/src/console/ui/store/confirmation.ts +55 -0
- package/src/console/ui/store/dry-run.test.ts +15 -0
- package/src/console/ui/store/dry-run.ts +29 -0
- package/src/console/ui/store/fixture.ts +163 -0
- package/src/console/ui/store/group.test.ts +21 -0
- package/src/console/ui/store/group.ts +41 -0
- package/src/console/ui/store/index.ts +40 -0
- package/src/console/ui/store/search.ts +78 -0
- package/src/console/ui/store/types.ts +65 -0
- package/src/console/ui/store/will-not-fire.test.ts +19 -0
- package/src/console/ui/store/will-not-fire.ts +40 -0
- package/src/console/ui/traces/TracesA11yView.tsx +200 -0
- package/src/console/ui/traces/a11y.test.tsx +97 -0
- package/src/console/ui/traces/chain.test.ts +61 -0
- package/src/console/ui/traces/chain.ts +144 -0
- package/src/console/ui/traces/critical-path.test.ts +24 -0
- package/src/console/ui/traces/critical-path.ts +59 -0
- package/src/console/ui/traces/filter.test.ts +78 -0
- package/src/console/ui/traces/filter.ts +112 -0
- package/src/console/ui/traces/fixture.ts +126 -0
- package/src/console/ui/traces/fold.test.ts +67 -0
- package/src/console/ui/traces/fold.ts +170 -0
- package/src/console/ui/traces/index.ts +64 -0
- package/src/console/ui/traces/mini.ts +65 -0
- package/src/console/ui/traces/replay.test.ts +24 -0
- package/src/console/ui/traces/replay.ts +35 -0
- package/src/console/ui/traces/sampling.test.ts +26 -0
- package/src/console/ui/traces/sampling.ts +66 -0
- package/src/console/ui/traces/search.ts +148 -0
- package/src/console/ui/traces/tier.ts +72 -0
- package/src/console/ui/traces/types.ts +125 -0
- package/src/console/ui/tsconfig.json +20 -0
- package/src/console/ui/vault/VaultA11yView.tsx +177 -0
- package/src/console/ui/vault/a11y.test.tsx +97 -0
- package/src/console/ui/vault/blast-radius.test.ts +35 -0
- package/src/console/ui/vault/blast-radius.ts +55 -0
- package/src/console/ui/vault/confirmation.test.ts +41 -0
- package/src/console/ui/vault/confirmation.ts +39 -0
- package/src/console/ui/vault/dormant.test.ts +37 -0
- package/src/console/ui/vault/dormant.ts +28 -0
- package/src/console/ui/vault/export-safe.test.ts +30 -0
- package/src/console/ui/vault/export-safe.ts +65 -0
- package/src/console/ui/vault/fixture.ts +75 -0
- package/src/console/ui/vault/group.test.ts +25 -0
- package/src/console/ui/vault/group.ts +50 -0
- package/src/console/ui/vault/index.ts +45 -0
- package/src/console/ui/vault/search.ts +62 -0
- package/src/console/ui/vault/types.ts +57 -0
- package/src/console/ui/vite.config.ts +84 -0
- package/src/console/xss.gate.test.ts +118 -0
- package/src/docker/compose.ts +226 -0
- package/src/docker/credentials.ts +25 -0
- package/src/docker/derive.ts +83 -0
- package/src/docker/docker.test.ts +185 -0
- package/src/docker/dockerfile.integration.test.ts +170 -0
- package/src/docker/dockerfile.ts +39 -0
- package/src/docker/helpers.ts +103 -0
- package/src/docker/index.ts +52 -0
- package/src/docker/pin.ts +90 -0
- package/src/docker/recipes/index.ts +33 -0
- package/src/docker/recipes/postgres.ts +18 -0
- package/src/docker/recipes/redis.ts +18 -0
- package/src/docker/stack.integration.test.ts +140 -0
- package/src/docker/stack.ts +62 -0
- package/src/docker/types.ts +134 -0
- package/src/drivers/ai-anthropic.ts +128 -0
- package/src/drivers/ai-mock.ts +82 -0
- package/src/drivers/ai-openai-compatible.ts +139 -0
- package/src/drivers/ai-providers.test.ts +163 -0
- package/src/drivers/ai-types.ts +83 -0
- package/src/drivers/bun-native-completeness.test.ts +155 -0
- package/src/drivers/channel-console.ts +118 -0
- package/src/drivers/channel-fcm.ts +96 -0
- package/src/drivers/channel-resend.ts +27 -0
- package/src/drivers/channel-smtp.ts +33 -0
- package/src/drivers/channel-types.ts +160 -0
- package/src/drivers/channel-unifonic.ts +79 -0
- package/src/drivers/channel-wa-cloud.ts +84 -0
- package/src/drivers/channel-webpush.ts +317 -0
- package/src/drivers/conformance.test.ts +89 -0
- package/src/drivers/conformance.ts +162 -0
- package/src/drivers/fs.ts +73 -0
- package/src/drivers/index.ts +194 -0
- package/src/drivers/kv-lua.ts +222 -0
- package/src/drivers/memory.ts +472 -0
- package/src/drivers/pgvector.ts +130 -0
- package/src/drivers/postgres.ts +221 -0
- package/src/drivers/redis.ts +254 -0
- package/src/drivers/s3.ts +129 -0
- package/src/drivers/signal-engine.ts +690 -0
- package/src/drivers/signal-memory.ts +27 -0
- package/src/drivers/signal-nats.ts +131 -0
- package/src/drivers/signal-postgres.ts +1089 -0
- package/src/drivers/signal-redis.ts +275 -0
- package/src/drivers/signal-types.ts +328 -0
- package/src/drivers/sqlite.ts +53 -0
- package/src/drivers/types.ts +334 -0
- package/src/drivers/vault-dotenv-parse.ts +56 -0
- package/src/drivers/vault-env.ts +101 -0
- package/src/drivers/vault-infisical.ts +61 -0
- package/src/drivers/vault-managed.ts +42 -0
- package/src/drivers/vault-memory.ts +38 -0
- package/src/drivers/vault-openbao.ts +80 -0
- package/src/drivers/vault-sops.ts +252 -0
- package/src/drivers/vault-types.ts +84 -0
- package/src/elements/ai/declare.ts +206 -0
- package/src/elements/ai/eval.ts +113 -0
- package/src/elements/ai/pii.ts +108 -0
- package/src/elements/ai/runtime.ts +540 -0
- package/src/elements/ai/schema.ts +194 -0
- package/src/elements/ai.test.ts +393 -0
- package/src/elements/ai.ts +56 -0
- package/src/elements/channel/consent.ts +81 -0
- package/src/elements/channel/costs.test.ts +62 -0
- package/src/elements/channel/costs.ts +130 -0
- package/src/elements/channel/declare.ts +141 -0
- package/src/elements/channel/email-auth.test.ts +47 -0
- package/src/elements/channel/email-auth.ts +109 -0
- package/src/elements/channel/locale.ts +97 -0
- package/src/elements/channel/mask.test.ts +19 -0
- package/src/elements/channel/mask.ts +60 -0
- package/src/elements/channel/mime.ts +23 -0
- package/src/elements/channel/outcomes.test.ts +40 -0
- package/src/elements/channel/outcomes.ts +126 -0
- package/src/elements/channel/receipts.ts +121 -0
- package/src/elements/channel/runtime.ts +500 -0
- package/src/elements/channel/suppression.ts +136 -0
- package/src/elements/channel.test.ts +232 -0
- package/src/elements/channel.ts +109 -0
- package/src/elements/clock/actions.ts +170 -0
- package/src/elements/clock/declare.ts +58 -0
- package/src/elements/clock/dst.ts +197 -0
- package/src/elements/clock/durable.ts +140 -0
- package/src/elements/clock/duration.ts +29 -0
- package/src/elements/clock/health.test.ts +103 -0
- package/src/elements/clock/health.ts +112 -0
- package/src/elements/clock/leader.ts +127 -0
- package/src/elements/clock/reconcile.ts +241 -0
- package/src/elements/clock/runtime.ts +243 -0
- package/src/elements/clock/schedule.ts +262 -0
- package/src/elements/clock/time-travel.ts +53 -0
- package/src/elements/clock.test.ts +365 -0
- package/src/elements/clock.ts +80 -0
- package/src/elements/gate/declare.ts +134 -0
- package/src/elements/gate/permissions.ts +104 -0
- package/src/elements/gate/runtime.ts +177 -0
- package/src/elements/gate/strategies.ts +388 -0
- package/src/elements/gate.test.ts +145 -0
- package/src/elements/gate.ts +41 -0
- package/src/elements/index.ts +169 -0
- package/src/elements/signal/chaos-child.ts +47 -0
- package/src/elements/signal/declare.ts +77 -0
- package/src/elements/signal/dry-run-replay.test.ts +95 -0
- package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
- package/src/elements/signal/reconcile.test.ts +23 -0
- package/src/elements/signal/reconcile.ts +122 -0
- package/src/elements/signal/runtime.ts +94 -0
- package/src/elements/signal.test.ts +457 -0
- package/src/elements/signal.ts +26 -0
- package/src/elements/store/cache.ts +197 -0
- package/src/elements/store/classify.ts +125 -0
- package/src/elements/store/declare.ts +200 -0
- package/src/elements/store/files-policy.test.ts +31 -0
- package/src/elements/store/files-policy.ts +70 -0
- package/src/elements/store/replica.ts +68 -0
- package/src/elements/store/runtime.ts +393 -0
- package/src/elements/store/sql-condition.ts +205 -0
- package/src/elements/store/sql-session.ts +579 -0
- package/src/elements/store/table.ts +282 -0
- package/src/elements/store.test.ts +370 -0
- package/src/elements/store.ts +99 -0
- package/src/elements/vault/declare.ts +143 -0
- package/src/elements/vault/fingerprint.ts +30 -0
- package/src/elements/vault/redact.ts +52 -0
- package/src/elements/vault/runtime.ts +461 -0
- package/src/elements/vault.test.ts +296 -0
- package/src/elements/vault.ts +44 -0
- package/src/index.ts +183 -0
- package/src/kernel/adopt-routes.ts +194 -0
- package/src/kernel/app.ts +1068 -0
- package/src/kernel/auth-resolve.ts +93 -0
- package/src/kernel/boot.test.ts +173 -0
- package/src/kernel/boot.ts +439 -0
- package/src/kernel/budget-entry.ts +55 -0
- package/src/kernel/budget.test.ts +19 -0
- package/src/kernel/call.test.ts +83 -0
- package/src/kernel/capability.ts +113 -0
- package/src/kernel/dry-run.test.ts +64 -0
- package/src/kernel/dry-run.ts +171 -0
- package/src/kernel/effects.test.ts +89 -0
- package/src/kernel/effects.ts +140 -0
- package/src/kernel/errors.registry.test.ts +39 -0
- package/src/kernel/errors.ts +225 -0
- package/src/kernel/flow.test.ts +154 -0
- package/src/kernel/flow.ts +290 -0
- package/src/kernel/fx.test.ts +245 -0
- package/src/kernel/fx.ts +996 -0
- package/src/kernel/hook-timing.test.ts +72 -0
- package/src/kernel/hook-timing.ts +190 -0
- package/src/kernel/hooks.test.ts +181 -0
- package/src/kernel/hooks.ts +275 -0
- package/src/kernel/index.ts +278 -0
- package/src/kernel/journal.ts +405 -0
- package/src/kernel/journey.ts +75 -0
- package/src/kernel/on.ts +74 -0
- package/src/kernel/pipeline.test.ts +349 -0
- package/src/kernel/pipeline.ts +245 -0
- package/src/kernel/plug.ts +82 -0
- package/src/kernel/plugin/capabilities.test.ts +84 -0
- package/src/kernel/plugin/conflicts.test.ts +106 -0
- package/src/kernel/plugin/decorate.test.ts +111 -0
- package/src/kernel/plugin/scoping.test.ts +138 -0
- package/src/kernel/plugin.ts +485 -0
- package/src/kernel/rate-limit.ts +25 -0
- package/src/kernel/registry.ts +398 -0
- package/src/kernel/router.test.ts +114 -0
- package/src/kernel/router.ts +556 -0
- package/src/kernel/routing-budget.test.ts +19 -0
- package/src/kernel/run-telemetry.ts +70 -0
- package/src/kernel/triggers.ts +324 -0
- package/src/kernel/unit.ts +88 -0
- package/src/manifest/diff.test.ts +866 -0
- package/src/manifest/diff.ts +1670 -0
- package/src/manifest/fixtures/base.manifest.json +112 -0
- package/src/manifest/fixtures/skyport.excerpt.json +51 -0
- package/src/manifest/fixtures/skyport.manifest.json +153 -0
- package/src/manifest/index.ts +27 -0
- package/src/manifest/types.ts +358 -0
- package/src/manifest/undeclared.test.ts +82 -0
- package/src/manifest/undeclared.ts +65 -0
- package/src/manifest/validate.test.ts +105 -0
- package/src/manifest/validate.ts +174 -0
- package/src/mcp/authorization.ts +340 -0
- package/src/mcp/confirmation.ts +174 -0
- package/src/mcp/data.ts +90 -0
- package/src/mcp/index.ts +80 -0
- package/src/mcp/injection.gate.test.ts +176 -0
- package/src/mcp/mcp.test.ts +348 -0
- package/src/mcp/protocol.ts +172 -0
- package/src/mcp/server.ts +283 -0
- package/src/mcp/session.ts +139 -0
- package/src/mcp/tools.ts +391 -0
- package/src/plugins/catalogue.test.ts +48 -0
- package/src/plugins/catalogue.ts +113 -0
- package/src/plugins/node-import-scan.test.ts +28 -0
- package/src/plugins/node-import-scan.ts +98 -0
- package/src/plugins/supply-chain.test.ts +48 -0
- package/src/plugins/supply-chain.ts +460 -0
- package/src/release/index.ts +30 -0
- package/src/release/limits.ts +20 -0
- package/src/release/measure.ts +443 -0
- package/src/release/publish.ts +56 -0
- package/src/runs/bench.test.ts +93 -0
- package/src/runs/collect.ts +144 -0
- package/src/runs/drivers/clickhouse.ts +108 -0
- package/src/runs/drivers/files.ts +201 -0
- package/src/runs/drivers/memory.ts +94 -0
- package/src/runs/drivers/postgres.ts +110 -0
- package/src/runs/duckdb.ts +99 -0
- package/src/runs/index.ts +82 -0
- package/src/runs/outlier.ts +180 -0
- package/src/runs/parquet.ts +210 -0
- package/src/runs/privacy.ts +49 -0
- package/src/runs/runs.test.ts +324 -0
- package/src/runs/runtime.ts +229 -0
- package/src/runs/shred.ts +243 -0
- package/src/runs/telemetry.ts +10 -0
- package/src/runs/types.ts +179 -0
- package/src/runtime/bun.ts +140 -0
- package/src/runtime/cold-start.bench.ts +20 -0
- package/src/runtime/index.ts +46 -0
- package/src/runtime/primitives.ts +242 -0
- package/src/runtime/security.test.ts +170 -0
- package/src/runtime/security.ts +168 -0
- package/src/runtime/serve.test.ts +223 -0
- package/src/runtime/types.ts +217 -0
- package/src/runtime/web-standard.ts +77 -0
- package/src/test/create-test-app.test.ts +161 -0
- package/src/test/create-test-app.ts +533 -0
- package/src/test/index.ts +21 -0
- package/src/test/provisions.integration.test.ts +174 -0
- package/src/upgrade/codemods.test.ts +58 -0
- package/src/upgrade/codemods.ts +83 -0
- package/src/validation/index.ts +23 -0
- package/src/validation/standard-schema.test.ts +96 -0
- package/src/validation/standard-schema.ts +316 -0
package/src/mcp/tools.ts
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool handlers — Manifest, schemas, effects, traces, safe actions.
|
|
3
|
+
*
|
|
4
|
+
* All returns go through {@link asData}. Write tools require a fresh
|
|
5
|
+
* confirmation token (no session-level consent cache).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Manifest } from "../manifest/types.ts";
|
|
9
|
+
import type { WideEvent } from "../runs/types.ts";
|
|
10
|
+
import {
|
|
11
|
+
authorizeToolCall,
|
|
12
|
+
MCP_TOOL_POLICIES,
|
|
13
|
+
type McpToolPolicy,
|
|
14
|
+
} from "./authorization.ts";
|
|
15
|
+
import {
|
|
16
|
+
createConfirmationGate,
|
|
17
|
+
MCP_CONFIRM_PHRASE,
|
|
18
|
+
type ConfirmationGateOptions,
|
|
19
|
+
} from "./confirmation.ts";
|
|
20
|
+
import { asData, freezeData, type McpDataEnvelope } from "./data.ts";
|
|
21
|
+
import type { McpRequester } from "./session.ts";
|
|
22
|
+
|
|
23
|
+
/** Narrow context the MCP surface needs — no token forwarding. */
|
|
24
|
+
export interface McpContext {
|
|
25
|
+
/** Current Manifest snapshot. */
|
|
26
|
+
readonly getManifest: () => Manifest | null;
|
|
27
|
+
/** List runs/traces (already operator-scoped by the provider). */
|
|
28
|
+
readonly listRuns: () => Promise<readonly WideEvent[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Safe invoke adapter. Called only after confirmation. Must NOT receive
|
|
31
|
+
* the caller's Bearer token — capabilities are passed as structured input.
|
|
32
|
+
*/
|
|
33
|
+
readonly invokeFlow?: (input: {
|
|
34
|
+
readonly flowId: string;
|
|
35
|
+
readonly body: unknown;
|
|
36
|
+
readonly operatorId: string;
|
|
37
|
+
readonly reason: string;
|
|
38
|
+
}) => Promise<unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Structural propose adapter. Called only after confirmation.
|
|
41
|
+
*/
|
|
42
|
+
readonly proposeStructural?: (input: {
|
|
43
|
+
readonly title: string;
|
|
44
|
+
readonly relativePath: string;
|
|
45
|
+
readonly contents: string;
|
|
46
|
+
readonly operatorId: string;
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
}) => Promise<unknown>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Result of a tool call — always an inert data envelope or an error envelope. */
|
|
52
|
+
export type ToolCallResult =
|
|
53
|
+
| { readonly ok: true; readonly data: McpDataEnvelope }
|
|
54
|
+
| {
|
|
55
|
+
readonly ok: false;
|
|
56
|
+
readonly code: "unauthorized" | "forbidden" | "invalid" | "not-found";
|
|
57
|
+
readonly message: string;
|
|
58
|
+
readonly data: McpDataEnvelope;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create the tool runtime bound to a context + confirmation gate.
|
|
63
|
+
*
|
|
64
|
+
* @param ctx - Manifest / runs / action adapters
|
|
65
|
+
* @param confirmOptions - Confirmation gate options
|
|
66
|
+
*/
|
|
67
|
+
export function createToolRuntime(
|
|
68
|
+
ctx: McpContext,
|
|
69
|
+
confirmOptions: ConfirmationGateOptions = {},
|
|
70
|
+
): {
|
|
71
|
+
readonly listTools: () => readonly McpToolPolicy[];
|
|
72
|
+
readonly callTool: (
|
|
73
|
+
requester: McpRequester,
|
|
74
|
+
name: string,
|
|
75
|
+
args: Record<string, unknown>,
|
|
76
|
+
) => Promise<ToolCallResult>;
|
|
77
|
+
/** Exposed for tests — confirmation gate has no session consent cache. */
|
|
78
|
+
readonly confirmationSize: () => number;
|
|
79
|
+
} {
|
|
80
|
+
const confirm = createConfirmationGate(confirmOptions);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
listTools: () => MCP_TOOL_POLICIES,
|
|
84
|
+
confirmationSize: () => confirm.size(),
|
|
85
|
+
async callTool(requester, name, args) {
|
|
86
|
+
const policy = MCP_TOOL_POLICIES.find((p) => p.name === name);
|
|
87
|
+
|
|
88
|
+
// Scope / param checks first (do not burn a confirmation token on ACL miss).
|
|
89
|
+
const preflight = authorizeToolCall(name, args, requester.scopes, {
|
|
90
|
+
confirmed: policy?.mutability !== "write",
|
|
91
|
+
});
|
|
92
|
+
if (
|
|
93
|
+
!preflight.ok &&
|
|
94
|
+
preflight.reason !== "confirmation-required"
|
|
95
|
+
) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
code:
|
|
99
|
+
preflight.reason === "unknown-tool" ? "not-found" : "forbidden",
|
|
100
|
+
message: preflight.detail ?? preflight.reason,
|
|
101
|
+
data: asData(
|
|
102
|
+
{
|
|
103
|
+
tool: name,
|
|
104
|
+
reason: preflight.reason,
|
|
105
|
+
detail: preflight.detail ?? null,
|
|
106
|
+
},
|
|
107
|
+
"error",
|
|
108
|
+
),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Write path: consume a fresh per-call confirmation (no session cache).
|
|
113
|
+
if (policy?.mutability === "write") {
|
|
114
|
+
const token =
|
|
115
|
+
typeof args.confirmToken === "string" ? args.confirmToken : "";
|
|
116
|
+
const phrase =
|
|
117
|
+
typeof args.confirmation === "string" ? args.confirmation : "";
|
|
118
|
+
const reason = typeof args.reason === "string" ? args.reason : "";
|
|
119
|
+
const boundArgs = stripConfirmFields(args);
|
|
120
|
+
const consumed = confirm.consume({
|
|
121
|
+
tool: name,
|
|
122
|
+
args: boundArgs,
|
|
123
|
+
principalId: requester.principalId,
|
|
124
|
+
token,
|
|
125
|
+
phrase,
|
|
126
|
+
reason,
|
|
127
|
+
});
|
|
128
|
+
if (!consumed.ok) {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
code: "forbidden",
|
|
132
|
+
message: `write tool requires fresh human confirmation (${consumed.reason})`,
|
|
133
|
+
data: asData(
|
|
134
|
+
{
|
|
135
|
+
tool: name,
|
|
136
|
+
requiredPhrase: MCP_CONFIRM_PHRASE,
|
|
137
|
+
confirmVia: "oke.action.confirm",
|
|
138
|
+
reason: consumed.reason,
|
|
139
|
+
},
|
|
140
|
+
"error",
|
|
141
|
+
),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
switch (name) {
|
|
147
|
+
case "oke.manifest.get":
|
|
148
|
+
return okData(
|
|
149
|
+
freezeData({ manifest: ctx.getManifest() }),
|
|
150
|
+
"manifest",
|
|
151
|
+
);
|
|
152
|
+
case "oke.schema.get":
|
|
153
|
+
return schemaGet(ctx, args);
|
|
154
|
+
case "oke.effects.get":
|
|
155
|
+
return effectsGet(ctx, args);
|
|
156
|
+
case "oke.traces.list":
|
|
157
|
+
return tracesList(ctx, args);
|
|
158
|
+
case "oke.traces.get":
|
|
159
|
+
return tracesGet(ctx, args);
|
|
160
|
+
case "oke.action.confirm":
|
|
161
|
+
return actionConfirm(confirm, requester, args);
|
|
162
|
+
case "oke.action.invoke":
|
|
163
|
+
return actionInvoke(ctx, requester, args);
|
|
164
|
+
case "oke.action.structural_propose":
|
|
165
|
+
return actionStructural(ctx, requester, args);
|
|
166
|
+
default:
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
code: "not-found",
|
|
170
|
+
message: `unknown tool: ${name}`,
|
|
171
|
+
data: asData({ tool: name }, "error"),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function okData<T>(
|
|
179
|
+
content: T,
|
|
180
|
+
provenance: Parameters<typeof asData>[1],
|
|
181
|
+
): ToolCallResult {
|
|
182
|
+
return { ok: true, data: asData(content, provenance) };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function stripConfirmFields(
|
|
186
|
+
args: Record<string, unknown>,
|
|
187
|
+
): Record<string, unknown> {
|
|
188
|
+
const out: Record<string, unknown> = {};
|
|
189
|
+
for (const [k, v] of Object.entries(args)) {
|
|
190
|
+
if (k === "confirmation" || k === "confirmToken" || k === "reason") {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
out[k] = v;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function schemaGet(
|
|
199
|
+
ctx: McpContext,
|
|
200
|
+
args: Record<string, unknown>,
|
|
201
|
+
): ToolCallResult {
|
|
202
|
+
const flowId = String(args.flowId ?? "");
|
|
203
|
+
const manifest = ctx.getManifest();
|
|
204
|
+
const flow = manifest?.flows?.[flowId];
|
|
205
|
+
if (!flow) {
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
code: "not-found",
|
|
209
|
+
message: `unknown flow: ${flowId}`,
|
|
210
|
+
data: asData({ flowId }, "error"),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return okData(
|
|
214
|
+
freezeData({
|
|
215
|
+
flowId,
|
|
216
|
+
in: flow.in ?? null,
|
|
217
|
+
out: flow.out ?? null,
|
|
218
|
+
errors: flow.errors ?? null,
|
|
219
|
+
}),
|
|
220
|
+
"schema",
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function effectsGet(
|
|
225
|
+
ctx: McpContext,
|
|
226
|
+
args: Record<string, unknown>,
|
|
227
|
+
): ToolCallResult {
|
|
228
|
+
const flowId = String(args.flowId ?? "");
|
|
229
|
+
const manifest = ctx.getManifest();
|
|
230
|
+
const flow = manifest?.flows?.[flowId];
|
|
231
|
+
if (!flow) {
|
|
232
|
+
return {
|
|
233
|
+
ok: false,
|
|
234
|
+
code: "not-found",
|
|
235
|
+
message: `unknown flow: ${flowId}`,
|
|
236
|
+
data: asData({ flowId }, "error"),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return okData(
|
|
240
|
+
freezeData({ flowId, effects: flow.effects ?? {} }),
|
|
241
|
+
"effects",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function tracesList(
|
|
246
|
+
ctx: McpContext,
|
|
247
|
+
args: Record<string, unknown>,
|
|
248
|
+
): Promise<ToolCallResult> {
|
|
249
|
+
const limit =
|
|
250
|
+
typeof args.limit === "number" && Number.isFinite(args.limit)
|
|
251
|
+
? Math.min(200, Math.max(1, Math.floor(args.limit)))
|
|
252
|
+
: 50;
|
|
253
|
+
const flowId =
|
|
254
|
+
typeof args.flowId === "string" && args.flowId.length > 0
|
|
255
|
+
? args.flowId
|
|
256
|
+
: null;
|
|
257
|
+
const all = await ctx.listRuns();
|
|
258
|
+
const filtered = flowId ? all.filter((r) => r.flow === flowId) : all;
|
|
259
|
+
const runs = filtered.slice(0, limit).map(projectRun);
|
|
260
|
+
return okData(freezeData({ runs }), "trace");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function tracesGet(
|
|
264
|
+
ctx: McpContext,
|
|
265
|
+
args: Record<string, unknown>,
|
|
266
|
+
): Promise<ToolCallResult> {
|
|
267
|
+
const runId = String(args.runId ?? "");
|
|
268
|
+
const all = await ctx.listRuns();
|
|
269
|
+
const run = all.find((r) => r.id === runId);
|
|
270
|
+
if (!run) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
code: "not-found",
|
|
274
|
+
message: `unknown run: ${runId}`,
|
|
275
|
+
data: asData({ runId }, "error"),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// Provenance `store-record` marks user-supplied fields as untrusted.
|
|
279
|
+
return okData(freezeData({ run: projectRun(run) }), "store-record");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function actionConfirm(
|
|
283
|
+
confirm: ReturnType<typeof createConfirmationGate>,
|
|
284
|
+
requester: McpRequester,
|
|
285
|
+
args: Record<string, unknown>,
|
|
286
|
+
): ToolCallResult {
|
|
287
|
+
const tool = String(args.tool ?? "");
|
|
288
|
+
const reason = String(args.reason ?? "");
|
|
289
|
+
const toolArgs =
|
|
290
|
+
args.args !== null && typeof args.args === "object" && !Array.isArray(args.args)
|
|
291
|
+
? (args.args as Record<string, unknown>)
|
|
292
|
+
: {};
|
|
293
|
+
const issued = confirm.request({
|
|
294
|
+
tool,
|
|
295
|
+
args: toolArgs,
|
|
296
|
+
principalId: requester.principalId,
|
|
297
|
+
reason,
|
|
298
|
+
});
|
|
299
|
+
if ("error" in issued) {
|
|
300
|
+
return {
|
|
301
|
+
ok: false,
|
|
302
|
+
code: "invalid",
|
|
303
|
+
message: "reason must be at least 3 characters",
|
|
304
|
+
data: asData({ error: issued.error }, "error"),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
return okData(
|
|
308
|
+
{
|
|
309
|
+
confirmToken: issued.token,
|
|
310
|
+
tool: issued.tool,
|
|
311
|
+
phrase: MCP_CONFIRM_PHRASE,
|
|
312
|
+
expiresAt: issued.expiresAt,
|
|
313
|
+
notice:
|
|
314
|
+
"Present confirmToken + confirmation phrase on the write call. Tokens are single-use; there is no session consent cache.",
|
|
315
|
+
},
|
|
316
|
+
"action-result",
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function actionInvoke(
|
|
321
|
+
ctx: McpContext,
|
|
322
|
+
requester: McpRequester,
|
|
323
|
+
args: Record<string, unknown>,
|
|
324
|
+
): Promise<ToolCallResult> {
|
|
325
|
+
if (!ctx.invokeFlow) {
|
|
326
|
+
return {
|
|
327
|
+
ok: false,
|
|
328
|
+
code: "forbidden",
|
|
329
|
+
message: "invoke adapter not configured",
|
|
330
|
+
data: asData({ tool: "oke.action.invoke" }, "error"),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const flowId = String(args.flowId ?? "");
|
|
334
|
+
const reason = String(args.reason ?? "");
|
|
335
|
+
const result = await ctx.invokeFlow({
|
|
336
|
+
flowId,
|
|
337
|
+
body: args.body,
|
|
338
|
+
operatorId: requester.principalId,
|
|
339
|
+
reason,
|
|
340
|
+
});
|
|
341
|
+
return okData(freezeData({ result }), "action-result");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function actionStructural(
|
|
345
|
+
ctx: McpContext,
|
|
346
|
+
requester: McpRequester,
|
|
347
|
+
args: Record<string, unknown>,
|
|
348
|
+
): Promise<ToolCallResult> {
|
|
349
|
+
if (!ctx.proposeStructural) {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
code: "forbidden",
|
|
353
|
+
message: "structural adapter not configured",
|
|
354
|
+
data: asData({ tool: "oke.action.structural_propose" }, "error"),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const result = await ctx.proposeStructural({
|
|
358
|
+
title: String(args.title ?? ""),
|
|
359
|
+
relativePath: String(args.relativePath ?? ""),
|
|
360
|
+
contents: String(args.contents ?? ""),
|
|
361
|
+
operatorId: requester.principalId,
|
|
362
|
+
reason: String(args.reason ?? ""),
|
|
363
|
+
});
|
|
364
|
+
return okData(freezeData({ result }), "action-result");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Project a wide event for MCP. Dimensions / logs may contain poisoned
|
|
369
|
+
* strings — they remain in `content` under a store-record provenance.
|
|
370
|
+
*
|
|
371
|
+
* @param run - Wide event
|
|
372
|
+
*/
|
|
373
|
+
export function projectRun(run: WideEvent): Record<string, unknown> {
|
|
374
|
+
return {
|
|
375
|
+
id: run.id,
|
|
376
|
+
parentId: run.parentId ?? null,
|
|
377
|
+
flow: run.flow,
|
|
378
|
+
unit: run.unit ?? null,
|
|
379
|
+
trigger: run.trigger,
|
|
380
|
+
plane: run.plane,
|
|
381
|
+
startedAt: run.startedAt,
|
|
382
|
+
endedAt: run.endedAt,
|
|
383
|
+
durationMs: run.durationMs,
|
|
384
|
+
error: run.error ?? null,
|
|
385
|
+
cost: run.cost ?? null,
|
|
386
|
+
effects: run.effects,
|
|
387
|
+
// Attacker-controlled fields — still data, never instruction.
|
|
388
|
+
logs: run.logs,
|
|
389
|
+
dimensions: run.dimensions,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORE catalogue state derivation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import {
|
|
7
|
+
CORE_PLUGINS,
|
|
8
|
+
isCorePluginOn,
|
|
9
|
+
isPrivacyConfigured,
|
|
10
|
+
isTenancyConfigured,
|
|
11
|
+
} from "./catalogue.ts";
|
|
12
|
+
|
|
13
|
+
describe("CORE catalogue", () => {
|
|
14
|
+
test("lists auth, console, rate-limit, tenancy, privacy", () => {
|
|
15
|
+
expect(CORE_PLUGINS.map((p) => p.id)).toEqual([
|
|
16
|
+
"auth",
|
|
17
|
+
"console",
|
|
18
|
+
"rate-limit",
|
|
19
|
+
"tenancy",
|
|
20
|
+
"privacy",
|
|
21
|
+
]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("plug-based state for auth/console/rate-limit", () => {
|
|
25
|
+
const plugged = new Set(["auth", "console"]);
|
|
26
|
+
expect(isCorePluginOn("auth", null, null, plugged)).toBe(true);
|
|
27
|
+
expect(isCorePluginOn("console", null, null, plugged)).toBe(true);
|
|
28
|
+
expect(isCorePluginOn("rate-limit", null, null, plugged)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("tenancy from config or Manifest", () => {
|
|
32
|
+
expect(isTenancyConfigured(null, { tenancy: { isolation: "row" } })).toBe(
|
|
33
|
+
true,
|
|
34
|
+
);
|
|
35
|
+
expect(
|
|
36
|
+
isTenancyConfigured({ oke: "1.0", app: "x", tenancy: { isolation: "row" } }, null),
|
|
37
|
+
).toBe(true);
|
|
38
|
+
expect(isTenancyConfigured(null, null)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("privacy from privacy block or runs.redact", () => {
|
|
42
|
+
expect(isPrivacyConfigured({ privacy: {} })).toBe(true);
|
|
43
|
+
expect(isPrivacyConfigured({ runs: { redact: { email: "7y" } } })).toBe(
|
|
44
|
+
true,
|
|
45
|
+
);
|
|
46
|
+
expect(isPrivacyConfigured({})).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core plugin catalogue (console §9.15).
|
|
3
|
+
*
|
|
4
|
+
* State derivation (binding decision — keep four-applications + Console aligned):
|
|
5
|
+
*
|
|
6
|
+
* - `auth` · `console` · `rate-limit` → **on** iff present via `.plug()`
|
|
7
|
+
* (Manifest `plugins` / registry install).
|
|
8
|
+
* - `tenancy` → **on** iff `oke.config.ts` has `tenancy: {…}` (or Manifest
|
|
9
|
+
* `tenancy` was reconciled from that section).
|
|
10
|
+
* - `privacy` → **on** iff `oke.config.ts` has a `privacy` block **or**
|
|
11
|
+
* top-level `runs.redact` (retention redaction map). Not a separate `.plug()`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Manifest } from "../manifest/types.ts";
|
|
15
|
+
|
|
16
|
+
/** How CORE plugin on/off is derived. */
|
|
17
|
+
export type CoreStateSource = "plug" | "config";
|
|
18
|
+
|
|
19
|
+
/** One CORE catalogue entry. */
|
|
20
|
+
export interface CorePluginSpec {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly stateFrom: CoreStateSource;
|
|
23
|
+
/** Short catalogue blurb (feature discovery when off). */
|
|
24
|
+
readonly summary: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Canonical CORE catalogue — always listed in the Plugins panel.
|
|
29
|
+
*/
|
|
30
|
+
export const CORE_PLUGINS: readonly CorePluginSpec[] = [
|
|
31
|
+
{
|
|
32
|
+
id: "auth",
|
|
33
|
+
stateFrom: "plug",
|
|
34
|
+
summary: "Hybrid session, two planes, roles as data",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "console",
|
|
38
|
+
stateFrom: "plug",
|
|
39
|
+
summary: "Operator Console on :6533",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "rate-limit",
|
|
43
|
+
stateFrom: "plug",
|
|
44
|
+
summary: "Attachment-scoped request rate limits",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "tenancy",
|
|
48
|
+
stateFrom: "config",
|
|
49
|
+
summary: "Multi-tenant isolation (row · schema · database)",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "privacy",
|
|
53
|
+
stateFrom: "config",
|
|
54
|
+
summary: "PII classification, redact, export/erase tooling",
|
|
55
|
+
},
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
/** Config shape inspected for tenancy / privacy state (loose). */
|
|
59
|
+
export interface PluginConfigProbe {
|
|
60
|
+
readonly tenancy?: unknown;
|
|
61
|
+
readonly privacy?: unknown;
|
|
62
|
+
readonly runs?: { readonly redact?: unknown };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether a CORE plugin is on given Manifest + config probe.
|
|
67
|
+
*
|
|
68
|
+
* @param id - Core plugin id
|
|
69
|
+
* @param manifest - Live Manifest
|
|
70
|
+
* @param config - Loaded oke.config (or probe)
|
|
71
|
+
* @param pluggedNames - Names present via `.plug()` / Manifest.plugins
|
|
72
|
+
*/
|
|
73
|
+
export function isCorePluginOn(
|
|
74
|
+
id: string,
|
|
75
|
+
manifest: Manifest | null,
|
|
76
|
+
config: PluginConfigProbe | null | undefined,
|
|
77
|
+
pluggedNames: ReadonlySet<string>,
|
|
78
|
+
): boolean {
|
|
79
|
+
const spec = CORE_PLUGINS.find((p) => p.id === id);
|
|
80
|
+
if (!spec) return pluggedNames.has(id);
|
|
81
|
+
if (spec.stateFrom === "plug") return pluggedNames.has(id);
|
|
82
|
+
if (id === "tenancy") return isTenancyConfigured(manifest, config);
|
|
83
|
+
if (id === "privacy") return isPrivacyConfigured(config);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tenancy is on when config or Manifest carries a tenancy section.
|
|
89
|
+
*
|
|
90
|
+
* @param manifest - Manifest
|
|
91
|
+
* @param config - Config probe
|
|
92
|
+
*/
|
|
93
|
+
export function isTenancyConfigured(
|
|
94
|
+
manifest: Manifest | null,
|
|
95
|
+
config: PluginConfigProbe | null | undefined,
|
|
96
|
+
): boolean {
|
|
97
|
+
if (config?.tenancy != null) return true;
|
|
98
|
+
if (manifest?.tenancy != null) return true;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Privacy is on when config has `privacy` or `runs.redact`.
|
|
104
|
+
*
|
|
105
|
+
* @param config - Config probe
|
|
106
|
+
*/
|
|
107
|
+
export function isPrivacyConfigured(
|
|
108
|
+
config: PluginConfigProbe | null | undefined,
|
|
109
|
+
): boolean {
|
|
110
|
+
if (config?.privacy != null) return true;
|
|
111
|
+
if (config?.runs?.redact != null) return true;
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oxc node:/bun: bypass scan.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { scanNodeImportsBypassingFx } from "./node-import-scan.ts";
|
|
7
|
+
|
|
8
|
+
describe("scanNodeImportsBypassingFx", () => {
|
|
9
|
+
test("detects static node: imports", () => {
|
|
10
|
+
const findings = scanNodeImportsBypassingFx([
|
|
11
|
+
{
|
|
12
|
+
path: "evil.ts",
|
|
13
|
+
source: `import fs from "node:fs";\nexport const x = fs;\n`,
|
|
14
|
+
},
|
|
15
|
+
]);
|
|
16
|
+
expect(findings.some((f) => f.specifier === "node:fs")).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("ignores relative imports", () => {
|
|
20
|
+
const findings = scanNodeImportsBypassingFx([
|
|
21
|
+
{
|
|
22
|
+
path: "ok.ts",
|
|
23
|
+
source: `import { foo } from "./foo.ts";\nexport { foo };\n`,
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
expect(findings).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oxc scan for direct `node:` / `bun:` imports that bypass `fx`.
|
|
3
|
+
*
|
|
4
|
+
* Reuses Prompt 7's `oxc-parser` + `walk` — does not reimplement a parser.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { parseSync } from "oxc-parser";
|
|
8
|
+
import { walk, type AstNode } from "../compiler/effects-infer.ts";
|
|
9
|
+
|
|
10
|
+
/** One bypassing import finding. */
|
|
11
|
+
export interface NodeImportFinding {
|
|
12
|
+
readonly source: string;
|
|
13
|
+
readonly specifier: string;
|
|
14
|
+
readonly line: number | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Source file input. */
|
|
18
|
+
export interface ScanSourceFile {
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly source: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Scan sources for `node:` / `bun:` imports (static + dynamic).
|
|
25
|
+
*
|
|
26
|
+
* @param files - Plugin package sources
|
|
27
|
+
*/
|
|
28
|
+
export function scanNodeImportsBypassingFx(
|
|
29
|
+
files: readonly ScanSourceFile[],
|
|
30
|
+
): readonly NodeImportFinding[] {
|
|
31
|
+
const out: NodeImportFinding[] = [];
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
let program: unknown;
|
|
34
|
+
try {
|
|
35
|
+
const result = parseSync(file.path, file.source, {
|
|
36
|
+
sourceType: "module",
|
|
37
|
+
lang: file.path.endsWith(".tsx") ? "tsx" : "ts",
|
|
38
|
+
});
|
|
39
|
+
program = result.program;
|
|
40
|
+
} catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
walk(program, (node) => {
|
|
44
|
+
const specifier = importSpecifier(node);
|
|
45
|
+
if (!specifier) return;
|
|
46
|
+
if (!specifier.startsWith("node:") && !specifier.startsWith("bun:")) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
out.push({
|
|
50
|
+
source: file.path,
|
|
51
|
+
specifier,
|
|
52
|
+
line: typeof node.start === "number"
|
|
53
|
+
? lineAt(file.source, node.start)
|
|
54
|
+
: null,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function importSpecifier(node: AstNode): string | null {
|
|
62
|
+
if (node.type === "ImportDeclaration") {
|
|
63
|
+
const src = (node as AstNode & { source?: { value?: unknown } }).source;
|
|
64
|
+
return typeof src?.value === "string" ? src.value : null;
|
|
65
|
+
}
|
|
66
|
+
if (node.type === "ImportExpression") {
|
|
67
|
+
const arg = (node as AstNode & { source?: AstNode }).source;
|
|
68
|
+
if (arg?.type === "Literal" || arg?.type === "StringLiteral") {
|
|
69
|
+
const value = (arg as unknown as { value?: unknown }).value;
|
|
70
|
+
if (typeof value === "string") return value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (node.type === "CallExpression") {
|
|
74
|
+
const call = node as AstNode & {
|
|
75
|
+
callee?: AstNode;
|
|
76
|
+
arguments?: AstNode[];
|
|
77
|
+
};
|
|
78
|
+
const callee = call.callee;
|
|
79
|
+
const isRequire =
|
|
80
|
+
callee?.type === "Identifier" &&
|
|
81
|
+
(callee as { name?: string }).name === "require";
|
|
82
|
+
if (!isRequire) return null;
|
|
83
|
+
const arg0 = call.arguments?.[0];
|
|
84
|
+
if (arg0 && (arg0.type === "Literal" || arg0.type === "StringLiteral")) {
|
|
85
|
+
const value = (arg0 as unknown as { value?: unknown }).value;
|
|
86
|
+
if (typeof value === "string") return value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function lineAt(source: string, offset: number): number {
|
|
93
|
+
let line = 1;
|
|
94
|
+
for (let i = 0; i < offset && i < source.length; i++) {
|
|
95
|
+
if (source.charCodeAt(i) === 10) line++;
|
|
96
|
+
}
|
|
97
|
+
return line;
|
|
98
|
+
}
|