@notis_ai/cli 0.2.0-beta.191.1 → 0.2.0-beta.197.1

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.
Files changed (107) hide show
  1. package/README.md +485 -86
  2. package/config/portal-route-roots.json +47 -0
  3. package/dist/agent-hooks/notis-agent-hook.mjs +32663 -6357
  4. package/dist/base-skills/notis-apps/SKILL.md +88 -67
  5. package/dist/base-skills/notis-apps/references/architecture.md +114 -167
  6. package/dist/base-skills/notis-apps/references/design.md +26 -107
  7. package/dist/base-skills/notis-apps/references/memory.md +58 -0
  8. package/dist/base-skills/notis-apps/references/reading.md +52 -74
  9. package/dist/base-skills/notis-apps/references/release.md +104 -142
  10. package/dist/base-skills/notis-apps/references/sdk.md +113 -60
  11. package/dist/base-skills/notis-apps/references/troubleshooting.md +22 -21
  12. package/dist/base-skills/notis-apps/references/views.md +142 -0
  13. package/dist/base-skills/notis-cli/SKILL.md +4 -4
  14. package/dist/base-skills/notis-cli/references/app-delivery.md +12 -11
  15. package/dist/base-skills/notis-cli/references/native-databases.md +6 -6
  16. package/dist/base-skills/notis-cli/references/tool-examples.md +4 -4
  17. package/dist/base-skills/notis-query/SKILL.md +34 -60
  18. package/dist/base-skills/notis-query/references/database-discovery.md +59 -69
  19. package/dist/base-skills/notis-query/references/documents.md +63 -50
  20. package/dist/base-skills/notis-query/references/query.md +57 -524
  21. package/dist/sdk/src/bundleModule.ts +79 -0
  22. package/dist/sdk/src/components/DocumentEditor.tsx +6 -3
  23. package/dist/sdk/src/components/DocumentPage.tsx +141 -0
  24. package/dist/sdk/src/components/RecordComponents.tsx +33 -0
  25. package/dist/sdk/src/components/RenderChartData.tsx +19 -0
  26. package/dist/sdk/src/config.ts +4 -26
  27. package/dist/sdk/src/documents.ts +2 -0
  28. package/dist/sdk/src/hooks/useAction.ts +28 -0
  29. package/dist/sdk/src/hooks/useCloudComputer.ts +35 -4
  30. package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
  31. package/dist/sdk/src/hooks/useHandover.ts +5 -4
  32. package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
  33. package/dist/sdk/src/hooks/useNativeDocuments.ts +83 -0
  34. package/dist/sdk/src/hooks/useNotis.ts +3 -0
  35. package/dist/sdk/src/hooks/useNotisNavigation.ts +26 -11
  36. package/dist/sdk/src/hooks/useQuery.ts +7 -4
  37. package/dist/sdk/src/hooks/useViewParams.ts +121 -0
  38. package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
  39. package/dist/sdk/src/index.ts +36 -0
  40. package/dist/sdk/src/interactions/shortcuts.tsx +17 -3
  41. package/dist/sdk/src/nativeData.ts +39 -0
  42. package/dist/sdk/src/presentation.tsx +82 -0
  43. package/dist/sdk/src/provider.tsx +9 -4
  44. package/dist/sdk/src/queryCache.ts +5 -3
  45. package/dist/sdk/src/queryErrors.ts +51 -0
  46. package/dist/sdk/src/runtime.ts +146 -8
  47. package/dist/sdk/src/space.ts +425 -0
  48. package/dist/sdk/src/styles.css +96 -72
  49. package/dist/sdk/src/vite.ts +11 -1
  50. package/dist/skill-sync/index.js +458 -68
  51. package/dist/skill-sync/index.js.map +4 -4
  52. package/dist/skill-sync-worker.mjs +451 -77
  53. package/dist/space-harness/host.js +66 -0
  54. package/dist/view-renderer/dist/host.css +1 -0
  55. package/dist/view-renderer/dist/host.js +1042 -0
  56. package/dist/view-renderer/dist/theme.css +73 -0
  57. package/dist/view-renderer/src/dom.js +180 -0
  58. package/dist/view-renderer/src/errors.js +8 -0
  59. package/dist/view-renderer/src/host.tsx +173 -0
  60. package/dist/view-renderer/src/index.js +4 -0
  61. package/dist/view-renderer/src/pool.js +76 -0
  62. package/dist/view-renderer/src/remote-broker.js +59 -0
  63. package/dist/view-renderer/src/renderer.js +201 -0
  64. package/package.json +11 -5
  65. package/skills/notis-apps/cli.md +270 -163
  66. package/skills/notis-cli/AGENT_INSTRUCTIONS.md +3 -3
  67. package/skills/notis-onboarding/BRIEF.md +7 -3
  68. package/src/command-specs/apps.js +1 -8
  69. package/src/command-specs/files.js +29 -0
  70. package/src/command-specs/handover.js +45 -16
  71. package/src/command-specs/helpers.js +1 -0
  72. package/src/command-specs/index.js +10 -4
  73. package/src/command-specs/skills.js +229 -0
  74. package/src/command-specs/spaces.js +572 -0
  75. package/src/command-specs/views.js +69 -0
  76. package/src/runtime/agent-browser.js +30 -7
  77. package/src/runtime/app-boundary-validator.js +7 -4
  78. package/src/runtime/app-data-literals.js +82 -0
  79. package/src/runtime/app-platform.js +321 -253
  80. package/src/runtime/confined-space-build.js +74 -0
  81. package/src/runtime/confined-space-verify.js +49 -0
  82. package/src/runtime/native-record-files.js +47 -0
  83. package/src/runtime/retired-sdk-files.js +20 -0
  84. package/src/runtime/skill-sync/cloud-client.ts +26 -4
  85. package/src/runtime/skill-sync/index.ts +154 -35
  86. package/src/runtime/skill-sync/local-scanner.ts +125 -17
  87. package/src/runtime/skill-sync/native-identity.ts +102 -0
  88. package/src/runtime/skill-sync/native-plan.ts +59 -0
  89. package/src/runtime/skill-sync/sync-plan.ts +16 -9
  90. package/src/runtime/skill-sync/types.ts +32 -6
  91. package/src/runtime/source-root.js +32 -0
  92. package/src/runtime/space-collection-scaffold.js +66 -0
  93. package/src/runtime/space-database-structures.js +229 -0
  94. package/src/runtime/space-fixture-broker.js +50 -0
  95. package/src/runtime/space-lock.js +337 -0
  96. package/src/runtime/space-platform.js +105 -0
  97. package/src/runtime/space-source.js +281 -0
  98. package/src/runtime/space-test-server.js +327 -0
  99. package/src/runtime/space-verification.js +89 -0
  100. package/src/runtime/space-verify-browser-worker.js +51 -0
  101. package/src/runtime/space-view-manifest.js +254 -0
  102. package/src/runtime/sync-skills.js +1 -1
  103. package/src/runtime/transport.js +8 -1
  104. package/src/runtime/view-render.js +49 -0
  105. package/src/space-harness-host.ts +103 -0
  106. package/template/app/globals.css +1 -0
  107. package/src/command-specs/reports.js +0 -97
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Pinned first build of one Space for a confined local effect
3
+ * (server/lib/confined_effects/space_build.py).
4
+ *
5
+ * node confined-space-build.js <build-request.json>
6
+ *
7
+ * The backend starts this file from a hashed, read-only toolchain under the macOS
8
+ * sandbox (no network). It reuses the canonical selected-source freeze, manifest,
9
+ * V4 checks and build receipt (buildSpaceArtifact) and replaces only the build
10
+ * runner: the canonical Vite command runs with this same node executable and a
11
+ * rebuilt environment, never PATH npm, a package script or the caller's
12
+ * environment. It never refreshes the SDK or installs anything.
13
+ */
14
+ import { spawn } from 'node:child_process';
15
+ import { readFileSync, realpathSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { pathToFileURL } from 'node:url';
18
+ import { buildSpaceArtifact } from './space-platform.js';
19
+
20
+ const CANONICAL_BUILD_SCRIPTS = new Set(['vite build', 'vite build --configLoader runner']);
21
+ const OUTPUT_LIMIT = 32 * 1024;
22
+
23
+ export function pinnedBuildEnvironment(environment = process.env) {
24
+ return { PATH: [dirname(process.execPath), '/usr/bin', '/bin'].join(':'), HOME: environment.HOME || '',
25
+ TMPDIR: environment.TMPDIR || '', LANG: 'C', LC_ALL: 'C', NODE_ENV: 'production' };
26
+ }
27
+
28
+ export function pinnedViteCommand(projectDir, executable = process.execPath) {
29
+ const pkg = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'));
30
+ const script = String(pkg.scripts?.build || '').trim();
31
+ if (!CANONICAL_BUILD_SCRIPTS.has(script)) throw new Error('Only the canonical vite build script can run in a confined build.');
32
+ const vite = realpathSync(join(projectDir, 'node_modules', 'vite', 'bin', 'vite.js'));
33
+ return [executable, vite, 'build', '--configLoader', 'runner'];
34
+ }
35
+
36
+ /** buildArtifact runBuild seam: the canonical build, pinned, with bounded captured output. */
37
+ export function runPinnedVite({ projectDir }) {
38
+ const [command, ...args] = pinnedViteCommand(projectDir);
39
+ return new Promise((resolvePromise, rejectPromise) => {
40
+ const child = spawn(command, args, { cwd: projectDir, env: pinnedBuildEnvironment(), stdio: ['ignore', 'pipe', 'pipe'] });
41
+ let output = '';
42
+ for (const stream of [child.stdout, child.stderr]) {
43
+ stream.on('data', chunk => { if (output.length < OUTPUT_LIMIT) output += chunk.toString().slice(0, OUTPUT_LIMIT - output.length); });
44
+ }
45
+ child.on('error', rejectPromise);
46
+ child.on('exit', (code, signal) => {
47
+ if (code === 0) resolvePromise();
48
+ else rejectPromise(new Error(`vite build failed (${signal || code})${output.trim() ? `:\n${output.trim()}` : ''}`));
49
+ });
50
+ });
51
+ }
52
+
53
+ export async function runConfinedBuild(request) {
54
+ if (!request || typeof request.projectDir !== 'string' || typeof request.key !== 'string') {
55
+ throw new Error('The build request names one workspace and one Space key.');
56
+ }
57
+ const result = await buildSpaceArtifact(request.projectDir, request.key, { stdio: 'pipe', refreshSdk: false, runBuild: runPinnedVite });
58
+ // A summary only: the backend grades the saved build receipt bytes, not this line.
59
+ const { local_key, kind, name, spec_version } = result.manifest || {};
60
+ return { status: 'built', key: request.key, receipt_path: result.receiptPath, source_hash: result.sourceHash,
61
+ manifest: { local_key, kind, name, spec_version } };
62
+ }
63
+
64
+ async function main(argv) {
65
+ const request = JSON.parse(readFileSync(argv[2], 'utf8'));
66
+ try {
67
+ process.stdout.write(JSON.stringify(await runConfinedBuild(request)) + '\n');
68
+ } catch (error) {
69
+ process.stdout.write(JSON.stringify({ status: 'failed', error: String(error?.message || error).slice(0, OUTPUT_LIMIT) }) + '\n');
70
+ process.exitCode = 1;
71
+ }
72
+ }
73
+
74
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) await main(process.argv);
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Credential-free canonical verification of one accepted confined build
3
+ * (server/lib/confined_effects/space_build.py).
4
+ *
5
+ * node confined-space-verify.js <verify-request.json>
6
+ *
7
+ * Runs outside the build sandbox so Chromium keeps its own sandbox
8
+ * (chromiumSandbox: true in the shared renderer pool). The request carries the
9
+ * capabilities returned by backend source verification and the accepted build's
10
+ * exact source and artifact digests; anything else refuses before rendering.
11
+ * Rendering uses only the declared offline fixtures at 390 and 1440 (no live
12
+ * data, no deployment, no Store publication).
13
+ */
14
+ import { readFileSync, realpathSync } from 'node:fs';
15
+ import { pathToFileURL } from 'node:url';
16
+ import { appFilesDigest } from './app-platform.js';
17
+ import { prepareSpaceRelease } from './space-platform.js';
18
+ import { verifySpaceRelease } from './space-verification.js';
19
+
20
+ export async function runConfinedVerification(request, { verify = verifySpaceRelease } = {}) {
21
+ if (!request || typeof request.projectDir !== 'string' || typeof request.key !== 'string'
22
+ || typeof request.sourceDigest !== 'string' || typeof request.artifactDigest !== 'string'
23
+ || !request.capabilities || typeof request.capabilities !== 'object') {
24
+ throw new Error('The verification request names one accepted build and its capabilities.');
25
+ }
26
+ const release = await prepareSpaceRelease(request.projectDir, request.key);
27
+ try {
28
+ const sourceDigest = appFilesDigest(release.sourceFiles), artifactDigest = appFilesDigest(release.files);
29
+ if (sourceDigest !== request.sourceDigest || artifactDigest !== request.artifactDigest) {
30
+ return { status: 'failed', error: 'build_changed', source_digest: sourceDigest, artifact_digest: artifactDigest };
31
+ }
32
+ const diagnostic = await verify(release, { capabilities: request.capabilities, timeoutMs: 45_000 });
33
+ return { status: diagnostic.status === 'passed' ? 'verified' : 'failed', diagnostic };
34
+ } finally { release.close(); }
35
+ }
36
+
37
+ async function main(argv) {
38
+ const request = JSON.parse(readFileSync(argv[2], 'utf8'));
39
+ try {
40
+ const result = await runConfinedVerification(request);
41
+ process.stdout.write(JSON.stringify(result) + '\n');
42
+ process.exitCode = result.status === 'verified' ? 0 : 3;
43
+ } catch (error) {
44
+ process.stdout.write(JSON.stringify({ status: 'failed', error: String(error?.message || error).slice(0, 32 * 1024) }) + '\n');
45
+ process.exitCode = 1;
46
+ }
47
+ }
48
+
49
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) await main(process.argv);
@@ -0,0 +1,47 @@
1
+ import { open } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ import { basename, extname } from 'node:path';
4
+ import { httpRequest } from './transport.js';
5
+ import { usageError } from './errors.js';
6
+
7
+ const MAX_BYTES = 8 * 1024 * 1024;
8
+ const UUID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i;
9
+ const MIME = { '.html': 'text/html', '.htm': 'text/html', '.txt': 'text/plain', '.md': 'text/markdown',
10
+ '.json': 'application/json', '.pdf': 'application/pdf', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp' };
11
+
12
+ /** Reads a bounded local file, never interprets it and never attaches it to a row. */
13
+ export async function uploadNativeRecordFileWithCliRuntime({ runtime, target, recordKey, schemaRevision, requestId,
14
+ filePath, name = basename(filePath || ''), contentType = MIME[extname(filePath || '').toLowerCase()] || 'application/octet-stream',
15
+ signal, request = httpRequest }) {
16
+ if (!UUID.test(recordKey || '') || !UUID.test(requestId || '') || !Number.isSafeInteger(schemaRevision) || schemaRevision < 0
17
+ || !target || typeof target !== 'object' || Array.isArray(target) || !filePath) {
18
+ throw usageError('Choose a local file, native target, record key, schema revision and stable request ID.');
19
+ }
20
+ const handle = await open(filePath, 'r');
21
+ let data;
22
+ try {
23
+ const stat = await handle.stat();
24
+ if (!stat.isFile() || stat.size < 1 || stat.size > MAX_BYTES) throw usageError('Choose a regular file up to 8 MB.');
25
+ const buffer = Buffer.alloc(MAX_BYTES + 1);
26
+ let length = 0;
27
+ while (length < buffer.length) {
28
+ const read = await handle.read(buffer, length, buffer.length - length, length);
29
+ if (!read.bytesRead) break;
30
+ length += read.bytesRead;
31
+ }
32
+ if (!length || length > MAX_BYTES) throw usageError('Choose a file up to 8 MB.');
33
+ data = buffer.subarray(0, length);
34
+ } finally { await handle.close(); }
35
+ const digest = createHash('sha256').update(data).digest('hex');
36
+ const body = { protocol: 1, target, record_key: recordKey, schema_revision: schemaRevision, request_id: requestId,
37
+ name, content_type: contentType, base64: data.toString('base64') };
38
+ // No automatic retry: repeating this command with the same request ID is the
39
+ // exact immutable intent; a mismatched request fails instead of overwriting.
40
+ const result = (await request({ runtime, method: 'POST', path: '/portal_native_files/upload', spacesProtocol: 1, body, signal })).payload;
41
+ if (result?.protocol !== 1 || result.record_key !== recordKey || result.request_id !== requestId
42
+ || result.sha256 !== digest || result.size_bytes !== data.length || result.file?.name !== name
43
+ || typeof result.file?.url !== 'string' || !/^https?:\/\//.test(result.file.url) || result.url !== result.file.url) {
44
+ throw usageError('The file upload is not confirmed. Repeat the same request ID and file bytes.');
45
+ }
46
+ return result;
47
+ }
@@ -0,0 +1,20 @@
1
+ /** Exact canonical files retired by the one-Shadow-runtime cutover.
2
+ * Modified or additional source is retained for explicit reconciliation. */
3
+ // Host mounting code stays in the canonical SDK and CLI host bundle, not in
4
+ // authored apps' public SDK mirror. It is not a public package export.
5
+ export const HOST_ONLY_SDK_FILES = Object.freeze(['src/presentation.tsx']);
6
+
7
+ export const RETIRED_SDK_FILES = Object.freeze({
8
+ "src/presentation.tsx": [
9
+ "d29022f92e1e96b451b62cc8a561878a1c0cf8a3b4c0419181de7266f8a1b525"
10
+ ],
11
+ "src/isolatedHost.tsx": [
12
+ "7f59049281a9533d39c557ca75635073c2f404f1eb0a528b274323d61db776cd"
13
+ ],
14
+ "src/frameProtocol.ts": [
15
+ "f32337ddb507718be04289a4acf50cf2485698bc716556230f35f06a6f633f19"
16
+ ],
17
+ "src/frameIsolation.ts": [
18
+ "8b57cb38f618eefc4133ae50fbf2b37bbdef5be02e44b39758d8328fb2a59ab6"
19
+ ]
20
+ });
@@ -1,5 +1,7 @@
1
- import { createSkillBundleBase64 } from './local-scanner';
2
- import type { AgentTargets, LocalSkill, SyncPullResponse, SyncPushResponse, SyncSettings } from './types';
1
+ import { captureNativeSkillFiles, createSkillBundleBase64 } from './local-scanner';
2
+ import { createHash } from 'node:crypto';
3
+ import { checkedNativeReference } from './native-identity';
4
+ import type { AgentTargets, LocalSkill, NativeSkillReference, SyncPullResponse, SyncPushResponse, SyncSettings } from './types';
3
5
 
4
6
  type JsonBody = Record<string, unknown> | undefined;
5
7
 
@@ -34,7 +36,7 @@ export async function fetchSyncSettings(serverUrl: string, jwt: string): Promise
34
36
 
35
37
  export async function pullSkills(serverUrl: string, jwt: string): Promise<SyncPullResponse> {
36
38
  return requestJson<SyncPullResponse>(`${serverUrl}/portal_skills/sync-pull`, jwt, {
37
- body: {},
39
+ body: { skill_sync_protocol: 2 },
38
40
  });
39
41
  }
40
42
 
@@ -49,11 +51,16 @@ export async function pushChangedSkills(
49
51
  skill_md: skill.skillMd,
50
52
  source_url: skill.sourceUrl,
51
53
  folder_hash: skill.folderHash,
52
- bundle_base64: await createSkillBundleBase64(skill),
54
+ ...(skill.nativeReference ? { bundle_files: await captureNativeSkillFiles(skill) }
55
+ : { bundle_base64: await createSkillBundleBase64(skill) }),
56
+ ...(skill.cloudId ? { cloud_id: skill.cloudId } : {}),
57
+ ...(skill.nativeReference ? { native_reference: skill.nativeReference, display_name: skill.displayName,
58
+ native_scope: skill.nativeScope } : {}),
53
59
  })));
54
60
 
55
61
  return requestJson<SyncPushResponse>(`${serverUrl}/portal_skills/sync-push`, jwt, {
56
62
  body: {
63
+ skill_sync_protocol: 2,
57
64
  skills: payloadSkills,
58
65
  },
59
66
  });
@@ -75,7 +82,22 @@ export async function updateAgentTargets(
75
82
  skillId: string,
76
83
  targets: Partial<AgentTargets>,
77
84
  expectedUpdatedAt?: string,
85
+ native?: { reference: NativeSkillReference; settingsRevision: number },
78
86
  ): Promise<{ success: boolean; agent_targets: AgentTargets; updated_at?: string }> {
87
+ if (native) {
88
+ checkedNativeReference(native.reference, skillId);
89
+ if (!Number.isSafeInteger(native.settingsRevision) || native.settingsRevision < 0) throw new Error('Refresh native Skill settings before changing assignments.');
90
+ const payload = { operation: 'update', target: { skill_id: skillId, access_revision: native.reference.access_revision },
91
+ revision: native.settingsRevision, patch: { agent_targets: Object.fromEntries(Object.entries(targets).sort(([a], [b]) => a.localeCompare(b))) } };
92
+ const requestId = 'sync-settings-' + createHash('sha256').update(JSON.stringify(payload)).digest('hex');
93
+ const saved = await requestJson<{ receipt_id: string; skill_id: string; target: { skill_id: string }; settings: { agent_targets: AgentTargets; updated_at: string };
94
+ current?: { target: { skill_id: string }; settings: { agent_targets: AgentTargets; updated_at: string } } }>(`${serverUrl}/portal_skills/native-settings`, jwt,
95
+ { body: { ...payload, request_id: requestId } });
96
+ const current = saved.current || saved;
97
+ if (saved.skill_id !== skillId || typeof saved.receipt_id !== 'string' || !saved.receipt_id
98
+ || current.target?.skill_id !== skillId || !current.settings) throw new Error('Native assignment update was not confirmed.');
99
+ return { success: true, agent_targets: current.settings.agent_targets, updated_at: current.settings.updated_at };
100
+ }
79
101
  return requestJson<{ success: boolean; agent_targets: AgentTargets; updated_at?: string }>(`${serverUrl}/portal_skills/agent-targets`, jwt, {
80
102
  method: 'PATCH',
81
103
  body: {