@notis_ai/cli 0.2.17 → 0.2.19
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 +42323 -18258
- 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 +271 -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 -1568
- 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 -3000
- 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
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
export const APP_CHANGELOG_FILE = 'CHANGELOG.md';
|
|
5
|
-
export const CHANGELOG_MERGE_DATE = '{PR_MERGE_DATE}';
|
|
6
|
-
|
|
7
|
-
const ENTRY_HEADING = /^##\s+\[([^\]]+)]\s+-\s+(\{PR_MERGE_DATE\}|\d{4}-\d{2}-\d{2})\s*$/;
|
|
8
|
-
|
|
9
|
-
function validCalendarDate(value) {
|
|
10
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
|
11
|
-
const date = new Date(`${value}T00:00:00Z`);
|
|
12
|
-
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function parseAppChangelog(markdown, { sourcePath = APP_CHANGELOG_FILE } = {}) {
|
|
16
|
-
const lines = String(markdown || '').replace(/\r\n?/g, '\n').split('\n');
|
|
17
|
-
const entries = [];
|
|
18
|
-
const errors = [];
|
|
19
|
-
let current = null;
|
|
20
|
-
|
|
21
|
-
function finishCurrent() {
|
|
22
|
-
if (!current) return;
|
|
23
|
-
entries.push({
|
|
24
|
-
title: current.title,
|
|
25
|
-
date: current.date,
|
|
26
|
-
body: current.body.join('\n').trim(),
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
for (const [index, line] of lines.entries()) {
|
|
31
|
-
// Only a level-2 heading (`## ...`, not `###`/`####`) can start a release
|
|
32
|
-
// entry; deeper sub-section headings are legitimate release-body content.
|
|
33
|
-
if (!/^##(?!#)/.test(line)) {
|
|
34
|
-
if (current) current.body.push(line);
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const match = line.match(ENTRY_HEADING);
|
|
39
|
-
if (!match) {
|
|
40
|
-
errors.push(
|
|
41
|
-
`${sourcePath}:${index + 1} must use \`## [Release title] - YYYY-MM-DD\` or \`## [Release title] - {PR_MERGE_DATE}\`.`,
|
|
42
|
-
);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
finishCurrent();
|
|
47
|
-
const title = match[1].trim();
|
|
48
|
-
const date = match[2];
|
|
49
|
-
if (!title) {
|
|
50
|
-
errors.push(`${sourcePath}:${index + 1} must include a release title inside square brackets.`);
|
|
51
|
-
}
|
|
52
|
-
if (date !== CHANGELOG_MERGE_DATE && !validCalendarDate(date)) {
|
|
53
|
-
errors.push(`${sourcePath}:${index + 1} contains an invalid calendar date.`);
|
|
54
|
-
}
|
|
55
|
-
current = { title, date, body: [] };
|
|
56
|
-
}
|
|
57
|
-
finishCurrent();
|
|
58
|
-
|
|
59
|
-
if (entries.length === 0 && errors.length === 0) {
|
|
60
|
-
errors.push(
|
|
61
|
-
`${sourcePath} must include at least one \`## [Release title] - YYYY-MM-DD\` entry.`,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { source_path: sourcePath, entries, errors, exists: true };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function readAppChangelog(projectDir) {
|
|
69
|
-
const path = join(projectDir, APP_CHANGELOG_FILE);
|
|
70
|
-
if (!existsSync(path)) {
|
|
71
|
-
return {
|
|
72
|
-
source_path: APP_CHANGELOG_FILE,
|
|
73
|
-
entries: [],
|
|
74
|
-
errors: [`${APP_CHANGELOG_FILE} is required for Store publication.`],
|
|
75
|
-
exists: false,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return parseAppChangelog(readFileSync(path, 'utf-8'));
|
|
79
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
existsSync,
|
|
4
|
-
lstatSync,
|
|
5
|
-
mkdirSync,
|
|
6
|
-
readFileSync,
|
|
7
|
-
renameSync,
|
|
8
|
-
rmSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
} from 'node:fs';
|
|
11
|
-
import { homedir } from 'node:os';
|
|
12
|
-
import { dirname, join } from 'node:path';
|
|
13
|
-
|
|
14
|
-
export const DEFAULT_APP_DEV_CONSUMERS_FILE = join(homedir(), '.notis', 'app-dev-consumers.json');
|
|
15
|
-
export const APP_DEV_CONSUMER_EXPIRES_AFTER_MS = 10_000;
|
|
16
|
-
const waitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
17
|
-
const CONSUMER_LOCK_STALE_AFTER_MS = 30_000;
|
|
18
|
-
|
|
19
|
-
function processIsRunning(pid) {
|
|
20
|
-
try {
|
|
21
|
-
process.kill(pid, 0);
|
|
22
|
-
return true;
|
|
23
|
-
} catch (error) {
|
|
24
|
-
return error?.code !== 'ESRCH';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function reclaimConsumerLock(lockPath) {
|
|
29
|
-
const stat = lstatSync(lockPath);
|
|
30
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
31
|
-
throw new Error(`Refusing unsafe app development consumer lock: ${lockPath}`);
|
|
32
|
-
}
|
|
33
|
-
let ownerPid = null;
|
|
34
|
-
try {
|
|
35
|
-
const owner = readFileSync(join(lockPath, 'owner'), 'utf8').trim();
|
|
36
|
-
const parsed = Number.parseInt(owner.split('.')[0] || '', 10);
|
|
37
|
-
ownerPid = Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
38
|
-
} catch {
|
|
39
|
-
// An interrupted owner write is reclaimed after the bounded stale window.
|
|
40
|
-
}
|
|
41
|
-
if (ownerPid !== null) return !processIsRunning(ownerPid);
|
|
42
|
-
return Date.now() - stat.mtimeMs >= CONSUMER_LOCK_STALE_AFTER_MS;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function withLock(filePath, callback) {
|
|
46
|
-
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
47
|
-
const lockPath = `${filePath}.lock`;
|
|
48
|
-
const owner = `${process.pid}.${randomUUID()}`;
|
|
49
|
-
const startedAt = Date.now();
|
|
50
|
-
while (true) {
|
|
51
|
-
try {
|
|
52
|
-
mkdirSync(lockPath, { mode: 0o700 });
|
|
53
|
-
writeFileSync(join(lockPath, 'owner'), owner, { mode: 0o600 });
|
|
54
|
-
break;
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (error?.code !== 'EEXIST') throw error;
|
|
57
|
-
try {
|
|
58
|
-
if (reclaimConsumerLock(lockPath)) {
|
|
59
|
-
rmSync(lockPath, { recursive: true, force: true });
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
} catch (lockError) {
|
|
63
|
-
if (lockError?.code === 'ENOENT') continue;
|
|
64
|
-
throw lockError;
|
|
65
|
-
}
|
|
66
|
-
if (Date.now() - startedAt > 2_000) {
|
|
67
|
-
throw new Error('App development consumer registry is busy.');
|
|
68
|
-
}
|
|
69
|
-
Atomics.wait(waitArray, 0, 0, 10);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
return callback();
|
|
74
|
-
} finally {
|
|
75
|
-
try {
|
|
76
|
-
if (readFileSync(join(lockPath, 'owner'), 'utf8').trim() === owner) {
|
|
77
|
-
rmSync(lockPath, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
// A replaced lock belongs to another writer.
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function normalize(value, now) {
|
|
86
|
-
const raw = value && typeof value === 'object' && Array.isArray(value.consumers)
|
|
87
|
-
? value.consumers
|
|
88
|
-
: [];
|
|
89
|
-
return raw.filter((lease) => {
|
|
90
|
-
const heartbeat = Date.parse(String(lease?.lastHeartbeatAt || ''));
|
|
91
|
-
return typeof lease?.instanceId === 'string'
|
|
92
|
-
&& typeof lease?.userId === 'string'
|
|
93
|
-
&& typeof lease?.apiBase === 'string'
|
|
94
|
-
&& Number.isSafeInteger(lease?.pid)
|
|
95
|
-
&& Number.isFinite(heartbeat)
|
|
96
|
-
&& now - heartbeat <= APP_DEV_CONSUMER_EXPIRES_AFTER_MS;
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function readAppDevConsumers(filePath = DEFAULT_APP_DEV_CONSUMERS_FILE, now = Date.now()) {
|
|
101
|
-
if (!existsSync(filePath)) return [];
|
|
102
|
-
try {
|
|
103
|
-
return normalize(JSON.parse(readFileSync(filePath, 'utf8')), now);
|
|
104
|
-
} catch {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function writeAppDevConsumers(filePath, consumers) {
|
|
110
|
-
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
111
|
-
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
112
|
-
writeFileSync(temporary, JSON.stringify({ version: 1, consumers }, null, 2), { mode: 0o600 });
|
|
113
|
-
renameSync(temporary, filePath);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function heartbeatAppDevConsumer(
|
|
117
|
-
lease,
|
|
118
|
-
filePath = DEFAULT_APP_DEV_CONSUMERS_FILE,
|
|
119
|
-
) {
|
|
120
|
-
return withLock(filePath, () => {
|
|
121
|
-
const now = Date.now();
|
|
122
|
-
const next = readAppDevConsumers(filePath, now)
|
|
123
|
-
.filter((entry) => entry.instanceId !== lease.instanceId);
|
|
124
|
-
next.push({
|
|
125
|
-
...lease,
|
|
126
|
-
apiBase: lease.apiBase.replace(/\/$/, ''),
|
|
127
|
-
lastHeartbeatAt: new Date(now).toISOString(),
|
|
128
|
-
});
|
|
129
|
-
writeAppDevConsumers(filePath, next);
|
|
130
|
-
return next;
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function removeAppDevConsumer(
|
|
135
|
-
instanceId,
|
|
136
|
-
filePath = DEFAULT_APP_DEV_CONSUMERS_FILE,
|
|
137
|
-
) {
|
|
138
|
-
return withLock(filePath, () => {
|
|
139
|
-
const next = readAppDevConsumers(filePath)
|
|
140
|
-
.filter((entry) => entry.instanceId !== instanceId);
|
|
141
|
-
writeAppDevConsumers(filePath, next);
|
|
142
|
-
return next;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function hasAppDevConsumer(consumers, { mode, userId, apiBase }) {
|
|
147
|
-
if (mode === 'machine') return consumers.length > 0;
|
|
148
|
-
if (mode !== 'environment') return true;
|
|
149
|
-
const normalizedApiBase = String(apiBase || '').replace(/\/$/, '');
|
|
150
|
-
return consumers.some((consumer) => (
|
|
151
|
-
consumer.userId === userId
|
|
152
|
-
&& consumer.apiBase.replace(/\/$/, '') === normalizedApiBase
|
|
153
|
-
));
|
|
154
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
|
|
6
|
-
const DEFAULT_LOCK_ROOT = join(homedir(), '.notis', 'app-dev-host-locks');
|
|
7
|
-
const OWNER_FILE = 'owner.json';
|
|
8
|
-
|
|
9
|
-
function processIsRunning(pid) {
|
|
10
|
-
try {
|
|
11
|
-
process.kill(pid, 0);
|
|
12
|
-
return true;
|
|
13
|
-
} catch (error) {
|
|
14
|
-
return error?.code !== 'ESRCH';
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function lockName(identity, apiBase, projectDir) {
|
|
19
|
-
return createHash('sha256').update(`${identity}\0${apiBase}\0${projectDir}`).digest('hex');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function readOwner(path) {
|
|
23
|
-
try {
|
|
24
|
-
const value = JSON.parse(readFileSync(join(path, OWNER_FILE), 'utf8'));
|
|
25
|
-
return Number.isSafeInteger(value?.pid) && typeof value?.ownerId === 'string'
|
|
26
|
-
? { pid: value.pid, ownerId: value.ownerId }
|
|
27
|
-
: null;
|
|
28
|
-
} catch {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function reclaimable(path, now, staleAfterMs) {
|
|
34
|
-
const stat = lstatSync(path);
|
|
35
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
36
|
-
throw new Error(`Refusing unsafe app development host lock: ${path}`);
|
|
37
|
-
}
|
|
38
|
-
const owner = readOwner(path);
|
|
39
|
-
if (owner) return !processIsRunning(owner.pid);
|
|
40
|
-
return now - stat.mtimeMs >= staleAfterMs;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function tryAcquireAppDevHostLock(options) {
|
|
44
|
-
const lockRoot = options.lockRoot || DEFAULT_LOCK_ROOT;
|
|
45
|
-
mkdirSync(lockRoot, { recursive: true, mode: 0o700 });
|
|
46
|
-
const path = join(lockRoot, lockName(options.identity, options.apiBase, options.projectDir));
|
|
47
|
-
const ownerId = `${process.pid}.${randomUUID()}`;
|
|
48
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
49
|
-
try {
|
|
50
|
-
mkdirSync(path, { mode: 0o700 });
|
|
51
|
-
try {
|
|
52
|
-
writeFileSync(join(path, OWNER_FILE), JSON.stringify({ pid: process.pid, ownerId }), {
|
|
53
|
-
mode: 0o600,
|
|
54
|
-
});
|
|
55
|
-
} catch (error) {
|
|
56
|
-
rmSync(path, { recursive: true, force: true });
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
return { path, ownerId };
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if (error?.code !== 'EEXIST') throw error;
|
|
62
|
-
if (reclaimable(path, options.now ?? Date.now(), options.staleAfterMs ?? 60_000)) {
|
|
63
|
-
rmSync(path, { recursive: true, force: true });
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function releaseAppDevHostLock(lock) {
|
|
73
|
-
try {
|
|
74
|
-
if (readOwner(lock.path)?.ownerId === lock.ownerId) {
|
|
75
|
-
rmSync(lock.path, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
} catch {
|
|
78
|
-
// A replaced lock belongs to another process.
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { readlinkSync, realpathSync } from 'node:fs';
|
|
3
|
-
|
|
4
|
-
export const NOTIS_APP_BUILD_COMMAND_FINGERPRINT = 'npm:run-build:watch:v1';
|
|
5
|
-
export const NOTIS_APPS_DEV_HOST_COMMAND_FINGERPRINT = 'notis:apps-dev:v1';
|
|
6
|
-
|
|
7
|
-
function normalizePath(value) {
|
|
8
|
-
if (typeof value !== 'string' || !value.trim()) return null;
|
|
9
|
-
try {
|
|
10
|
-
return realpathSync(value.trim());
|
|
11
|
-
} catch {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function isExpectedNotisBuildCommand(command) {
|
|
17
|
-
if (typeof command !== 'string') return false;
|
|
18
|
-
return /(?:^|[/\s])npm(?:\s|$)/.test(command)
|
|
19
|
-
&& /\brun\s+build\b/.test(command)
|
|
20
|
-
&& /(?:^|\s)--watch(?:\s|$)/.test(command);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function isExpectedNotisAppsDevHostCommand(command) {
|
|
24
|
-
return typeof command === 'string'
|
|
25
|
-
&& /(?:notis(?:\.js)?|@notis_ai[/\\]cli)/.test(command)
|
|
26
|
-
&& /\bapps\s+dev\b/.test(command);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function readDarwinProcessCwd(pid, execute) {
|
|
30
|
-
const output = execute('lsof', ['-a', '-p', String(pid), '-d', 'cwd', '-Fn'], {
|
|
31
|
-
encoding: 'utf8',
|
|
32
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
33
|
-
});
|
|
34
|
-
const line = output.split('\n').find((entry) => entry.startsWith('n'));
|
|
35
|
-
return line ? line.slice(1) : null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function inspectAppDevWatcherProcess(pid, {
|
|
39
|
-
platform = process.platform,
|
|
40
|
-
execute = execFileSync,
|
|
41
|
-
readLink = readlinkSync,
|
|
42
|
-
} = {}) {
|
|
43
|
-
if (!Number.isSafeInteger(pid) || pid <= 0 || platform === 'win32') return null;
|
|
44
|
-
try {
|
|
45
|
-
const ps = (field) => execute('ps', ['-o', `${field}=`, '-p', String(pid)], {
|
|
46
|
-
encoding: 'utf8',
|
|
47
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
48
|
-
}).trim();
|
|
49
|
-
const processGroupPid = Number.parseInt(ps('pgid'), 10);
|
|
50
|
-
const startIdentity = ps('lstart').replace(/\s+/g, ' ').trim();
|
|
51
|
-
const command = ps('command');
|
|
52
|
-
const cwd = platform === 'linux'
|
|
53
|
-
? readLink(`/proc/${pid}/cwd`)
|
|
54
|
-
: readDarwinProcessCwd(pid, execute);
|
|
55
|
-
const projectDir = normalizePath(cwd);
|
|
56
|
-
if (!Number.isSafeInteger(processGroupPid) || processGroupPid <= 0) return null;
|
|
57
|
-
if (!startIdentity || !command || !projectDir) return null;
|
|
58
|
-
return { pid, processGroupPid, startIdentity, command, projectDir };
|
|
59
|
-
} catch {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function captureDesktopWatcherOwnership({
|
|
65
|
-
pid,
|
|
66
|
-
projectDir,
|
|
67
|
-
desktopOwnerId,
|
|
68
|
-
desktopOwnerScope,
|
|
69
|
-
inspect = inspectAppDevWatcherProcess,
|
|
70
|
-
} = {}) {
|
|
71
|
-
const owner = typeof desktopOwnerId === 'string' ? desktopOwnerId.trim() : '';
|
|
72
|
-
const ownerScope = typeof desktopOwnerScope === 'string' ? desktopOwnerScope.trim() : '';
|
|
73
|
-
const expectedProjectDir = normalizePath(projectDir);
|
|
74
|
-
if (!owner || !ownerScope || !expectedProjectDir) return null;
|
|
75
|
-
const identity = inspect(pid);
|
|
76
|
-
if (
|
|
77
|
-
!identity
|
|
78
|
-
|| identity.processGroupPid !== pid
|
|
79
|
-
|| identity.projectDir !== expectedProjectDir
|
|
80
|
-
|| !isExpectedNotisBuildCommand(identity.command)
|
|
81
|
-
) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
desktopOwnerId: owner,
|
|
86
|
-
desktopOwnerScope: ownerScope,
|
|
87
|
-
watcherProcessGroupPid: identity.processGroupPid,
|
|
88
|
-
watcherStartIdentity: identity.startIdentity,
|
|
89
|
-
watcherProjectDir: identity.projectDir,
|
|
90
|
-
watcherCommandFingerprint: NOTIS_APP_BUILD_COMMAND_FINGERPRINT,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function captureDesktopHostOwnership({
|
|
95
|
-
pid = process.pid,
|
|
96
|
-
desktopOwnerId,
|
|
97
|
-
desktopOwnerScope,
|
|
98
|
-
inspect = inspectAppDevWatcherProcess,
|
|
99
|
-
} = {}) {
|
|
100
|
-
const owner = typeof desktopOwnerId === 'string' ? desktopOwnerId.trim() : '';
|
|
101
|
-
const ownerScope = typeof desktopOwnerScope === 'string' ? desktopOwnerScope.trim() : '';
|
|
102
|
-
if (!owner || !ownerScope) return null;
|
|
103
|
-
const identity = inspect(pid);
|
|
104
|
-
if (!identity || !isExpectedNotisAppsDevHostCommand(identity.command)) return null;
|
|
105
|
-
return {
|
|
106
|
-
desktopOwnerId: owner,
|
|
107
|
-
desktopOwnerScope: ownerScope,
|
|
108
|
-
desktopHostStartIdentity: identity.startIdentity,
|
|
109
|
-
desktopHostCommandFingerprint: NOTIS_APPS_DEV_HOST_COMMAND_FINGERPRINT,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
existsSync,
|
|
4
|
-
lstatSync,
|
|
5
|
-
mkdirSync,
|
|
6
|
-
readFileSync,
|
|
7
|
-
readdirSync,
|
|
8
|
-
realpathSync,
|
|
9
|
-
renameSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
statSync,
|
|
12
|
-
writeFileSync,
|
|
13
|
-
} from 'node:fs';
|
|
14
|
-
import { homedir } from 'node:os';
|
|
15
|
-
import { dirname, join, parse as parsePath, resolve } from 'node:path';
|
|
16
|
-
|
|
17
|
-
import { usageError } from './errors.js';
|
|
18
|
-
import { CONFIG_DIR } from './profiles.js';
|
|
19
|
-
|
|
20
|
-
export const APP_DEV_ROOTS_VERSION = 1;
|
|
21
|
-
export const DEFAULT_APP_DEV_ROOT = join(homedir(), '.notis', 'apps');
|
|
22
|
-
export const DEFAULT_APP_DEV_ROOTS_FILE = join(CONFIG_DIR, 'app-dev-roots.json');
|
|
23
|
-
const CONFIG_FILENAMES = ['notis.config.ts', 'notis.config.js', 'notis.config.mjs'];
|
|
24
|
-
const LOCK_TIMEOUT_MS = 5_000;
|
|
25
|
-
const lockWait = new Int32Array(new SharedArrayBuffer(4));
|
|
26
|
-
|
|
27
|
-
function rootsFile(filePath) {
|
|
28
|
-
if (filePath) return resolve(filePath);
|
|
29
|
-
const envPath = process.env.NOTIS_APP_DEV_ROOTS_FILE;
|
|
30
|
-
return typeof envPath === 'string' && envPath.trim()
|
|
31
|
-
? resolve(envPath.trim())
|
|
32
|
-
: DEFAULT_APP_DEV_ROOTS_FILE;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function legacyProjectsFile(filePath) {
|
|
36
|
-
const sessionsFile = filePath
|
|
37
|
-
|| join(CONFIG_DIR, 'app-dev-sessions.json');
|
|
38
|
-
const parsed = parsePath(resolve(sessionsFile));
|
|
39
|
-
return join(parsed.dir, `${parsed.name}-projects${parsed.ext || '.json'}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function readLockOwner(lockPath) {
|
|
43
|
-
try {
|
|
44
|
-
return readFileSync(join(lockPath, 'owner'), 'utf8').trim();
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function withLock(filePath, callback) {
|
|
51
|
-
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
52
|
-
const lockPath = `${filePath}.lock`;
|
|
53
|
-
const owner = `${process.pid}.${randomUUID()}`;
|
|
54
|
-
const startedAt = Date.now();
|
|
55
|
-
while (true) {
|
|
56
|
-
try {
|
|
57
|
-
mkdirSync(lockPath, { mode: 0o700 });
|
|
58
|
-
writeFileSync(join(lockPath, 'owner'), owner, { mode: 0o600 });
|
|
59
|
-
break;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if (error?.code !== 'EEXIST') throw error;
|
|
62
|
-
try {
|
|
63
|
-
const stat = lstatSync(lockPath);
|
|
64
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
65
|
-
throw new Error(`Refusing unsafe app development roots lock: ${lockPath}`);
|
|
66
|
-
}
|
|
67
|
-
const existingOwner = readLockOwner(lockPath);
|
|
68
|
-
const pid = Number.parseInt(String(existingOwner || '').split('.')[0] || '', 10);
|
|
69
|
-
if (Number.isInteger(pid) && pid > 0) {
|
|
70
|
-
try {
|
|
71
|
-
process.kill(pid, 0);
|
|
72
|
-
} catch (ownerError) {
|
|
73
|
-
if (ownerError?.code === 'ESRCH') {
|
|
74
|
-
rmSync(lockPath, { recursive: true, force: true });
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} catch (statError) {
|
|
80
|
-
if (statError?.code === 'ENOENT') continue;
|
|
81
|
-
throw statError;
|
|
82
|
-
}
|
|
83
|
-
if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {
|
|
84
|
-
throw new Error(`Timed out waiting for app development roots lock: ${filePath}`);
|
|
85
|
-
}
|
|
86
|
-
Atomics.wait(lockWait, 0, 0, 10);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
return callback();
|
|
91
|
-
} finally {
|
|
92
|
-
if (readLockOwner(lockPath) === owner) {
|
|
93
|
-
rmSync(lockPath, { recursive: true, force: true });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function canonicalExistingDirectory(inputPath) {
|
|
99
|
-
const absolute = resolve(String(inputPath || ''));
|
|
100
|
-
let stat;
|
|
101
|
-
try {
|
|
102
|
-
stat = statSync(absolute);
|
|
103
|
-
} catch {
|
|
104
|
-
throw usageError(`App development root does not exist: ${absolute}`);
|
|
105
|
-
}
|
|
106
|
-
if (!stat.isDirectory()) {
|
|
107
|
-
throw usageError(`App development root is not a directory: ${absolute}`);
|
|
108
|
-
}
|
|
109
|
-
return realpathSync(absolute);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function normalizeRegistry(raw) {
|
|
113
|
-
const roots = Array.isArray(raw?.roots) ? raw.roots : [];
|
|
114
|
-
const seen = new Set();
|
|
115
|
-
return roots
|
|
116
|
-
.map((entry) => typeof entry === 'string' ? { path: entry } : entry)
|
|
117
|
-
.filter((entry) => entry && typeof entry.path === 'string' && entry.path.trim())
|
|
118
|
-
.map((entry) => ({
|
|
119
|
-
path: resolve(entry.path),
|
|
120
|
-
registeredAt: typeof entry.registeredAt === 'string'
|
|
121
|
-
? entry.registeredAt
|
|
122
|
-
: new Date(0).toISOString(),
|
|
123
|
-
}))
|
|
124
|
-
.filter((entry) => {
|
|
125
|
-
if (seen.has(entry.path)) return false;
|
|
126
|
-
seen.add(entry.path);
|
|
127
|
-
return true;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function readRaw(filePath) {
|
|
132
|
-
if (!existsSync(filePath)) return { version: APP_DEV_ROOTS_VERSION, roots: [] };
|
|
133
|
-
try {
|
|
134
|
-
return {
|
|
135
|
-
version: APP_DEV_ROOTS_VERSION,
|
|
136
|
-
roots: normalizeRegistry(JSON.parse(readFileSync(filePath, 'utf8'))),
|
|
137
|
-
};
|
|
138
|
-
} catch {
|
|
139
|
-
return { version: APP_DEV_ROOTS_VERSION, roots: [] };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function writeRaw(registry, filePath) {
|
|
144
|
-
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
145
|
-
const normalized = {
|
|
146
|
-
version: APP_DEV_ROOTS_VERSION,
|
|
147
|
-
roots: normalizeRegistry(registry),
|
|
148
|
-
};
|
|
149
|
-
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
150
|
-
writeFileSync(temporary, JSON.stringify(normalized, null, 2), { mode: 0o600 });
|
|
151
|
-
renameSync(temporary, filePath);
|
|
152
|
-
return normalized;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function migrateLegacyProjectsUnlocked(registry, options) {
|
|
156
|
-
const path = legacyProjectsFile(options.legacySessionsFilePath);
|
|
157
|
-
if (!existsSync(path)) return registry;
|
|
158
|
-
let projects = [];
|
|
159
|
-
try {
|
|
160
|
-
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
161
|
-
projects = Array.isArray(raw?.projects) ? raw.projects : [];
|
|
162
|
-
} catch {
|
|
163
|
-
return registry;
|
|
164
|
-
}
|
|
165
|
-
const byPath = new Map(registry.roots.map((entry) => [entry.path, entry]));
|
|
166
|
-
for (const project of projects) {
|
|
167
|
-
if (typeof project?.projectDir !== 'string' || !project.projectDir.trim()) continue;
|
|
168
|
-
try {
|
|
169
|
-
const path = canonicalExistingDirectory(project.projectDir);
|
|
170
|
-
if (path === realpathIfExists(DEFAULT_APP_DEV_ROOT)) continue;
|
|
171
|
-
if (!byPath.has(path)) {
|
|
172
|
-
byPath.set(path, {
|
|
173
|
-
path,
|
|
174
|
-
registeredAt: typeof project.lastMountedAt === 'string'
|
|
175
|
-
? project.lastMountedAt
|
|
176
|
-
: new Date().toISOString(),
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
} catch {
|
|
180
|
-
// Deleted or inaccessible legacy projects are intentionally not migrated.
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
rmSync(path, { force: true });
|
|
184
|
-
return { version: APP_DEV_ROOTS_VERSION, roots: [...byPath.values()] };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function realpathIfExists(path) {
|
|
188
|
-
try {
|
|
189
|
-
return realpathSync(path);
|
|
190
|
-
} catch {
|
|
191
|
-
return resolve(path);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export function readAppDevRoots(options = {}) {
|
|
196
|
-
const filePath = rootsFile(options.filePath);
|
|
197
|
-
let registry = readRaw(filePath);
|
|
198
|
-
if (options.migrateLegacy !== false) {
|
|
199
|
-
registry = withLock(filePath, () => {
|
|
200
|
-
const before = readRaw(filePath);
|
|
201
|
-
const after = migrateLegacyProjectsUnlocked(before, options);
|
|
202
|
-
const changed = JSON.stringify(before.roots) !== JSON.stringify(after.roots);
|
|
203
|
-
return changed ? writeRaw(after, filePath) : before;
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
const implicit = realpathIfExists(options.defaultRoot || DEFAULT_APP_DEV_ROOT);
|
|
207
|
-
const roots = [
|
|
208
|
-
{ path: implicit, registeredAt: null, implicit: true },
|
|
209
|
-
...registry.roots
|
|
210
|
-
.filter((entry) => entry.path !== implicit)
|
|
211
|
-
.map((entry) => ({ ...entry, implicit: false })),
|
|
212
|
-
];
|
|
213
|
-
return { version: APP_DEV_ROOTS_VERSION, roots };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function registerAppDevRoot(inputPath, options = {}) {
|
|
217
|
-
const path = canonicalExistingDirectory(inputPath);
|
|
218
|
-
const implicit = realpathIfExists(options.defaultRoot || DEFAULT_APP_DEV_ROOT);
|
|
219
|
-
if (path === implicit) return readAppDevRoots(options);
|
|
220
|
-
const filePath = rootsFile(options.filePath);
|
|
221
|
-
withLock(filePath, () => {
|
|
222
|
-
const registry = readRaw(filePath);
|
|
223
|
-
if (!registry.roots.some((entry) => entry.path === path)) {
|
|
224
|
-
registry.roots.push({ path, registeredAt: new Date().toISOString() });
|
|
225
|
-
writeRaw(registry, filePath);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
return readAppDevRoots({ ...options, migrateLegacy: false });
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function removeAppDevRoot(inputPath, options = {}) {
|
|
232
|
-
const path = realpathIfExists(resolve(String(inputPath || '')));
|
|
233
|
-
const implicit = realpathIfExists(options.defaultRoot || DEFAULT_APP_DEV_ROOT);
|
|
234
|
-
if (path === implicit) {
|
|
235
|
-
throw usageError(`The default app development root cannot be removed: ${implicit}`);
|
|
236
|
-
}
|
|
237
|
-
const filePath = rootsFile(options.filePath);
|
|
238
|
-
let removed = false;
|
|
239
|
-
withLock(filePath, () => {
|
|
240
|
-
const registry = readRaw(filePath);
|
|
241
|
-
const next = registry.roots.filter((entry) => entry.path !== path);
|
|
242
|
-
removed = next.length !== registry.roots.length;
|
|
243
|
-
if (removed) writeRaw({ ...registry, roots: next }, filePath);
|
|
244
|
-
});
|
|
245
|
-
return { removed, path, registry: readAppDevRoots({ ...options, migrateLegacy: false }) };
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function hasConfig(path) {
|
|
249
|
-
return CONFIG_FILENAMES.some((name) => existsSync(join(path, name)));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function childAppDirs(parent) {
|
|
253
|
-
if (!existsSync(parent)) return [];
|
|
254
|
-
let entries = [];
|
|
255
|
-
try {
|
|
256
|
-
entries = readdirSync(parent, { withFileTypes: true });
|
|
257
|
-
} catch {
|
|
258
|
-
return [];
|
|
259
|
-
}
|
|
260
|
-
return entries
|
|
261
|
-
.filter((entry) => entry.isDirectory())
|
|
262
|
-
.filter((entry) => !entry.name.startsWith('.') && !entry.name.startsWith('_'))
|
|
263
|
-
.filter((entry) => entry.name !== 'node_modules')
|
|
264
|
-
.map((entry) => join(parent, entry.name))
|
|
265
|
-
.filter(hasConfig);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export function discoverAppProjectsInRoot(inputRoot) {
|
|
269
|
-
const root = realpathIfExists(resolve(inputRoot));
|
|
270
|
-
const candidates = [];
|
|
271
|
-
if (hasConfig(root)) candidates.push(root);
|
|
272
|
-
candidates.push(...childAppDirs(root));
|
|
273
|
-
candidates.push(...childAppDirs(join(root, 'apps')));
|
|
274
|
-
return [...new Set(candidates.map(realpathIfExists))].sort();
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function discoverRegisteredAppProjects(options = {}) {
|
|
278
|
-
const roots = readAppDevRoots(options).roots;
|
|
279
|
-
return [...new Set(roots.flatMap((entry) => discoverAppProjectsInRoot(entry.path)))].sort();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export function getAppDevRootsFile(filePath) {
|
|
283
|
-
return rootsFile(filePath);
|
|
284
|
-
}
|