@notis_ai/cli 0.2.13 → 0.2.15
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 +125 -21
- package/dist/agent-hooks/notis-agent-hook.mjs +19750 -0
- package/{skills → dist/base-skills}/notis-apps/SKILL.md +93 -58
- package/{skills → dist/base-skills}/notis-cli/SKILL.md +9 -9
- package/dist/base-skills/notis-query/SKILL.md +705 -0
- package/dist/skill-sync/index.js +1528 -0
- package/dist/skill-sync/index.js.map +7 -0
- package/package.json +5 -2
- package/skills/notis-apps/cli.md +44 -18
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +39 -0
- package/skills/notis-onboarding/BRIEF.md +16 -0
- package/src/agent-hook-entry.js +5 -0
- package/src/cli.js +23 -14
- package/src/command-specs/agents.js +392 -0
- package/src/command-specs/apps.js +1090 -418
- package/src/command-specs/auth.js +63 -10
- package/src/command-specs/handover.js +374 -0
- package/src/command-specs/index.js +9 -0
- package/src/command-specs/onboarding.js +69 -5
- package/src/command-specs/skills.js +56 -0
- package/src/command-specs/tools.js +6 -0
- package/src/runtime/agent-memory-state.js +126 -0
- package/src/runtime/agent-setup.js +383 -0
- package/src/runtime/app-dev-consumers.js +154 -0
- package/src/runtime/app-dev-host-lock.js +80 -0
- package/src/runtime/app-dev-roots.js +284 -0
- package/src/runtime/app-dev-server.js +229 -30
- package/src/runtime/app-dev-sessions.js +105 -145
- package/src/runtime/app-platform.js +1499 -161
- package/src/runtime/app-registry-scaffolds.js +367 -0
- package/src/runtime/base-skills.d.ts +20 -0
- package/src/runtime/base-skills.js +167 -0
- package/src/runtime/delegated-context.js +68 -0
- package/src/runtime/git.js +233 -0
- package/src/runtime/login-listener.js +15 -0
- package/src/runtime/oauth.js +1506 -141
- package/src/runtime/output.js +7 -0
- package/src/runtime/ports.js +16 -0
- package/src/runtime/profiles.js +0 -5
- package/src/runtime/skill-sync/cloud-client.ts +96 -0
- package/src/runtime/skill-sync/index.ts +644 -0
- package/src/runtime/skill-sync/local-scanner.ts +1046 -0
- package/src/runtime/skill-sync/symlink-manager.ts +383 -0
- package/src/runtime/skill-sync/sync-plan.ts +22 -0
- package/src/runtime/skill-sync/types.ts +103 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
- package/src/runtime/store-screenshot.js +6 -1
- package/src/runtime/sync-skills.d.ts +37 -0
- package/src/runtime/sync-skills.js +215 -0
- package/src/runtime/transport.js +19 -2
- package/template/.harness/index.html.tmpl +116 -47
- package/template/app/layout.tsx +5 -1
- package/template/notis.config.ts +1 -0
- package/template/packages/sdk/package.json +2 -1
- package/template/packages/sdk/src/components/DocumentEditor.tsx +13 -3
- package/template/packages/sdk/src/components/MarkdownEditor.tsx +121 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +37 -119
- package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +5 -1
- package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +1 -1
- package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +55 -0
- package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
- package/template/packages/sdk/src/config.ts +71 -0
- package/template/packages/sdk/src/documents.ts +28 -1
- package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
- package/template/packages/sdk/src/hooks/useCloudComputer.ts +97 -0
- package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +709 -0
- package/template/packages/sdk/src/hooks/useDatabaseSubscription.ts +76 -0
- package/template/packages/sdk/src/hooks/useHandover.ts +78 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +44 -482
- package/template/packages/sdk/src/hooks/useTool.ts +5 -4
- package/template/packages/sdk/src/index.ts +64 -2
- package/template/packages/sdk/src/interactions/actions.ts +46 -0
- package/template/packages/sdk/src/interactions/shortcuts.tsx +628 -0
- package/template/packages/sdk/src/interactions.ts +41 -0
- package/template/packages/sdk/src/provider.tsx +2 -1
- package/template/packages/sdk/src/runtime.ts +219 -3
- package/dist/scaffolds/notis-database/CHANGELOG.md +0 -5
- package/dist/scaffolds/notis-database/app/globals.css +0 -44
- package/dist/scaffolds/notis-database/app/layout.tsx +0 -6
- package/dist/scaffolds/notis-database/app/page.tsx +0 -1088
- package/dist/scaffolds/notis-database/components/ui/badge.tsx +0 -28
- package/dist/scaffolds/notis-database/components/ui/button.tsx +0 -53
- package/dist/scaffolds/notis-database/components/ui/card.tsx +0 -56
- package/dist/scaffolds/notis-database/components/ui/table.tsx +0 -120
- package/dist/scaffolds/notis-database/components.json +0 -20
- package/dist/scaffolds/notis-database/index.html +0 -12
- package/dist/scaffolds/notis-database/lib/types.ts +0 -132
- package/dist/scaffolds/notis-database/lib/utils.ts +0 -6
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +0 -1839
- package/dist/scaffolds/notis-database/notis.config.ts +0 -73
- package/dist/scaffolds/notis-database/package-lock.json +0 -3935
- package/dist/scaffolds/notis-database/package.json +0 -32
- package/dist/scaffolds/notis-database/packages/sdk/package.json +0 -36
- package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +0 -93
- package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +0 -60
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
- package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +0 -196
- package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +0 -229
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +0 -41
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +0 -121
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +0 -34
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +0 -64
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +0 -56
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
- package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +0 -83
- package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +0 -43
- package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +0 -220
- package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +0 -186
- package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +0 -15
- package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +0 -56
- package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +0 -15
- package/dist/scaffolds/notis-database/postcss.config.mjs +0 -8
- package/dist/scaffolds/notis-database/src/dev-main.tsx +0 -23
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +0 -561
- package/dist/scaffolds/notis-database/tailwind.config.ts +0 -59
- package/dist/scaffolds/notis-database/tsconfig.json +0 -23
- package/dist/scaffolds/notis-database/vite.config.ts +0 -22
- package/dist/scaffolds/notis-journal/CHANGELOG.md +0 -29
- package/dist/scaffolds/notis-journal/app/globals.css +0 -37
- package/dist/scaffolds/notis-journal/app/insights/page.tsx +0 -513
- package/dist/scaffolds/notis-journal/app/journal-core.tsx +0 -362
- package/dist/scaffolds/notis-journal/app/journal-ui.tsx +0 -337
- package/dist/scaffolds/notis-journal/app/layout.tsx +0 -6
- package/dist/scaffolds/notis-journal/app/page.tsx +0 -486
- package/dist/scaffolds/notis-journal/components/ui/badge.tsx +0 -28
- package/dist/scaffolds/notis-journal/components/ui/button.tsx +0 -53
- package/dist/scaffolds/notis-journal/components/ui/card.tsx +0 -56
- package/dist/scaffolds/notis-journal/components.json +0 -20
- package/dist/scaffolds/notis-journal/index.html +0 -12
- package/dist/scaffolds/notis-journal/lib/utils.ts +0 -6
- package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +0 -132
- package/dist/scaffolds/notis-journal/notis.config.ts +0 -93
- package/dist/scaffolds/notis-journal/package-lock.json +0 -4615
- package/dist/scaffolds/notis-journal/package.json +0 -34
- package/dist/scaffolds/notis-journal/packages/sdk/package.json +0 -36
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +0 -93
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +0 -60
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
- package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +0 -196
- package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +0 -229
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +0 -41
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +0 -121
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +0 -34
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +0 -64
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +0 -56
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
- package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +0 -83
- package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +0 -43
- package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +0 -220
- package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +0 -186
- package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +0 -15
- package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +0 -56
- package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +0 -15
- package/dist/scaffolds/notis-journal/postcss.config.mjs +0 -8
- package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +0 -120
- package/dist/scaffolds/notis-journal/src/dev-main.tsx +0 -58
- package/dist/scaffolds/notis-journal/src/mock-runtime.ts +0 -197
- package/dist/scaffolds/notis-journal/tailwind.config.ts +0 -58
- package/dist/scaffolds/notis-journal/tsconfig.json +0 -23
- package/dist/scaffolds/notis-journal/vite.config.ts +0 -10
- package/dist/scaffolds/notis-notes/CHANGELOG.md +0 -5
- package/dist/scaffolds/notis-notes/app/globals.css +0 -3
- package/dist/scaffolds/notis-notes/app/layout.tsx +0 -6
- package/dist/scaffolds/notis-notes/app/page.tsx +0 -1943
- package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +0 -596
- package/dist/scaffolds/notis-notes/components/ui/badge.tsx +0 -28
- package/dist/scaffolds/notis-notes/components/ui/button.tsx +0 -53
- package/dist/scaffolds/notis-notes/components/ui/card.tsx +0 -56
- package/dist/scaffolds/notis-notes/components.json +0 -20
- package/dist/scaffolds/notis-notes/lib/utils.ts +0 -6
- package/dist/scaffolds/notis-notes/lib/visible-properties.ts +0 -144
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +0 -752
- package/dist/scaffolds/notis-notes/notis.config.ts +0 -80
- package/dist/scaffolds/notis-notes/package-lock.json +0 -4636
- package/dist/scaffolds/notis-notes/package.json +0 -35
- package/dist/scaffolds/notis-notes/packages/sdk/package.json +0 -36
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +0 -93
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +0 -60
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
- package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +0 -196
- package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +0 -229
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +0 -41
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +0 -121
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +0 -34
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +0 -64
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +0 -56
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
- package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +0 -83
- package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +0 -43
- package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +0 -220
- package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +0 -186
- package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +0 -15
- package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +0 -56
- package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +0 -15
- package/dist/scaffolds/notis-notes/postcss.config.mjs +0 -8
- package/dist/scaffolds/notis-notes/tailwind.config.ts +0 -58
- package/dist/scaffolds/notis-notes/tsconfig.json +0 -23
- package/dist/scaffolds/notis-notes/vite.config.ts +0 -10
- package/dist/scaffolds/notis-random/CHANGELOG.md +0 -15
- package/dist/scaffolds/notis-random/README.md +0 -33
- package/dist/scaffolds/notis-random/app/globals.css +0 -11
- package/dist/scaffolds/notis-random/app/history/page.tsx +0 -67
- package/dist/scaffolds/notis-random/app/layout.tsx +0 -7
- package/dist/scaffolds/notis-random/app/page.tsx +0 -289
- package/dist/scaffolds/notis-random/components/ui/button.tsx +0 -50
- package/dist/scaffolds/notis-random/components/ui/card.tsx +0 -16
- package/dist/scaffolds/notis-random/components/ui/input.tsx +0 -23
- package/dist/scaffolds/notis-random/components.json +0 -20
- package/dist/scaffolds/notis-random/index.html +0 -12
- package/dist/scaffolds/notis-random/lib/notis-tools.ts +0 -128
- package/dist/scaffolds/notis-random/lib/rng.ts +0 -202
- package/dist/scaffolds/notis-random/lib/roll-record.ts +0 -189
- package/dist/scaffolds/notis-random/lib/utils.ts +0 -25
- package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +0 -753
- package/dist/scaffolds/notis-random/notis.config.ts +0 -86
- package/dist/scaffolds/notis-random/package-lock.json +0 -4513
- package/dist/scaffolds/notis-random/package.json +0 -36
- package/dist/scaffolds/notis-random/packages/sdk/package.json +0 -36
- package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +0 -93
- package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +0 -60
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
- package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +0 -196
- package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +0 -229
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +0 -41
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +0 -121
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +0 -34
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +0 -64
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +0 -56
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
- package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +0 -83
- package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +0 -43
- package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +0 -220
- package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +0 -186
- package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +0 -15
- package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +0 -56
- package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +0 -15
- package/dist/scaffolds/notis-random/postcss.config.mjs +0 -6
- package/dist/scaffolds/notis-random/src/dev-main.tsx +0 -70
- package/dist/scaffolds/notis-random/src/mock-runtime.ts +0 -129
- package/dist/scaffolds/notis-random/tailwind.config.ts +0 -50
- package/dist/scaffolds/notis-random/tsconfig.json +0 -23
- package/dist/scaffolds/notis-random/vite.config.ts +0 -11
- package/dist/scaffolds.json +0 -49
- package/template/metadata/screenshot-1.png +0 -0
- package/template/metadata/screenshot-2.png +0 -0
- package/template/metadata/screenshot-3.png +0 -0
|
@@ -1,30 +1,77 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { dirname, join
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
4
|
import { CONFIG_DIR } from './profiles.js';
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_APP_DEV_SESSIONS_FILE = join(CONFIG_DIR, 'app-dev-sessions.json');
|
|
7
7
|
export const APP_DEV_SESSIONS_FILE = DEFAULT_APP_DEV_SESSIONS_FILE;
|
|
8
8
|
export const APP_DEV_SESSIONS_VERSION = 1;
|
|
9
|
-
|
|
9
|
+
const REGISTRY_LOCK_TIMEOUT_MS = 5_000;
|
|
10
|
+
const registryLockWait = new Int32Array(new SharedArrayBuffer(4));
|
|
11
|
+
|
|
12
|
+
function readRegistryLockOwner(lockPath) {
|
|
13
|
+
try {
|
|
14
|
+
return readFileSync(join(lockPath, 'owner'), 'utf8').trim();
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function withRegistryLock(filePath, callback) {
|
|
21
|
+
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
22
|
+
const lockPath = `${filePath}.lock`;
|
|
23
|
+
const ownerId = `${process.pid}.${randomUUID()}`;
|
|
24
|
+
const startedAt = Date.now();
|
|
25
|
+
while (true) {
|
|
26
|
+
try {
|
|
27
|
+
mkdirSync(lockPath, { mode: 0o700 });
|
|
28
|
+
try {
|
|
29
|
+
writeFileSync(join(lockPath, 'owner'), ownerId, { mode: 0o600 });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
37
|
+
try {
|
|
38
|
+
const lockStat = lstatSync(lockPath);
|
|
39
|
+
if (lockStat.isSymbolicLink() || !lockStat.isDirectory()) {
|
|
40
|
+
throw new Error(`Refusing unsafe app development registry lock: ${lockPath}`);
|
|
41
|
+
}
|
|
42
|
+
} catch (statError) {
|
|
43
|
+
if (statError?.code === 'ENOENT') continue;
|
|
44
|
+
throw statError;
|
|
45
|
+
}
|
|
46
|
+
if (Date.now() - startedAt >= REGISTRY_LOCK_TIMEOUT_MS) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Timed out waiting for app development registry lock: ${filePath}. `
|
|
49
|
+
+ `If no app development process is running, remove the orphaned lock: ${lockPath}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
Atomics.wait(registryLockWait, 0, 0, 10);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return callback();
|
|
57
|
+
} finally {
|
|
58
|
+
try {
|
|
59
|
+
if (readRegistryLockOwner(lockPath) === ownerId) {
|
|
60
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Never remove or disrupt a lock that no longer belongs to this writer.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
10
67
|
|
|
11
68
|
export function getAppDevSessionsFile(filePath) {
|
|
12
69
|
if (filePath) {
|
|
13
70
|
return filePath;
|
|
14
71
|
}
|
|
15
|
-
const envPath = process.env.NOTIS_APP_DEV_SESSIONS_FILE;
|
|
16
|
-
if (typeof envPath === 'string' && envPath.trim()) {
|
|
17
|
-
return envPath.trim();
|
|
18
|
-
}
|
|
19
72
|
return DEFAULT_APP_DEV_SESSIONS_FILE;
|
|
20
73
|
}
|
|
21
74
|
|
|
22
|
-
export function getAppDevSessionMountAcksFile(sessionsFilePath) {
|
|
23
|
-
const resolvedSessionsFile = getAppDevSessionsFile(sessionsFilePath);
|
|
24
|
-
const parsed = parsePath(resolvedSessionsFile);
|
|
25
|
-
return join(parsed.dir, `${parsed.name}-mount-acks${parsed.ext || '.json'}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
75
|
function normalizeSessions(raw) {
|
|
29
76
|
if (!raw || typeof raw !== 'object' || !Array.isArray(raw.sessions)) {
|
|
30
77
|
return [];
|
|
@@ -62,7 +109,7 @@ export function readAppDevSessions(filePath) {
|
|
|
62
109
|
}
|
|
63
110
|
}
|
|
64
111
|
|
|
65
|
-
|
|
112
|
+
function writeAppDevSessionsUnlocked(registry, filePath) {
|
|
66
113
|
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
67
114
|
mkdirSync(dirname(resolvedFilePath), { recursive: true, mode: 0o700 });
|
|
68
115
|
const normalized = {
|
|
@@ -75,151 +122,64 @@ export function writeAppDevSessions(registry, filePath) {
|
|
|
75
122
|
return normalized;
|
|
76
123
|
}
|
|
77
124
|
|
|
125
|
+
export function writeAppDevSessions(registry, filePath) {
|
|
126
|
+
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
127
|
+
return withRegistryLock(resolvedFilePath, () => writeAppDevSessionsUnlocked(registry, resolvedFilePath));
|
|
128
|
+
}
|
|
129
|
+
|
|
78
130
|
export function upsertAppDevSessions(sessions, filePath) {
|
|
79
131
|
const nextSessions = Array.isArray(sessions) ? sessions : [sessions];
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
132
|
+
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
133
|
+
return withRegistryLock(resolvedFilePath, () => {
|
|
134
|
+
const registry = readAppDevSessions(resolvedFilePath);
|
|
135
|
+
const keys = new Set(nextSessions.map((session) => `${session.sessionId}:${session.appId}`));
|
|
136
|
+
registry.sessions = [
|
|
137
|
+
...registry.sessions.filter((session) => !keys.has(`${session.sessionId}:${session.appId}`)),
|
|
138
|
+
...nextSessions,
|
|
139
|
+
];
|
|
140
|
+
return writeAppDevSessionsUnlocked(registry, resolvedFilePath);
|
|
141
|
+
});
|
|
87
142
|
}
|
|
88
143
|
|
|
89
144
|
export function heartbeatAppDevSession(sessionId, lastHeartbeatAt, filePath) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: session,
|
|
95
|
-
|
|
96
|
-
|
|
145
|
+
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
146
|
+
return withRegistryLock(resolvedFilePath, () => {
|
|
147
|
+
const registry = readAppDevSessions(resolvedFilePath);
|
|
148
|
+
registry.sessions = registry.sessions.map((session) =>
|
|
149
|
+
session.sessionId === sessionId ? { ...session, lastHeartbeatAt } : session,
|
|
150
|
+
);
|
|
151
|
+
return writeAppDevSessionsUnlocked(registry, resolvedFilePath);
|
|
152
|
+
});
|
|
97
153
|
}
|
|
98
154
|
|
|
99
|
-
export function linkAppDevSessionTarget({ appId, devSlug, targetAppId, lastHeartbeatAt = new Date().toISOString() }, filePath) {
|
|
155
|
+
export function linkAppDevSessionTarget({ sessionId, appId, devSlug, targetAppId, lastHeartbeatAt = new Date().toISOString() }, filePath) {
|
|
100
156
|
const target = typeof targetAppId === 'string' ? targetAppId.trim() : '';
|
|
101
157
|
if (!target) {
|
|
102
158
|
return readAppDevSessions(filePath);
|
|
103
159
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
targetAppId: target,
|
|
114
|
-
|
|
115
|
-
};
|
|
160
|
+
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
161
|
+
const result = withRegistryLock(resolvedFilePath, () => {
|
|
162
|
+
const registry = readAppDevSessions(resolvedFilePath);
|
|
163
|
+
registry.sessions = registry.sessions.map((session) => {
|
|
164
|
+
const hasSelector = Boolean(appId || devSlug);
|
|
165
|
+
const matchesSession = !sessionId || session.sessionId === sessionId;
|
|
166
|
+
const matchesApp = !appId || session.appId === appId;
|
|
167
|
+
const matchesSlug = !devSlug || session.devSlug === devSlug;
|
|
168
|
+
if ((!hasSelector && !sessionId) || !matchesSession || !matchesApp || !matchesSlug) return session;
|
|
169
|
+
const linked = { ...session, targetAppId: target, lastHeartbeatAt };
|
|
170
|
+
return linked;
|
|
171
|
+
});
|
|
172
|
+
return writeAppDevSessionsUnlocked(registry, resolvedFilePath);
|
|
116
173
|
});
|
|
117
|
-
return writeAppDevSessions(registry, filePath);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function removeAppDevSession(sessionId, filePath) {
|
|
121
|
-
const registry = readAppDevSessions(filePath);
|
|
122
|
-
registry.sessions = registry.sessions.filter((session) => session.sessionId !== sessionId);
|
|
123
|
-
const result = writeAppDevSessions(registry, filePath);
|
|
124
|
-
removeAppDevSessionMountAcknowledgements(sessionId, filePath);
|
|
125
174
|
return result;
|
|
126
175
|
}
|
|
127
176
|
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
typeof acknowledgement === 'object' &&
|
|
135
|
-
typeof acknowledgement.sessionId === 'string' &&
|
|
136
|
-
typeof acknowledgement.appId === 'string' &&
|
|
137
|
-
typeof acknowledgement.devSlug === 'string' &&
|
|
138
|
-
typeof acknowledgement.mountNonce === 'string' &&
|
|
139
|
-
typeof acknowledgement.acknowledgedAt === 'string' &&
|
|
140
|
-
['listed', 'rendered'].includes(acknowledgement.stage),
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function readAppDevSessionMountAcknowledgements(sessionsFilePath) {
|
|
145
|
-
const filePath = getAppDevSessionMountAcksFile(sessionsFilePath);
|
|
146
|
-
if (!existsSync(filePath)) {
|
|
147
|
-
return { version: APP_DEV_SESSION_MOUNT_ACKS_VERSION, acknowledgements: [] };
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
const raw = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
151
|
-
return {
|
|
152
|
-
version: APP_DEV_SESSION_MOUNT_ACKS_VERSION,
|
|
153
|
-
acknowledgements: normalizeMountAcknowledgements(raw),
|
|
154
|
-
};
|
|
155
|
-
} catch {
|
|
156
|
-
return { version: APP_DEV_SESSION_MOUNT_ACKS_VERSION, acknowledgements: [] };
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function writeAppDevSessionMountAcknowledgements(registry, sessionsFilePath) {
|
|
161
|
-
const filePath = getAppDevSessionMountAcksFile(sessionsFilePath);
|
|
162
|
-
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
163
|
-
const normalized = {
|
|
164
|
-
version: APP_DEV_SESSION_MOUNT_ACKS_VERSION,
|
|
165
|
-
acknowledgements: normalizeMountAcknowledgements(registry),
|
|
166
|
-
};
|
|
167
|
-
const tempPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
168
|
-
writeFileSync(tempPath, JSON.stringify(normalized, null, 2), { mode: 0o600 });
|
|
169
|
-
renameSync(tempPath, filePath);
|
|
170
|
-
return normalized;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function removeAppDevSessionMountAcknowledgements(sessionId, sessionsFilePath) {
|
|
174
|
-
const filePath = getAppDevSessionMountAcksFile(sessionsFilePath);
|
|
175
|
-
if (!existsSync(filePath)) {
|
|
176
|
-
return { version: APP_DEV_SESSION_MOUNT_ACKS_VERSION, acknowledgements: [] };
|
|
177
|
-
}
|
|
178
|
-
const registry = readAppDevSessionMountAcknowledgements(sessionsFilePath);
|
|
179
|
-
registry.acknowledgements = registry.acknowledgements.filter(
|
|
180
|
-
(acknowledgement) => acknowledgement.sessionId !== sessionId,
|
|
181
|
-
);
|
|
182
|
-
return writeAppDevSessionMountAcknowledgements(registry, sessionsFilePath);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function mountAcknowledgementKey(value, fallbackStage = 'listed') {
|
|
186
|
-
return `${value.sessionId}:${value.appId}:${value.devSlug}:${value.mountNonce}:${value.stage || fallbackStage}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export async function waitForAppDevSessionMountAcknowledgements(expectedSessions, options = {}) {
|
|
190
|
-
const expected = Array.isArray(expectedSessions) ? expectedSessions : [expectedSessions];
|
|
191
|
-
const stage = options.stage === 'rendered' ? 'rendered' : 'listed';
|
|
192
|
-
const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(0, options.timeoutMs) : 15_000;
|
|
193
|
-
const pollIntervalMs = Number.isFinite(options.pollIntervalMs)
|
|
194
|
-
? Math.max(1, options.pollIntervalMs)
|
|
195
|
-
: 100;
|
|
196
|
-
const now = typeof options.now === 'function' ? options.now : Date.now;
|
|
197
|
-
const sleep = typeof options.sleep === 'function'
|
|
198
|
-
? options.sleep
|
|
199
|
-
: (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
200
|
-
const startedAt = now();
|
|
201
|
-
|
|
202
|
-
while (true) {
|
|
203
|
-
const registry = readAppDevSessionMountAcknowledgements(options.sessionsFilePath);
|
|
204
|
-
const acknowledgedKeys = new Set(registry.acknowledgements.map(mountAcknowledgementKey));
|
|
205
|
-
const missing = expected.filter(
|
|
206
|
-
(session) => !acknowledgedKeys.has(mountAcknowledgementKey(session, stage)),
|
|
207
|
-
);
|
|
208
|
-
if (missing.length === 0) {
|
|
209
|
-
return { mounted: true, missing: [], acknowledgements: registry.acknowledgements };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const elapsedMs = now() - startedAt;
|
|
213
|
-
if (elapsedMs >= timeoutMs) {
|
|
214
|
-
return { mounted: false, missing, acknowledgements: registry.acknowledgements };
|
|
215
|
-
}
|
|
216
|
-
await sleep(Math.min(pollIntervalMs, timeoutMs - elapsedMs));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export function waitForAppDevSessionRenderAcknowledgements(expectedSessions, options = {}) {
|
|
221
|
-
return waitForAppDevSessionMountAcknowledgements(expectedSessions, {
|
|
222
|
-
...options,
|
|
223
|
-
stage: 'rendered',
|
|
177
|
+
export function removeAppDevSession(sessionId, filePath) {
|
|
178
|
+
const resolvedFilePath = getAppDevSessionsFile(filePath);
|
|
179
|
+
const result = withRegistryLock(resolvedFilePath, () => {
|
|
180
|
+
const registry = readAppDevSessions(resolvedFilePath);
|
|
181
|
+
registry.sessions = registry.sessions.filter((session) => session.sessionId !== sessionId);
|
|
182
|
+
return writeAppDevSessionsUnlocked(registry, resolvedFilePath);
|
|
224
183
|
});
|
|
184
|
+
return result;
|
|
225
185
|
}
|