@notis_ai/cli 0.2.18 → 0.2.20
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/README.md +542 -146
- package/bin/check-runtime.js +15 -0
- package/bin/notis.js +2 -0
- package/config/notis_app_design_rules.json +320 -0
- package/config/portal-route-roots.json +45 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +42325 -18273
- package/dist/base-skills/notis-cli/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-cli/SKILL.md +51 -280
- package/dist/base-skills/notis-cli/references/app-delivery.md +17 -0
- package/dist/base-skills/notis-cli/references/intelligence.md +94 -0
- package/dist/base-skills/notis-cli/references/native-databases.md +21 -0
- package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
- package/dist/base-skills/notis-cli/references/troubleshooting.md +67 -0
- package/dist/base-skills/notis-query/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-query/SKILL.md +35 -699
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
- package/dist/base-skills/notis-query/references/documents.md +74 -0
- package/dist/base-skills/notis-query/references/query.md +76 -0
- package/dist/base-skills/notis-spaces/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-spaces/SKILL.md +218 -0
- package/dist/base-skills/notis-spaces/references/architecture.md +116 -0
- package/dist/base-skills/notis-spaces/references/context.md +81 -0
- package/dist/base-skills/notis-spaces/references/design.md +141 -0
- package/dist/base-skills/notis-spaces/references/fixtures.md +29 -0
- package/dist/base-skills/notis-spaces/references/memory.md +58 -0
- package/dist/base-skills/notis-spaces/references/reading.md +73 -0
- package/dist/base-skills/notis-spaces/references/release.md +120 -0
- package/dist/base-skills/notis-spaces/references/reports.md +77 -0
- package/dist/base-skills/notis-spaces/references/sdk.md +273 -0
- package/dist/base-skills/notis-spaces/references/store.md +118 -0
- package/dist/base-skills/notis-spaces/references/troubleshooting.md +28 -0
- package/dist/base-skills/notis-spaces/references/views.md +229 -0
- package/{template/packages → dist}/sdk/package.json +9 -10
- package/dist/sdk/src/agentContext.ts +36 -0
- package/dist/sdk/src/bundleModule.ts +175 -0
- package/dist/sdk/src/components/Dialog.tsx +103 -0
- package/dist/sdk/src/components/DocumentEditor.tsx +113 -0
- package/dist/sdk/src/components/DocumentPage.tsx +184 -0
- package/{template/packages → dist}/sdk/src/components/Markdown.tsx +9 -3
- package/{template/packages → dist}/sdk/src/components/MarkdownEditor.tsx +26 -5
- package/{template/packages → dist}/sdk/src/components/MultiSelectActionBar.tsx +95 -12
- package/{template/packages → dist}/sdk/src/components/MultiSelectCheckbox.tsx +4 -2
- package/dist/sdk/src/components/NotisCommentBoundary.tsx +172 -0
- package/{template/packages → dist}/sdk/src/components/NotisSelectionBoundary.tsx +9 -5
- package/dist/sdk/src/components/RecordComponents.tsx +111 -0
- package/dist/sdk/src/components/Skeleton.tsx +38 -0
- package/dist/sdk/src/config.ts +31 -0
- package/{template/packages → dist}/sdk/src/documents.ts +21 -6
- package/dist/sdk/src/hooks/useAction.ts +28 -0
- package/dist/sdk/src/hooks/useAgentContext.ts +23 -0
- package/{template/packages → dist}/sdk/src/hooks/useBackend.ts +3 -2
- package/dist/sdk/src/hooks/useCloudComputer.ts +94 -0
- package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
- package/{template/packages → dist}/sdk/src/hooks/useCollectionInteractions.ts +157 -147
- package/dist/sdk/src/hooks/useDatabaseSchema.ts +50 -0
- package/{template/packages → dist}/sdk/src/hooks/useDatabaseSubscription.ts +2 -2
- package/dist/sdk/src/hooks/useDocument.ts +51 -0
- package/{template/packages → dist}/sdk/src/hooks/useDocuments.ts +25 -59
- package/{template/packages → dist}/sdk/src/hooks/useHandover.ts +7 -7
- package/dist/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/dist/sdk/src/hooks/useMarqueeSelection.ts +194 -0
- package/{template/packages → dist}/sdk/src/hooks/useMultiSelect.ts +2 -8
- package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
- package/dist/sdk/src/hooks/useNativeDocuments.ts +143 -0
- package/{template/packages → dist}/sdk/src/hooks/useNotis.ts +6 -1
- package/dist/sdk/src/hooks/useNotisNavigation.ts +70 -0
- package/dist/sdk/src/hooks/useQuery.ts +81 -0
- package/dist/sdk/src/hooks/useToolQuery.ts +12 -0
- package/dist/sdk/src/hooks/useTopBarSearch.ts +102 -0
- package/dist/sdk/src/hooks/useTopBarTitleLeaf.ts +75 -0
- package/{template/packages → dist}/sdk/src/hooks/useUpsertDocument.ts +1 -1
- package/dist/sdk/src/hooks/useViewParams.ts +195 -0
- package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
- package/{template/packages → dist}/sdk/src/index.ts +64 -1
- package/{template/packages → dist}/sdk/src/interactions/actions.ts +14 -1
- package/{template/packages → dist}/sdk/src/interactions/shortcuts.tsx +129 -23
- package/dist/sdk/src/interactions/visibility.ts +13 -0
- package/{template/packages → dist}/sdk/src/interactions.ts +5 -1
- package/dist/sdk/src/nativeData.ts +39 -0
- package/{template/packages → dist}/sdk/src/provider.tsx +9 -4
- package/dist/sdk/src/queryCache.ts +292 -0
- package/dist/sdk/src/queryErrors.ts +51 -0
- package/dist/sdk/src/runtime.ts +675 -0
- package/dist/sdk/src/runtimeModules.ts +33 -0
- package/dist/sdk/src/space.ts +442 -0
- package/dist/sdk/src/standardViewParams.ts +36 -0
- package/{template/packages → dist}/sdk/src/styles.css +81 -16
- package/dist/sdk/src/tailwind.ts +66 -0
- package/dist/sdk/src/ui/charts/RenderChartData.tsx +25 -0
- package/dist/sdk/src/ui/engine/core.ts +392 -0
- package/dist/sdk/src/ui/engine/dom.ts +183 -0
- package/dist/sdk/src/ui/engine/hooks.ts +195 -0
- package/dist/sdk/src/ui/engine/instance.ts +63 -0
- package/dist/sdk/src/ui/engine/keycapContext.ts +2 -0
- package/dist/sdk/src/ui/engine/registry.ts +262 -0
- package/dist/sdk/src/ui/engine/types.ts +62 -0
- package/dist/sdk/src/ui/icons.tsx +91 -0
- package/dist/sdk/src/ui/util.ts +83 -0
- package/dist/sdk/src/viewPeriod.ts +26 -0
- package/dist/sdk/src/vite.ts +92 -0
- package/dist/sdk/src/words/actions.ts +295 -0
- package/dist/sdk/src/words/clashes.ts +158 -0
- package/dist/sdk/src/words/format.ts +136 -0
- package/dist/sdk/src/words/index.ts +13 -0
- package/dist/sdk/src/words/keymap.ts +72 -0
- package/dist/sdk/src/words/keys.ts +297 -0
- package/dist/sdk/src/words/shell.ts +46 -0
- package/dist/sdk/src/words/types.ts +65 -0
- package/dist/skill-sync/index.js +832 -151
- package/dist/skill-sync/index.js.map +4 -4
- package/dist/skill-sync-worker.mjs +3941 -0
- package/dist/space-harness/host.js +1460 -0
- package/dist/space-sdk/dist/chunk-ZSN57ATH.js +5 -0
- package/dist/space-sdk/dist/index.js +15 -0
- package/dist/space-sdk/dist/ui.js +18 -0
- package/dist/space-sdk/package.json +33 -0
- package/dist/space-sdk/src/agentContext.d.ts +36 -0
- package/dist/space-sdk/src/components/Dialog.d.ts +20 -0
- package/dist/space-sdk/src/components/DocumentEditor.d.ts +46 -0
- package/dist/space-sdk/src/components/DocumentPage.d.ts +53 -0
- package/dist/space-sdk/src/components/Markdown.d.ts +18 -0
- package/dist/space-sdk/src/components/MarkdownEditor.d.ts +11 -0
- package/dist/space-sdk/src/components/MultiSelectActionBar.d.ts +31 -0
- package/dist/space-sdk/src/components/MultiSelectCheckbox.d.ts +14 -0
- package/dist/space-sdk/src/components/MultiSelectDragOverlay.d.ts +8 -0
- package/dist/space-sdk/src/components/NotisCommentBoundary.d.ts +36 -0
- package/dist/space-sdk/src/components/NotisSelectionBoundary.d.ts +16 -0
- package/dist/space-sdk/src/components/RecordComponents.d.ts +64 -0
- package/dist/space-sdk/src/components/ShortcutHints.d.ts +12 -0
- package/dist/space-sdk/src/components/Skeleton.d.ts +23 -0
- package/dist/space-sdk/src/config.ts +31 -0
- package/dist/space-sdk/src/documents.d.ts +59 -0
- package/dist/space-sdk/src/hooks/useAction.d.ts +8 -0
- package/dist/space-sdk/src/hooks/useActiveResource.d.ts +3 -0
- package/dist/space-sdk/src/hooks/useAgentContext.d.ts +7 -0
- package/dist/space-sdk/src/hooks/useBackend.d.ts +23 -0
- package/dist/space-sdk/src/hooks/useCloudComputer.d.ts +45 -0
- package/dist/space-sdk/src/hooks/useCloudComputerShell.d.ts +49 -0
- package/dist/space-sdk/src/hooks/useCollectionInteractions.d.ts +118 -0
- package/dist/space-sdk/src/hooks/useDatabaseSchema.d.ts +14 -0
- package/dist/space-sdk/src/hooks/useDatabaseSubscription.d.ts +26 -0
- package/dist/space-sdk/src/hooks/useDocument.d.ts +22 -0
- package/dist/space-sdk/src/hooks/useDocuments.d.ts +31 -0
- package/dist/space-sdk/src/hooks/useHandover.d.ts +43 -0
- package/dist/space-sdk/src/hooks/useLongPressSelection.d.ts +9 -0
- package/dist/space-sdk/src/hooks/useMarqueeSelection.d.ts +57 -0
- package/dist/space-sdk/src/hooks/useMultiSelect.d.ts +51 -0
- package/dist/space-sdk/src/hooks/useNativeDocumentBody.d.ts +24 -0
- package/dist/space-sdk/src/hooks/useNativeDocuments.d.ts +55 -0
- package/dist/space-sdk/src/hooks/useNotis.d.ts +25 -0
- package/dist/space-sdk/src/hooks/useNotisNavigation.d.ts +33 -0
- package/dist/space-sdk/src/hooks/useQuery.d.ts +22 -0
- package/dist/space-sdk/src/hooks/useTool.d.ts +20 -0
- package/dist/space-sdk/src/hooks/useToolQuery.d.ts +3 -0
- package/dist/space-sdk/src/hooks/useTools.d.ts +15 -0
- package/dist/space-sdk/src/hooks/useTopBarSearch.d.ts +48 -0
- package/dist/space-sdk/src/hooks/useTopBarTitleLeaf.d.ts +24 -0
- package/dist/space-sdk/src/hooks/useUpsertDocument.d.ts +27 -0
- package/dist/space-sdk/src/hooks/useViewParams.d.ts +76 -0
- package/dist/space-sdk/src/hooks/useViewerReads.d.ts +38 -0
- package/dist/space-sdk/src/index.d.ts +98 -0
- package/dist/space-sdk/src/interactions/actions.d.ts +33 -0
- package/dist/space-sdk/src/interactions/shortcuts.d.ts +42 -0
- package/dist/space-sdk/src/interactions/visibility.d.ts +2 -0
- package/dist/space-sdk/src/nativeData.d.ts +66 -0
- package/dist/space-sdk/src/provider.d.ts +17 -0
- package/dist/space-sdk/src/queryCache.d.ts +45 -0
- package/dist/space-sdk/src/queryErrors.ts +51 -0
- package/dist/space-sdk/src/runtime.d.ts +615 -0
- package/dist/space-sdk/src/runtimeModules.ts +33 -0
- package/dist/space-sdk/src/space.ts +442 -0
- package/dist/space-sdk/src/standardViewParams.d.ts +15 -0
- package/dist/space-sdk/src/tailwind-candidates.d.ts +3418 -0
- package/dist/space-sdk/src/tailwind.ts +66 -0
- package/dist/space-sdk/src/ui/actions/ActionToast.d.ts +15 -0
- package/dist/space-sdk/src/ui/actions/actionWords.d.ts +16 -0
- package/dist/space-sdk/src/ui/actions/index.d.ts +7 -0
- package/dist/space-sdk/src/ui/actions/useActions.d.ts +69 -0
- package/dist/space-sdk/src/ui/actions/useToast.d.ts +41 -0
- package/dist/space-sdk/src/ui/blocks/Tabs.d.ts +139 -0
- package/dist/space-sdk/src/ui/blocks/blocks.d.ts +217 -0
- package/dist/space-sdk/src/ui/blocks/index.d.ts +4 -0
- package/dist/space-sdk/src/ui/board/BoardView.d.ts +36 -0
- package/dist/space-sdk/src/ui/board/boardModel.d.ts +46 -0
- package/dist/space-sdk/src/ui/board/boardWords.d.ts +9 -0
- package/dist/space-sdk/src/ui/board/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/calendar/CalendarView.d.ts +49 -0
- package/dist/space-sdk/src/ui/calendar/DayPicker.d.ts +23 -0
- package/dist/space-sdk/src/ui/calendar/calendarMath.d.ts +81 -0
- package/dist/space-sdk/src/ui/charts/Chart.d.ts +203 -0
- package/dist/space-sdk/src/ui/charts/ChartTable.d.ts +37 -0
- package/dist/space-sdk/src/ui/charts/RenderChartData.d.ts +16 -0
- package/dist/space-sdk/src/ui/charts/StatRow.d.ts +59 -0
- package/dist/space-sdk/src/ui/charts/chartData.d.ts +84 -0
- package/dist/space-sdk/src/ui/comments/CommentPane.d.ts +58 -0
- package/dist/space-sdk/src/ui/comments/commentModel.d.ts +157 -0
- package/dist/space-sdk/src/ui/comments/commentWords.d.ts +60 -0
- package/dist/space-sdk/src/ui/comments/index.d.ts +4 -0
- package/dist/space-sdk/src/ui/comments/useComments.d.ts +146 -0
- package/dist/space-sdk/src/ui/engine/HelpSheet.d.ts +12 -0
- package/dist/space-sdk/src/ui/engine/KeyboardEngine.d.ts +27 -0
- package/dist/space-sdk/src/ui/engine/core.d.ts +73 -0
- package/dist/space-sdk/src/ui/engine/hooks.d.ts +64 -0
- package/dist/space-sdk/src/ui/engine/index.d.ts +14 -0
- package/dist/space-sdk/src/ui/engine/instance.d.ts +21 -0
- package/dist/space-sdk/src/ui/engine/keycapContext.d.ts +2 -0
- package/dist/space-sdk/src/ui/engine/registry.d.ts +77 -0
- package/dist/space-sdk/src/ui/engine/step.d.ts +11 -0
- package/dist/space-sdk/src/ui/engine/types.d.ts +54 -0
- package/dist/space-sdk/src/ui/fields/fieldWords.d.ts +56 -0
- package/dist/space-sdk/src/ui/fields/fields.d.ts +162 -0
- package/dist/space-sdk/src/ui/fields/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/fields/useFieldDraft.d.ts +22 -0
- package/dist/space-sdk/src/ui/gallery/AttachmentGallery.d.ts +53 -0
- package/dist/space-sdk/src/ui/gallery/ImageViewer.d.ts +27 -0
- package/dist/space-sdk/src/ui/gallery/galleryWords.d.ts +95 -0
- package/dist/space-sdk/src/ui/gallery/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/icons.d.ts +13 -0
- package/dist/space-sdk/src/ui/index.d.ts +54 -0
- package/dist/space-sdk/src/ui/keycap/Keycap.d.ts +22 -0
- package/dist/space-sdk/src/ui/keycap/index.d.ts +2 -0
- package/dist/space-sdk/src/ui/layout/Panels.d.ts +50 -0
- package/dist/space-sdk/src/ui/layout/chatPanelEdge.d.ts +77 -0
- package/dist/space-sdk/src/ui/layout/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/layout/panelMath.d.ts +91 -0
- package/dist/space-sdk/src/ui/list/ListReader.d.ts +380 -0
- package/dist/space-sdk/src/ui/list/Row.d.ts +145 -0
- package/dist/space-sdk/src/ui/list/SelectionBar.d.ts +65 -0
- package/dist/space-sdk/src/ui/list/index.d.ts +14 -0
- package/dist/space-sdk/src/ui/list/rowState.d.ts +22 -0
- package/dist/space-sdk/src/ui/list/useListKeys.d.ts +89 -0
- package/dist/space-sdk/src/ui/list/useReorder.d.ts +86 -0
- package/dist/space-sdk/src/ui/list/useSelection.d.ts +90 -0
- package/dist/space-sdk/src/ui/open/OpenAsMenu.d.ts +15 -0
- package/dist/space-sdk/src/ui/open/index.d.ts +6 -0
- package/dist/space-sdk/src/ui/open/openModeStore.d.ts +39 -0
- package/dist/space-sdk/src/ui/open/useOpenMode.d.ts +17 -0
- package/dist/space-sdk/src/ui/overlay/Menu.d.ts +88 -0
- package/dist/space-sdk/src/ui/overlay/Popover.d.ts +28 -0
- package/dist/space-sdk/src/ui/overlay/Tooltip.d.ts +19 -0
- package/dist/space-sdk/src/ui/overlay/index.d.ts +9 -0
- package/dist/space-sdk/src/ui/overlay/layerRoot.d.ts +24 -0
- package/dist/space-sdk/src/ui/overlay/position.d.ts +31 -0
- package/dist/space-sdk/src/ui/period/PeriodPicker.d.ts +30 -0
- package/dist/space-sdk/src/ui/period/dateFormat.d.ts +50 -0
- package/dist/space-sdk/src/ui/period/dateWords.d.ts +88 -0
- package/dist/space-sdk/src/ui/period/periods.d.ts +70 -0
- package/dist/space-sdk/src/ui/period/usePeriod.d.ts +23 -0
- package/dist/space-sdk/src/ui/present/index.d.ts +3 -0
- package/dist/space-sdk/src/ui/present/presentWords.d.ts +27 -0
- package/dist/space-sdk/src/ui/present/screens.d.ts +36 -0
- package/dist/space-sdk/src/ui/present/useAudienceScreen.d.ts +38 -0
- package/dist/space-sdk/src/ui/styles.d.ts +21 -0
- package/dist/space-sdk/src/ui/timeline/TimelineView.d.ts +44 -0
- package/dist/space-sdk/src/ui/timeline/timelineMath.d.ts +123 -0
- package/dist/space-sdk/src/viewPeriod.d.ts +8 -0
- package/dist/space-sdk/src/vite.ts +92 -0
- package/dist/space-sdk/src/words/actions.ts +295 -0
- package/dist/space-sdk/src/words/clashes.ts +158 -0
- package/dist/space-sdk/src/words/format.ts +136 -0
- package/dist/space-sdk/src/words/index.ts +13 -0
- package/dist/space-sdk/src/words/keymap.ts +72 -0
- package/dist/space-sdk/src/words/keys.ts +297 -0
- package/dist/space-sdk/src/words/shell.ts +46 -0
- package/dist/space-sdk/src/words/types.ts +65 -0
- package/dist/view-renderer/dist/host.css +1 -0
- package/dist/view-renderer/dist/host.js +2374 -0
- package/dist/view-renderer/dist/theme.css +73 -0
- package/dist/view-renderer/src/dom.js +212 -0
- package/dist/view-renderer/src/errors.js +8 -0
- package/dist/view-renderer/src/host.tsx +250 -0
- package/dist/view-renderer/src/index.js +4 -0
- package/dist/view-renderer/src/png.js +213 -0
- package/dist/view-renderer/src/pool.js +110 -0
- package/dist/view-renderer/src/remote-broker.js +59 -0
- package/dist/view-renderer/src/renderer.js +279 -0
- package/package.json +21 -11
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +63 -12
- package/skills/notis-onboarding/BRIEF.md +14 -9
- package/skills/notis-query/cli.md +1 -1
- package/skills/notis-spaces/cli.md +540 -0
- package/src/cli.js +8 -8
- package/src/command-specs/agents.js +121 -8
- package/src/command-specs/auth.js +3 -3
- package/src/command-specs/diagnostics.js +97 -20
- package/src/command-specs/documents.js +158 -0
- package/src/command-specs/files.js +29 -0
- package/src/command-specs/handover.js +331 -79
- package/src/command-specs/helpers.js +50 -40
- package/src/command-specs/index.js +14 -5
- package/src/command-specs/meta.js +38 -9
- package/src/command-specs/onboarding.js +1 -1
- package/src/command-specs/profile.js +42 -1
- package/src/command-specs/skills.js +314 -9
- package/src/command-specs/smoke.js +16 -4
- package/src/command-specs/spaces.js +783 -0
- package/src/command-specs/tools.js +118 -21
- package/src/command-specs/version-history.js +20 -0
- package/src/command-specs/views.js +69 -0
- package/src/runtime/agent-browser.js +178 -267
- package/src/runtime/agent-setup.js +172 -13
- package/src/runtime/app-boundary-validator.js +338 -2
- package/src/runtime/app-data-literals.js +82 -0
- package/src/runtime/app-platform.js +750 -1570
- package/src/runtime/auth-recovery.js +17 -0
- package/src/runtime/base-skills.d.ts +11 -0
- package/src/runtime/base-skills.js +100 -3
- package/src/runtime/computer-identity.d.ts +30 -0
- package/src/runtime/computer-identity.js +151 -0
- package/src/runtime/config-lock.js +135 -0
- package/src/runtime/confined-space-build.js +74 -0
- package/src/runtime/confined-space-verify.js +49 -0
- package/src/runtime/delegated-context.js +5 -21
- package/src/runtime/native-record-files.js +47 -0
- package/src/runtime/oauth.js +342 -31
- package/src/runtime/profiles.js +19 -112
- package/src/runtime/retired-sdk-files.js +24 -0
- package/src/runtime/retry.js +150 -0
- package/src/runtime/skill-sync/cloud-client.ts +33 -8
- package/src/runtime/skill-sync/index.ts +443 -106
- package/src/runtime/skill-sync/local-scanner.ts +248 -32
- package/src/runtime/skill-sync/native-identity.ts +102 -0
- package/src/runtime/skill-sync/native-plan.ts +70 -0
- package/src/runtime/skill-sync/symlink-manager.ts +66 -16
- package/src/runtime/skill-sync/sync-plan.ts +34 -2
- package/src/runtime/skill-sync/types.ts +54 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +4 -4
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/runtime/source-root.js +32 -0
- package/src/runtime/space-collection-scaffold.js +107 -0
- package/src/runtime/space-database-structures.js +229 -0
- package/src/runtime/space-fixture-broker.js +68 -0
- package/src/runtime/space-lock.js +337 -0
- package/src/runtime/space-platform.js +133 -0
- package/src/runtime/space-source.js +290 -0
- package/src/runtime/space-test-server.js +354 -0
- package/src/runtime/space-verification.js +94 -0
- package/src/runtime/space-verify-browser-worker.js +51 -0
- package/src/runtime/space-view-manifest.js +368 -0
- package/src/runtime/sync-skills.d.ts +5 -1
- package/src/runtime/sync-skills.js +11 -2
- package/src/runtime/tool-confirmation.js +138 -0
- package/src/runtime/transport.js +118 -24
- package/src/runtime/upsell.js +219 -0
- package/src/runtime/view-render.js +49 -0
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +114 -0
- package/src/space-harness-host.ts +122 -0
- package/template/.harness/index.html.tmpl +9 -352
- package/template/app/globals.css +29 -3
- package/template/app/layout.tsx +2 -6
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/button.tsx +1 -1
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +4 -36
- package/template/package-lock.json +1800 -1867
- package/template/package.json +16 -14
- package/template/postcss.config.mjs +1 -1
- package/template/tailwind.config.ts +1 -6
- package/template/tsconfig.json +1 -0
- package/template/vite.config.ts +2 -2
- package/dist/base-skills/notis-apps/SKILL.md +0 -613
- package/skills/notis-apps/cli.md +0 -285
- package/src/command-specs/apps.js +0 -3011
- package/src/runtime/app-changelog.js +0 -79
- package/src/runtime/app-dev-consumers.js +0 -154
- package/src/runtime/app-dev-host-lock.js +0 -80
- package/src/runtime/app-dev-process-identity.js +0 -111
- package/src/runtime/app-dev-roots.js +0 -284
- package/src/runtime/app-dev-server.js +0 -1140
- package/src/runtime/app-dev-sessions.js +0 -185
- package/src/runtime/app-registry-scaffolds.js +0 -367
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/cli-mode.generated.js +0 -5
- package/src/runtime/cli-mode.js +0 -34
- package/src/runtime/store-screenshot.js +0 -143
- package/template/CHANGELOG.md +0 -5
- package/template/app/page.tsx +0 -60
- package/template/components/ui/card.tsx +0 -56
- package/template/packages/sdk/src/components/DocumentEditor.tsx +0 -103
- package/template/packages/sdk/src/config.ts +0 -259
- package/template/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/template/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/template/packages/sdk/src/hooks/useNotisNavigation.ts +0 -52
- package/template/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/template/packages/sdk/src/runtime.ts +0 -439
- package/template/packages/sdk/src/vite.ts +0 -56
- package/template/packages/sdk/tsconfig.json +0 -15
- /package/{template/packages → dist}/sdk/src/components/MultiSelectDragOverlay.tsx +0 -0
- /package/{template/packages → dist}/sdk/src/components/ShortcutHints.tsx +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useActiveResource.ts +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useTool.ts +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useTools.ts +0 -0
- /package/{template/packages → dist}/sdk/src/ui.ts +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** One authoring project, one selected immutable Space release at a time. */
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { appFilesDigest, buildArtifact, freezeSourceWorkspace, prepareAppRelease, prepareSelectedBuild, saveSelectedBuild, syncSpaceSdk } from './app-platform.js';
|
|
5
|
+
import { validateProjectBoundary, validateProjectDesign, validateSourceDesign } from './app-boundary-validator.js';
|
|
6
|
+
import { collectSelectedSpaceSource, loadSelectedSpace } from './space-source.js';
|
|
7
|
+
import { viewManifestFields } from './space-view-manifest.js';
|
|
8
|
+
import { usageError } from './errors.js';
|
|
9
|
+
import { assertSourceRoot } from './source-root.js';
|
|
10
|
+
|
|
11
|
+
export function generateSpaceManifest(selection) {
|
|
12
|
+
const definition = selection.definition;
|
|
13
|
+
return { schema: 'notis-space/v1', kind: selection.entry ? 'presentation' : 'container', local_key: selection.key,
|
|
14
|
+
name: definition.name, description: definition.description || '', icon: definition.icon || null, accent: definition.accent || null,
|
|
15
|
+
resources: definition.resources || {}, actions: definition.actions || {},
|
|
16
|
+
...(definition.navigation ? { navigation: definition.navigation } : {}),
|
|
17
|
+
// Absent when none, so manifests without viewer reads stay byte-identical.
|
|
18
|
+
...(definition.viewerReads?.length ? { viewer_reads: [...definition.viewerReads].sort() } : {}),
|
|
19
|
+
// Absent when undeclared, for the same reason.
|
|
20
|
+
...(definition.cloudComputer ? { cloud_computer: definition.cloudComputer } : {}),
|
|
21
|
+
readable_context: definition.readableContext || '', collection: definition.collection || null,
|
|
22
|
+
// V4 declarations are mandatory for every presentation; the wire schema stays v1.
|
|
23
|
+
...viewManifestFields(definition, { entry: selection.entry, resources: definition.resources || {} }),
|
|
24
|
+
...(selection.entry ? { presentation: { export_name: 'SpaceView' }, bundle: { js: 'bundle/app.js', css: 'bundle/app.css' } } : {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function prepareSelectedArtifact(projectDir, selection, { designBar = 'enforce', log = null } = {}) {
|
|
29
|
+
validateProjectBoundary(projectDir);
|
|
30
|
+
// Spaces render inside the Notis host: the app rules plus the Space-only ones. Only the
|
|
31
|
+
// render boundary check (portal/scripts/spaces-boundary) reports instead of refusing, so it
|
|
32
|
+
// can still draw a published Space whose source predates a design rule.
|
|
33
|
+
validateProjectDesign(projectDir, { profile: 'space', enforce: designBar !== 'report', log });
|
|
34
|
+
const entryPath = join(projectDir, '.notis/_entry.tsx');
|
|
35
|
+
const manifestPath = join(projectDir, '.notis/output/manifest.json');
|
|
36
|
+
mkdirSync(dirname(entryPath), { recursive: true });
|
|
37
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
38
|
+
if (selection.entry) {
|
|
39
|
+
const reference = path => JSON.stringify('./' + relative(dirname(entryPath), join(projectDir, path)).replaceAll('\\', '/'));
|
|
40
|
+
const lines = [`export { default as SpaceView } from ${reference(selection.entry)};`];
|
|
41
|
+
if (selection.layout) lines.push(`export { default as __AppShell } from ${reference(selection.layout)};`);
|
|
42
|
+
if (selection.markdown) lines.push(`export { default as SpaceMarkdown } from ${reference(selection.markdown)};`);
|
|
43
|
+
writeFileSync(entryPath, lines.join('\n') + '\n');
|
|
44
|
+
}
|
|
45
|
+
writeFileSync(manifestPath, JSON.stringify(generateSpaceManifest(selection), null, 2) + '\n');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// The SDK modules a Space host provides (packages/sdk/src/runtimeModules.ts;
|
|
49
|
+
// test/space-host-sdk.test.js keeps the two lists equal).
|
|
50
|
+
export const SPACE_HOST_SDK_MODULES = Object.freeze(['@notis/sdk', '@notis/sdk/ui']);
|
|
51
|
+
const SDK_IMPORT = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)(["'])(@notis\/sdk(?:\/[^"'\s]*)?)\1/g;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A Space bundle imports the SDK from its host and never carries SDK code.
|
|
55
|
+
* Refuses a bundle that contains SDK internals (their `notis.sdk.*` registry
|
|
56
|
+
* keys) or imports an SDK entry the host does not provide.
|
|
57
|
+
*/
|
|
58
|
+
export function assertSpaceBundleUsesHostSdk(source) {
|
|
59
|
+
if (/["']notis\.sdk\./.test(source)) {
|
|
60
|
+
throw usageError('This Space bundle contains Notis SDK code. Spaces import @notis/sdk from the Notis host; reinstall the CLI and build again.');
|
|
61
|
+
}
|
|
62
|
+
const foreign = [...new Set([...source.matchAll(SDK_IMPORT)].map(match => match[2]))]
|
|
63
|
+
.filter(specifier => !SPACE_HOST_SDK_MODULES.includes(specifier));
|
|
64
|
+
if (foreign.length) {
|
|
65
|
+
throw usageError(`This Space imports ${foreign.join(', ')}, which the Notis host does not provide. Use ${SPACE_HOST_SDK_MODULES.join(' or ')}.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function finalizeSelectedArtifact(projectDir) {
|
|
70
|
+
const manifestPath = join(projectDir, '.notis/output/manifest.json');
|
|
71
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
72
|
+
if (manifest.bundle?.js) assertSpaceBundleUsesHostSdk(readFileSync(join(projectDir, '.notis/output', manifest.bundle.js), 'utf8'));
|
|
73
|
+
// A CSS-free Vite entry legitimately emits only JavaScript. Declare actual
|
|
74
|
+
// output before the immutable build receipt, not a nonexistent stylesheet.
|
|
75
|
+
if (manifest.bundle?.css && !existsSync(join(projectDir, '.notis/output', manifest.bundle.css))) {
|
|
76
|
+
delete manifest.bundle.css;
|
|
77
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build from the selected source slice so CSS scans/config loaders cannot pull
|
|
83
|
+
* in unshared sibling files. Staging resolves the original project's installed
|
|
84
|
+
* dependencies; it is not another authoring project or dependency installation.
|
|
85
|
+
* The existing pinned freeze/receipt code owns all staging writes and cleanup.
|
|
86
|
+
*/
|
|
87
|
+
export async function buildSpaceRelease(projectDir, selectedKey, { stdio = 'inherit', refreshSdk = true, runBuild, designBar = 'enforce', designLog = null } = {}) {
|
|
88
|
+
projectDir = assertSourceRoot(projectDir);
|
|
89
|
+
if (refreshSdk) syncSpaceSdk(projectDir);
|
|
90
|
+
const selection = await loadSelectedSpace(projectDir, selectedKey);
|
|
91
|
+
const sourceFiles = collectSelectedSpaceSource(projectDir, selection);
|
|
92
|
+
const sourceHash = appFilesDigest(sourceFiles);
|
|
93
|
+
const staging = freezeSourceWorkspace(projectDir, sourceFiles);
|
|
94
|
+
let release;
|
|
95
|
+
try {
|
|
96
|
+
if (existsSync(join(projectDir, 'node_modules'))) {
|
|
97
|
+
symlinkSync(join(projectDir, 'node_modules'), join(staging.projectDir, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir');
|
|
98
|
+
}
|
|
99
|
+
const frozenSelection = await loadSelectedSpace(staging.projectDir, selectedKey);
|
|
100
|
+
await buildArtifact(staging.projectDir, { stdio, refreshSdk: false, executable: Boolean(selection.entry),
|
|
101
|
+
prepare: directory => prepareSelectedArtifact(directory, frozenSelection, { designBar, log: designLog }), finalize: finalizeSelectedArtifact,
|
|
102
|
+
...(runBuild ? { runBuild } : {}) });
|
|
103
|
+
const current = await loadSelectedSpace(projectDir, selectedKey);
|
|
104
|
+
if (sourceHash !== appFilesDigest(collectSelectedSpaceSource(projectDir, current))) throw usageError('This Space source changed during the selected build. Build again.');
|
|
105
|
+
release = prepareAppRelease(staging.projectDir);
|
|
106
|
+
const close = release.close;
|
|
107
|
+
return { ...release, selectedKey, sourceHash, fixturePath: selection.fixturePath, close() { try { close(); } finally { staging.close(); } } };
|
|
108
|
+
} catch (error) {
|
|
109
|
+
try { release?.close(); } finally { staging.close(); }
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function buildSpaceArtifact(projectDir, selectedKey, options) {
|
|
115
|
+
const release = await buildSpaceRelease(projectDir, selectedKey, options);
|
|
116
|
+
try {
|
|
117
|
+
const receiptPath = saveSelectedBuild(projectDir, selectedKey, release);
|
|
118
|
+
return { manifest: release.manifest, receiptPath, sourceHash: release.sourceHash };
|
|
119
|
+
} finally { release.close(); }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function prepareSpaceRelease(projectDir, selectedKey) {
|
|
123
|
+
const selection = await loadSelectedSpace(projectDir, selectedKey);
|
|
124
|
+
const source = collectSelectedSpaceSource(projectDir, selection);
|
|
125
|
+
const release = prepareSelectedBuild(projectDir, selectedKey, source);
|
|
126
|
+
if (release.manifest?.schema !== 'notis-space/v1' || release.manifest.local_key !== selectedKey) {
|
|
127
|
+
release.close(); throw usageError('The saved build belongs to a different Space.');
|
|
128
|
+
}
|
|
129
|
+
// Verify and deploy check the exact saved source the server will check again.
|
|
130
|
+
try { validateSourceDesign(release.sourceFiles, { profile: 'space', encoding: 'base64' }); }
|
|
131
|
+
catch (error) { release.close(); throw error; }
|
|
132
|
+
return { ...release, fixturePath: selection.fixturePath };
|
|
133
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/** Selected, reproducible source slices; never archive a sibling by tree position. */
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { dirname, extname, join, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { collectSourceFiles, loadAppConfig } from './app-platform.js';
|
|
6
|
+
import { usageError } from './errors.js';
|
|
7
|
+
import { VIEW_DEFINITION_FIELDS, viewManifestFields } from './space-view-manifest.js';
|
|
8
|
+
|
|
9
|
+
const CONFIG_FIELDS = new Set(['name', 'description', 'icon', 'accent', 'entry', 'layout', 'readableContext', 'resources', 'actions', 'navigation', 'viewerReads', 'cloudComputer', 'collection', 'sourceIncludes', 'verificationFixtures', ...VIEW_DEFINITION_FIELDS]);
|
|
10
|
+
/** Reads with each signed-in viewer's own authority (never an issuer grant); see docs/notis-spaces-platform.md. */
|
|
11
|
+
export const VIEWER_READ_FAMILIES = Object.freeze(['databases', 'skills']);
|
|
12
|
+
const WORKSPACE_INDEXES = new Set(['notis.config.ts', 'notis.config.js', 'notis.config.mjs']);
|
|
13
|
+
const ROOT_CONFIGS = ['package.json', 'package-lock.json', 'tsconfig.json', 'vite.config.ts', 'vite.config.js', 'vite.config.mjs',
|
|
14
|
+
'postcss.config.js', 'postcss.config.cjs', 'postcss.config.mjs', 'tailwind.config.ts', 'tailwind.config.js', 'tailwind.config.cjs', 'tailwind.config.mjs'];
|
|
15
|
+
|
|
16
|
+
function sourceCompiler(projectDir, files) {
|
|
17
|
+
const require = createRequire(join(projectDir, 'package.json'));
|
|
18
|
+
let ts;
|
|
19
|
+
try { ts = require('typescript'); }
|
|
20
|
+
catch { throw usageError('Install this workspace\'s TypeScript dependency before packaging Space source.'); }
|
|
21
|
+
const codeFiles = Object.keys(files).filter(path => /\.[cm]?[jt]sx?$/.test(path)).map(path => join(projectDir, path));
|
|
22
|
+
let source, checker, syntax, close = () => {};
|
|
23
|
+
if (typeof ts.createProgram === 'function') {
|
|
24
|
+
let options = { moduleResolution: ts.ModuleResolutionKind.Bundler, allowJs: true };
|
|
25
|
+
if (files['tsconfig.json']) {
|
|
26
|
+
const config = ts.readConfigFile(join(projectDir, 'tsconfig.json'), ts.sys.readFile);
|
|
27
|
+
if (config.error) throw usageError('Fix tsconfig.json before building this Space.');
|
|
28
|
+
options = { ...options, ...ts.parseJsonConfigFileContent(config.config, ts.sys, projectDir).options, allowJs: true };
|
|
29
|
+
}
|
|
30
|
+
const program = ts.createProgram(codeFiles, options), projectChecker = program.getTypeChecker();
|
|
31
|
+
source = file => program.getSourceFile(join(projectDir, file));
|
|
32
|
+
checker = () => projectChecker;
|
|
33
|
+
syntax = ts.SyntaxKind;
|
|
34
|
+
} else {
|
|
35
|
+
// TypeScript 7 ships the native compiler and moves JS APIs under unstable/.
|
|
36
|
+
// Keep one scoped API process, rather than falling back to regex parsing or
|
|
37
|
+
// assuming the old typescript package exports a parser.
|
|
38
|
+
const { API } = require('typescript/unstable/sync');
|
|
39
|
+
syntax = require('typescript/unstable/ast').SyntaxKind;
|
|
40
|
+
const api = new API({ cwd: projectDir });
|
|
41
|
+
try {
|
|
42
|
+
const snapshot = api.updateSnapshot({ openFiles: codeFiles });
|
|
43
|
+
const project = file => snapshot.getDefaultProjectForFile(join(projectDir, file));
|
|
44
|
+
source = file => project(file)?.program.getSourceFile(join(projectDir, file));
|
|
45
|
+
checker = file => project(file)?.checker;
|
|
46
|
+
close = () => { try { snapshot.dispose(); } finally { api.close(); } };
|
|
47
|
+
} catch (error) { api.close(); throw error; }
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
close,
|
|
51
|
+
inspect(file) {
|
|
52
|
+
const tree = source(file);
|
|
53
|
+
if (!tree) throw usageError(`The compiler could not inspect ${file}. Include it in this project's TypeScript configuration.`);
|
|
54
|
+
let dynamic = false;
|
|
55
|
+
const assets = [];
|
|
56
|
+
const visit = node => {
|
|
57
|
+
if (node.kind === syntax.CallExpression && node.expression.kind === syntax.ImportKeyword
|
|
58
|
+
&& ![syntax.StringLiteral, syntax.NoSubstitutionTemplateLiteral].includes(node.arguments[0]?.kind)) dynamic = true;
|
|
59
|
+
if (node.kind === syntax.NewExpression && node.expression.getText() === 'URL' && node.arguments?.[1]?.getText() === 'import.meta.url') {
|
|
60
|
+
if ([syntax.StringLiteral, syntax.NoSubstitutionTemplateLiteral].includes(node.arguments[0]?.kind)) assets.push(node.arguments[0].text);
|
|
61
|
+
else dynamic = true;
|
|
62
|
+
}
|
|
63
|
+
if (node.kind === syntax.CallExpression && node.expression.getText().startsWith('import.meta.glob')) dynamic = true;
|
|
64
|
+
node.forEachChild(visit);
|
|
65
|
+
};
|
|
66
|
+
visit(tree);
|
|
67
|
+
return { dynamic, assets, references: (tree.referencedFiles || []).map(item => item.fileName),
|
|
68
|
+
imports: (tree.imports || []).map(node => {
|
|
69
|
+
const symbol = checker(file)?.getSymbolAtLocation(node);
|
|
70
|
+
const declaration = symbol?.declarations?.[0];
|
|
71
|
+
const resolved = declaration?.resolve ? declaration.resolve() : declaration;
|
|
72
|
+
return { specifier: node.text, resolved: resolved?.getSourceFile().fileName };
|
|
73
|
+
}) };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function key(value, label) {
|
|
79
|
+
if (typeof value !== 'string' || !/^[a-z][a-z0-9_-]{0,99}$/.test(value)) throw usageError(`${label} must be a stable lowercase source key.`);
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function localPath(value) {
|
|
84
|
+
if (typeof value !== 'string' || !value || value.includes('\\') || value.startsWith('/') || value.includes('\0')
|
|
85
|
+
|| value.split('/').some(part => part === '..') || value.startsWith('.notis/') || value.startsWith('.env')) {
|
|
86
|
+
throw usageError('Use a source path inside this workspace, without private profile state.');
|
|
87
|
+
}
|
|
88
|
+
return value.replace(/^\.\//, '').replace(/\/$/, '');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function loadSelectedSpace(projectDir, selectedKey) {
|
|
92
|
+
projectDir = realpathSync(projectDir);
|
|
93
|
+
selectedKey = key(selectedKey, 'Space');
|
|
94
|
+
const workspace = await loadAppConfig(projectDir);
|
|
95
|
+
if (!workspace || typeof workspace !== 'object' || Array.isArray(workspace) || !workspace.spaces
|
|
96
|
+
|| typeof workspace.spaces !== 'object' || Object.keys(workspace).some(name => !['spaces', 'resources', 'defaultSpace'].includes(name))) {
|
|
97
|
+
throw usageError('Use defineSpaces with an explicit workspace Space index.');
|
|
98
|
+
}
|
|
99
|
+
if (!Object.hasOwn(workspace.spaces, selectedKey)) throw usageError('Choose a Space declared in this source workspace.');
|
|
100
|
+
const reference = workspace.spaces[selectedKey];
|
|
101
|
+
if (!reference || typeof reference !== 'object' || Object.keys(reference).some(name => !['definition', 'parent', 'defaultChild'].includes(name))) {
|
|
102
|
+
throw usageError('A workspace Space entry declares a definition file and optional composition.');
|
|
103
|
+
}
|
|
104
|
+
const definitionPath = localPath(reference.definition);
|
|
105
|
+
const definition = await loadAppConfig(projectDir, { file: definitionPath });
|
|
106
|
+
if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
|
|
107
|
+
throw usageError('Export a Space definition object with defineSpace.');
|
|
108
|
+
}
|
|
109
|
+
const unsupported = Object.keys(definition).filter(name => !CONFIG_FIELDS.has(name)).sort();
|
|
110
|
+
if (unsupported.length) {
|
|
111
|
+
const hint = unsupported.includes('settings') ? ' Define URL-visible controls with params.' : '';
|
|
112
|
+
throw usageError(`Unsupported Space definition fields: ${unsupported.join(', ')}.${hint}`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof definition.name !== 'string' || !definition.name.trim() || definition.name.length > 200) {
|
|
115
|
+
throw usageError('Give the Space a name of 1 to 200 characters.');
|
|
116
|
+
}
|
|
117
|
+
const resolveDefinitionPath = value => {
|
|
118
|
+
if (typeof value !== 'string' || !value || value.startsWith('/') || value.includes('\\') || value.includes('\0')) throw usageError('Use a workspace-relative source path.');
|
|
119
|
+
return localPath(relative(projectDir, resolve(projectDir, dirname(definitionPath), value)).split(sep).join('/'));
|
|
120
|
+
};
|
|
121
|
+
const entry = definition.entry ? resolveDefinitionPath(definition.entry) : null;
|
|
122
|
+
const layout = definition.layout ? resolveDefinitionPath(definition.layout) : null;
|
|
123
|
+
if (!entry && layout) throw usageError('A container has no executable layout.');
|
|
124
|
+
const resources = definition.resources || {};
|
|
125
|
+
if (definition.navigation != null && (typeof definition.navigation !== 'object' || Array.isArray(definition.navigation))) {
|
|
126
|
+
throw usageError('Declare portable named Space destinations.');
|
|
127
|
+
}
|
|
128
|
+
for (const [alias, target] of Object.entries(definition.navigation || {})) {
|
|
129
|
+
key(alias, 'Navigation alias');
|
|
130
|
+
if (!target || typeof target !== 'object' || Object.keys(target).join(',') !== 'key') throw usageError('A navigation reference needs one portable key.');
|
|
131
|
+
key(target.key, 'Navigation target');
|
|
132
|
+
}
|
|
133
|
+
for (const [alias, resource] of Object.entries(resources)) {
|
|
134
|
+
key(alias, 'Resource alias');
|
|
135
|
+
if (resource?.kind === 'database' && resource.create != null) {
|
|
136
|
+
const value = resource.create;
|
|
137
|
+
if (Object.keys(resource).some(name => !['kind', 'create'].includes(name))
|
|
138
|
+
|| !value || typeof value !== 'object' || Array.isArray(value)
|
|
139
|
+
|| Object.keys(value).some(name => !['name', 'schema', 'starterRows'].includes(name))
|
|
140
|
+
|| typeof value.name !== 'string' || !value.name.trim() || value.name.length > 200
|
|
141
|
+
|| !value.schema || typeof value.schema !== 'object' || Array.isArray(value.schema)
|
|
142
|
+
|| (value.starterRows !== undefined && (!Array.isArray(value.starterRows) || value.starterRows.length > 100
|
|
143
|
+
|| value.starterRows.some(row => !row || typeof row !== 'object' || Array.isArray(row))))) {
|
|
144
|
+
throw usageError(`Database ${alias} needs create:{name,schema,starterRows?}, without an existing key.`);
|
|
145
|
+
}
|
|
146
|
+
if (!Object.values(definition.params || {}).some(param => param?.type === 'record' && param.database === alias && param.main === true)) {
|
|
147
|
+
throw usageError(`New database ${alias} needs a record param marked main in this view.`);
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!resource || !['database', 'document', 'skill', 'automation'].includes(resource.kind) || typeof resource.key !== 'string'
|
|
152
|
+
|| Object.keys(resource).some(name => !['kind', 'key'].includes(name))) throw usageError(`Invalid resource reference: ${alias}`);
|
|
153
|
+
}
|
|
154
|
+
if (definition.viewerReads != null && (!Array.isArray(definition.viewerReads)
|
|
155
|
+
|| definition.viewerReads.some(family => !VIEWER_READ_FAMILIES.includes(family))
|
|
156
|
+
|| new Set(definition.viewerReads).size !== definition.viewerReads.length)) {
|
|
157
|
+
throw usageError("viewerReads lists 'databases' and/or 'skills' once each: the page reads what each signed-in viewer can open.");
|
|
158
|
+
}
|
|
159
|
+
if (definition.viewerReads?.length && !entry) throw usageError('A container has no page, so it cannot declare viewerReads.');
|
|
160
|
+
if (definition.cloudComputer != null && !['read', 'shell'].includes(definition.cloudComputer)) {
|
|
161
|
+
throw usageError("cloudComputer is 'read' or 'shell': the page uses each signed-in viewer's own cloud computer once that viewer allows it.");
|
|
162
|
+
}
|
|
163
|
+
if (definition.cloudComputer != null && !entry) throw usageError('A container has no page, so it cannot use the cloud computer.');
|
|
164
|
+
if (definition.collection && (resources[definition.collection.database]?.kind !== 'database' || !definition.collection.titleProperty)) {
|
|
165
|
+
throw usageError('A collection needs a declared database binding and title property.');
|
|
166
|
+
}
|
|
167
|
+
for (const [actionId, template] of Object.entries(definition.actions || {})) {
|
|
168
|
+
key(actionId, 'Action');
|
|
169
|
+
if (!template || typeof template.tool !== 'string' || !template.tool || Object.keys(template).sort().join(',') !== 'arguments,inputs,tool'
|
|
170
|
+
|| template.inputs?.type !== 'object' || template.inputs?.additionalProperties !== false || !template.arguments || Array.isArray(template.arguments)) {
|
|
171
|
+
throw usageError(`Action ${actionId} needs the canonical closed inputs/tool/arguments template.`);
|
|
172
|
+
}
|
|
173
|
+
const scan = value => {
|
|
174
|
+
if (Array.isArray(value)) { value.forEach(scan); return; }
|
|
175
|
+
if (!value || typeof value !== 'object' || Object.hasOwn(value, '$literal')) return;
|
|
176
|
+
if (Object.hasOwn(value, '$asset') && !Object.hasOwn(resources, value.$asset)) throw usageError(`Action ${actionId} uses an undeclared resource alias.`);
|
|
177
|
+
Object.values(value).forEach(scan);
|
|
178
|
+
};
|
|
179
|
+
scan(template.arguments);
|
|
180
|
+
}
|
|
181
|
+
// V4 view manifest (path, params, shows, memory, chrome, markdown); required with specVersion 2.
|
|
182
|
+
viewManifestFields(definition, { entry, resources });
|
|
183
|
+
const markdown = definition.markdown ? resolveDefinitionPath(definition.markdown) : null;
|
|
184
|
+
const fixturePath = definition.verificationFixtures ? resolveDefinitionPath(definition.verificationFixtures) : null;
|
|
185
|
+
return { key: selectedKey, workspace, definition, definitionPath, entry, layout, markdown, fixturePath, resolveDefinitionPath };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
/** Include type-only/local declarations too; bundle chunks alone omit them. */
|
|
190
|
+
export function collectSelectedSpaceSource(projectDir, selection, { buildInputs = [] } = {}) {
|
|
191
|
+
projectDir = realpathSync(projectDir);
|
|
192
|
+
const files = collectSourceFiles(projectDir), selected = new Set(), queued = [];
|
|
193
|
+
const compiler = sourceCompiler(projectDir, files);
|
|
194
|
+
try {
|
|
195
|
+
function add(path) {
|
|
196
|
+
path = localPath(path);
|
|
197
|
+
if (path === 'resources.json' || path === 'skills' || path.startsWith('skills/')) {
|
|
198
|
+
throw usageError('Resource lists and Skill folders use native Space transport, not sourceIncludes or view imports.');
|
|
199
|
+
}
|
|
200
|
+
// This is a synthesized input in the frozen workspace. Its original imports
|
|
201
|
+
// describe unrelated composition, not dependencies of the selected Space.
|
|
202
|
+
if (WORKSPACE_INDEXES.has(path)) return;
|
|
203
|
+
if (!Object.hasOwn(files, path)) throw usageError(`Source dependency is missing, private or linked: ${path}`);
|
|
204
|
+
if (!selected.has(path)) { selected.add(path); queued.push(path); }
|
|
205
|
+
}
|
|
206
|
+
function dependency(specifier, importer, resolved) {
|
|
207
|
+
if (typeof specifier !== 'string' || specifier.startsWith('node:')) return;
|
|
208
|
+
if (resolved) {
|
|
209
|
+
const path = relative(projectDir, resolved).split(sep).join('/');
|
|
210
|
+
const external = path.includes('/node_modules/') || path.startsWith('node_modules/');
|
|
211
|
+
if (!external) { add(path); return; }
|
|
212
|
+
// A relative import is always project source. An ambient declaration such as
|
|
213
|
+
// vite/client's `*.css` module can claim it depending on the working directory.
|
|
214
|
+
if (!specifier.startsWith('.')) return;
|
|
215
|
+
}
|
|
216
|
+
const raw = specifier.split(/[?#]/, 1)[0];
|
|
217
|
+
const path = relative(projectDir, resolve(dirname(join(projectDir, importer)), raw)).split(sep).join('/');
|
|
218
|
+
const candidate = [path, ...['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.d.ts', '/index.ts', '/index.tsx', '/index.js'].map(suffix => path + suffix)]
|
|
219
|
+
.find(name => Object.hasOwn(files, name));
|
|
220
|
+
if (candidate) { add(candidate); return; }
|
|
221
|
+
if (specifier.startsWith('.') || specifier.startsWith('@/') || specifier.startsWith('#')) throw usageError(`Unresolved source dependency ${specifier} in ${importer}.`);
|
|
222
|
+
}
|
|
223
|
+
ROOT_CONFIGS.filter(path => Object.hasOwn(files, path)).forEach(add);
|
|
224
|
+
const projectPackage = JSON.parse(Buffer.from(files['package.json'] || '', 'base64').toString('utf8'));
|
|
225
|
+
for (const [name, dependency] of Object.entries({ ...projectPackage.dependencies, ...projectPackage.devDependencies })) {
|
|
226
|
+
if (typeof dependency !== 'string' || !dependency.startsWith('file:')) continue;
|
|
227
|
+
const directory = localPath(dependency.slice(5));
|
|
228
|
+
add(`${directory}/package.json`);
|
|
229
|
+
const metadata = JSON.parse(Buffer.from(files[`${directory}/package.json`], 'base64').toString('utf8'));
|
|
230
|
+
// The SDK's `node` targets are its test runtime in dist/: never Space source, never uploaded.
|
|
231
|
+
const declared = (value, condition) => {
|
|
232
|
+
if (name === '@notis/sdk' && condition === 'node') return;
|
|
233
|
+
if (typeof value === 'string' && value.startsWith('./')) add(`${directory}/${value.slice(2)}`);
|
|
234
|
+
else if (value && typeof value === 'object') Object.entries(value).forEach(([key, item]) => declared(item, key));
|
|
235
|
+
};
|
|
236
|
+
declared(metadata.exports);
|
|
237
|
+
for (const value of [metadata.main, metadata.module, metadata.types]) if (typeof value === 'string') add(`${directory}/${value.replace(/^\.\//, '')}`);
|
|
238
|
+
}
|
|
239
|
+
add(selection.definitionPath);
|
|
240
|
+
if (selection.entry) add(selection.entry);
|
|
241
|
+
if (selection.layout) add(selection.layout);
|
|
242
|
+
if (selection.markdown) add(selection.markdown);
|
|
243
|
+
if (selection.fixturePath) add(selection.fixturePath);
|
|
244
|
+
for (const include of selection.definition.sourceIncludes || []) {
|
|
245
|
+
const path = selection.resolveDefinitionPath(include);
|
|
246
|
+
const matches = Object.keys(files).filter(name => name === path || name.startsWith(path + '/'));
|
|
247
|
+
if (!matches.length) throw usageError(`No source files match explicit inclusion ${path}.`);
|
|
248
|
+
matches.forEach(add);
|
|
249
|
+
}
|
|
250
|
+
for (const reference of Object.values(selection.definition.resources || {})) {
|
|
251
|
+
if (reference.create || reference.kind === 'skill') continue;
|
|
252
|
+
const resource = selection.workspace.resources?.[reference.key];
|
|
253
|
+
if (resource) {
|
|
254
|
+
if (resource.kind !== reference.kind) throw usageError(`Resource kind changed for ${reference.key}.`);
|
|
255
|
+
const path = localPath(resource.source);
|
|
256
|
+
const matches = Object.keys(files).filter(name => name === path || name.startsWith(path + '/'));
|
|
257
|
+
if (!matches.length) throw usageError(`Missing declared resource source ${path}.`);
|
|
258
|
+
matches.forEach(add);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
for (const input of buildInputs) {
|
|
262
|
+
const path = relative(projectDir, input.split(/[?#]/, 1)[0]).split(sep).join('/');
|
|
263
|
+
if (path.startsWith('.notis/') || path.includes('/node_modules/') || path.startsWith('node_modules/')) continue;
|
|
264
|
+
add(path);
|
|
265
|
+
}
|
|
266
|
+
while (queued.length) {
|
|
267
|
+
const file = queued.shift(), source = Buffer.from(files[file], 'base64').toString('utf8');
|
|
268
|
+
if (/\.[cm]?[jt]sx?$/.test(file)) {
|
|
269
|
+
const parsed = compiler.inspect(file);
|
|
270
|
+
for (const entry of parsed.imports) dependency(entry.specifier, file, entry.resolved);
|
|
271
|
+
for (const reference of parsed.references) dependency(reference, file);
|
|
272
|
+
for (const asset of parsed.assets) dependency(asset, file);
|
|
273
|
+
if (parsed.dynamic && !(selection.definition.sourceIncludes || []).length) throw usageError(`Declare sourceIncludes for dynamic source dependencies in ${file}.`);
|
|
274
|
+
} else if (extname(file) === '.css') {
|
|
275
|
+
for (const match of source.matchAll(/@(import|config)\s+(?:url\(\s*)?['"]([^'"]+)['"]/g)) {
|
|
276
|
+
if (match[2].startsWith('.')) dependency(match[2], file);
|
|
277
|
+
}
|
|
278
|
+
for (const match of source.matchAll(/url\(\s*['"]?(\.[^'"\s)]+)['"]?\s*\)/g)) dependency(match[1], file);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const result = Object.fromEntries([...selected].sort().map(path => [path, files[path]]));
|
|
282
|
+
// The exported index describes this target only. Composition is not a source
|
|
283
|
+
// permission and publishing one definition never applies siblings or parents.
|
|
284
|
+
for (const path of WORKSPACE_INDEXES) delete result[path];
|
|
285
|
+
const resourceKeys = new Set(Object.values(selection.definition.resources || {}).map(resource => resource.key));
|
|
286
|
+
const resources = Object.fromEntries(Object.entries(selection.workspace.resources || {}).filter(([name]) => resourceKeys.has(name)));
|
|
287
|
+
result['notis.config.ts'] = Buffer.from(`import { defineSpaces } from '@notis/sdk/config';\nexport default defineSpaces(${JSON.stringify({ spaces: { [selection.key]: { definition: selection.definitionPath } }, resources, defaultSpace: selection.key }, null, 2)});\n`).toString('base64');
|
|
288
|
+
return result;
|
|
289
|
+
} finally { compiler.close(); }
|
|
290
|
+
}
|