@notis_ai/cli 0.2.0-beta.191.1 → 0.2.0-beta.196.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.
- package/README.md +479 -80
- package/config/portal-route-roots.json +48 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +29072 -2784
- package/dist/base-skills/notis-apps/SKILL.md +88 -67
- package/dist/base-skills/notis-apps/references/architecture.md +114 -167
- package/dist/base-skills/notis-apps/references/design.md +26 -107
- package/dist/base-skills/notis-apps/references/memory.md +58 -0
- package/dist/base-skills/notis-apps/references/reading.md +52 -74
- package/dist/base-skills/notis-apps/references/release.md +104 -142
- package/dist/base-skills/notis-apps/references/sdk.md +113 -60
- package/dist/base-skills/notis-apps/references/troubleshooting.md +22 -21
- package/dist/base-skills/notis-apps/references/views.md +142 -0
- package/dist/base-skills/notis-cli/SKILL.md +4 -4
- package/dist/base-skills/notis-cli/references/app-delivery.md +12 -11
- package/dist/base-skills/notis-cli/references/native-databases.md +6 -6
- package/dist/base-skills/notis-cli/references/tool-examples.md +4 -4
- package/dist/base-skills/notis-query/SKILL.md +34 -60
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -69
- package/dist/base-skills/notis-query/references/documents.md +63 -50
- package/dist/base-skills/notis-query/references/query.md +57 -524
- package/dist/sdk/src/bundleModule.ts +79 -0
- package/dist/sdk/src/components/DocumentEditor.tsx +6 -3
- package/dist/sdk/src/components/DocumentPage.tsx +141 -0
- package/dist/sdk/src/components/RecordComponents.tsx +33 -0
- package/dist/sdk/src/components/RenderChartData.tsx +19 -0
- package/dist/sdk/src/config.ts +4 -26
- package/dist/sdk/src/documents.ts +2 -0
- package/dist/sdk/src/hooks/useAction.ts +28 -0
- package/dist/sdk/src/hooks/useCloudComputer.ts +35 -4
- package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
- package/dist/sdk/src/hooks/useHandover.ts +5 -4
- package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
- package/dist/sdk/src/hooks/useNativeDocuments.ts +83 -0
- package/dist/sdk/src/hooks/useNotis.ts +3 -0
- package/dist/sdk/src/hooks/useNotisNavigation.ts +26 -11
- package/dist/sdk/src/hooks/useQuery.ts +7 -4
- package/dist/sdk/src/hooks/useViewParams.ts +121 -0
- package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
- package/dist/sdk/src/index.ts +36 -0
- package/dist/sdk/src/interactions/shortcuts.tsx +17 -3
- package/dist/sdk/src/nativeData.ts +39 -0
- package/dist/sdk/src/presentation.tsx +82 -0
- package/dist/sdk/src/provider.tsx +9 -4
- package/dist/sdk/src/queryCache.ts +5 -3
- package/dist/sdk/src/queryErrors.ts +51 -0
- package/dist/sdk/src/runtime.ts +146 -8
- package/dist/sdk/src/space.ts +425 -0
- package/dist/sdk/src/styles.css +96 -72
- package/dist/sdk/src/vite.ts +11 -1
- package/dist/skill-sync/index.js +458 -68
- package/dist/skill-sync/index.js.map +4 -4
- package/dist/skill-sync-worker.mjs +451 -77
- package/dist/space-harness/host.js +66 -0
- package/dist/view-renderer/dist/host.css +1 -0
- package/dist/view-renderer/dist/host.js +1042 -0
- package/dist/view-renderer/dist/theme.css +73 -0
- package/dist/view-renderer/src/dom.js +180 -0
- package/dist/view-renderer/src/errors.js +8 -0
- package/dist/view-renderer/src/host.tsx +173 -0
- package/dist/view-renderer/src/index.js +4 -0
- package/dist/view-renderer/src/pool.js +76 -0
- package/dist/view-renderer/src/remote-broker.js +59 -0
- package/dist/view-renderer/src/renderer.js +201 -0
- package/package.json +11 -5
- package/skills/notis-apps/cli.md +270 -163
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +2 -2
- package/skills/notis-onboarding/BRIEF.md +7 -3
- package/src/command-specs/apps.js +1 -8
- package/src/command-specs/files.js +29 -0
- package/src/command-specs/helpers.js +1 -0
- package/src/command-specs/index.js +10 -4
- package/src/command-specs/skills.js +229 -0
- package/src/command-specs/spaces.js +572 -0
- package/src/command-specs/views.js +69 -0
- package/src/runtime/agent-browser.js +30 -7
- package/src/runtime/app-boundary-validator.js +7 -4
- package/src/runtime/app-data-literals.js +82 -0
- package/src/runtime/app-platform.js +321 -253
- package/src/runtime/confined-space-build.js +74 -0
- package/src/runtime/confined-space-verify.js +49 -0
- package/src/runtime/native-record-files.js +47 -0
- package/src/runtime/retired-sdk-files.js +20 -0
- package/src/runtime/skill-sync/cloud-client.ts +26 -4
- package/src/runtime/skill-sync/index.ts +154 -35
- package/src/runtime/skill-sync/local-scanner.ts +125 -17
- package/src/runtime/skill-sync/native-identity.ts +102 -0
- package/src/runtime/skill-sync/native-plan.ts +59 -0
- package/src/runtime/skill-sync/sync-plan.ts +16 -9
- package/src/runtime/skill-sync/types.ts +32 -6
- package/src/runtime/source-root.js +32 -0
- package/src/runtime/space-collection-scaffold.js +66 -0
- package/src/runtime/space-database-structures.js +229 -0
- package/src/runtime/space-fixture-broker.js +50 -0
- package/src/runtime/space-lock.js +337 -0
- package/src/runtime/space-platform.js +105 -0
- package/src/runtime/space-source.js +281 -0
- package/src/runtime/space-test-server.js +327 -0
- package/src/runtime/space-verification.js +89 -0
- package/src/runtime/space-verify-browser-worker.js +51 -0
- package/src/runtime/space-view-manifest.js +254 -0
- package/src/runtime/sync-skills.js +1 -1
- package/src/runtime/transport.js +8 -1
- package/src/runtime/view-render.js +49 -0
- package/src/space-harness-host.ts +103 -0
- package/template/app/globals.css +1 -0
- package/src/command-specs/reports.js +0 -97
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createBrowserPool } from './pool.js';
|
|
3
|
+
import { expandScrollAreas, extractRenderedMarkdown, restrictBrowserTransports, freezeFrameViewportSizes, settleFrameImages } from './dom.js';
|
|
4
|
+
import { ViewRenderError, renderFailure } from './errors.js';
|
|
5
|
+
|
|
6
|
+
export const RENDER_ORIGIN = 'http://notis-render.invalid';
|
|
7
|
+
const READ_OPERATIONS = new Set(['action', 'shown', 'viewer_read', 'document_body', 'record', 'html', 'report', 'resolve_links']);
|
|
8
|
+
const CSP = "default-src 'none'; script-src 'self' blob: data: 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-src 'self' blob: data:; worker-src 'none'; form-action 'none'; base-uri 'none'; object-src 'none'";
|
|
9
|
+
const DEFAULT_HTML = '<!doctype html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="/theme.css"><link rel="stylesheet" href="/host.css"></head><body><script src="/host.js"></script></body></html>';
|
|
10
|
+
|
|
11
|
+
function inputs({ outputs = ['markdown', 'screenshot'], width = 1440, timeoutMs = 45000 }) {
|
|
12
|
+
if (!Array.isArray(outputs) || !outputs.length || new Set(outputs).size !== outputs.length || outputs.some(value => !['markdown', 'screenshot'].includes(value))) throw new TypeError('Choose markdown and/or screenshot once.');
|
|
13
|
+
if (![390, 1440].includes(width)) throw new TypeError('Render at 390 or 1440 CSS pixels.');
|
|
14
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > 90000) throw new TypeError('Use a bounded render timeout.');
|
|
15
|
+
return { outputs, width, timeoutMs };
|
|
16
|
+
}
|
|
17
|
+
function publicSnapshot(snapshot) {
|
|
18
|
+
// Explicit whitelist: never spread a ticket, broker object, endpoint or token
|
|
19
|
+
// into source-visible bootstrap data, even when the provider adds fields.
|
|
20
|
+
return { descriptor: snapshot.descriptor, source: snapshot.source, css: snapshot.css || '',
|
|
21
|
+
cacheKey: snapshot.cacheKey, markdownExport: snapshot.markdownExport || null,
|
|
22
|
+
url: snapshot.url, hostCss: snapshot.hostCss || '', params: snapshot.params || {}, locale: snapshot.locale || 'en' };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function diagnosticFailure(error, stage, fallback = 'render_failed') {
|
|
26
|
+
const failure = error instanceof ViewRenderError
|
|
27
|
+
? new ViewRenderError(error.code, error.message, error.status) : renderFailure(fallback);
|
|
28
|
+
// Private in-process diagnostics only. Public JSON/error projections cannot
|
|
29
|
+
// enumerate a browser exception, which may contain source or request data.
|
|
30
|
+
Object.defineProperties(failure, { cause: { value: error }, stage: { value: stage } });
|
|
31
|
+
return failure;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Shared by CLI and warm service. No broad credential is an argument to this library. */
|
|
35
|
+
export async function renderView({ broker, outputs, width, timeoutMs, signal, pool, host, maxHeight = 60000, maxPixels = 80000000 }) {
|
|
36
|
+
const config = inputs({ outputs, width, timeoutMs });
|
|
37
|
+
const deadline = AbortSignal.timeout(config.timeoutMs);
|
|
38
|
+
const cancellation = signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
39
|
+
// Fail authorization before opening a browser or exposing cached page data.
|
|
40
|
+
let snapshot;
|
|
41
|
+
try { snapshot = await broker.snapshot({ signal: cancellation }); }
|
|
42
|
+
catch (error) { throw diagnosticFailure(error, 'manifest', cancellation.aborted ? 'render_timeout' : 'render_failed'); }
|
|
43
|
+
if (!snapshot || typeof snapshot.url !== 'string' || !snapshot.view || !snapshot.descriptor || typeof snapshot.source !== 'string') throw renderFailure('render_manifest_invalid');
|
|
44
|
+
if (snapshot.render_options && (!Array.isArray(snapshot.render_options.outputs) || snapshot.render_options.width !== config.width
|
|
45
|
+
|| JSON.stringify([...snapshot.render_options.outputs].sort()) !== JSON.stringify([...config.outputs].sort()))) {
|
|
46
|
+
throw new ViewRenderError('render_options_changed', 'Use the outputs and width authorized for this render.', 409);
|
|
47
|
+
}
|
|
48
|
+
const hostSource = host?.script ?? await readFile(new URL('../dist/host.js', import.meta.url), 'utf8');
|
|
49
|
+
snapshot.hostCss = host ? host.css || '' : await readFile(new URL('../dist/host.css', import.meta.url), 'utf8');
|
|
50
|
+
snapshot.theme_css ||= host ? host.themeCss || '' : await readFile(new URL('../dist/theme.css', import.meta.url), 'utf8');
|
|
51
|
+
const ownPool = !pool;
|
|
52
|
+
pool ||= createBrowserPool();
|
|
53
|
+
const blocked = [];
|
|
54
|
+
let authorityLost = false, authorityFailure, transientFailure, assetRequests = 0, assetBytes = 0, readRequests = 0;
|
|
55
|
+
let stage = 'browser_context';
|
|
56
|
+
let assetOrigin = null;
|
|
57
|
+
try { const asset = new URL(snapshot.asset_origin); if (asset.protocol === 'https:' && asset.origin === snapshot.asset_origin) assetOrigin = asset.origin; } catch {}
|
|
58
|
+
const policy = assetOrigin ? CSP.replace("img-src 'self' data: blob:", "img-src 'self' data: blob: " + assetOrigin).replace("font-src 'self' data:", "font-src 'self' data: " + assetOrigin) : CSP;
|
|
59
|
+
try {
|
|
60
|
+
return await pool.use(async context => {
|
|
61
|
+
stage = 'browser_setup';
|
|
62
|
+
context.setDefaultTimeout(config.timeoutMs);
|
|
63
|
+
await context.addInitScript(restrictBrowserTransports);
|
|
64
|
+
await context.routeWebSocket('**/*', socket => socket.close({ code: 1008, reason: 'Read-only rendering has no sockets.' }));
|
|
65
|
+
await context.route('**/*', async route => {
|
|
66
|
+
const request = route.request();
|
|
67
|
+
const url = new URL(request.url());
|
|
68
|
+
const respond = (status, data, contentType = 'application/json') => route.fulfill({ status, body: typeof data === 'string' ? data : JSON.stringify(data),
|
|
69
|
+
contentType, headers: { 'Cache-Control': 'no-store', 'Content-Security-Policy': policy, 'X-Content-Type-Options': 'nosniff' } });
|
|
70
|
+
let payload;
|
|
71
|
+
try {
|
|
72
|
+
if (url.origin !== RENDER_ORIGIN || url.search || url.hash) {
|
|
73
|
+
const kind = request.resourceType();
|
|
74
|
+
if (url.origin === assetOrigin && request.method() === 'GET' && ['image', 'font'].includes(kind)
|
|
75
|
+
&& !url.search && !url.hash && ++assetRequests <= 128) {
|
|
76
|
+
const asset = await broker.read({ operation: 'asset', url: url.href, kind }, { signal: cancellation });
|
|
77
|
+
const bytes = Buffer.from(asset.data_base64, 'base64'); assetBytes += bytes.length;
|
|
78
|
+
if (assetBytes > 32 * 1024 * 1024 || !/^(image|font)\//.test(asset.mime_type)) throw renderFailure('render_asset_limit');
|
|
79
|
+
return route.fulfill({ status: 200, body: bytes, contentType: asset.mime_type,
|
|
80
|
+
headers: { 'Cache-Control': 'no-store', 'Access-Control-Allow-Origin': '*', 'X-Content-Type-Options': 'nosniff' } });
|
|
81
|
+
}
|
|
82
|
+
if (blocked.length < 100) blocked.push({ code: 'render_egress_denied' });
|
|
83
|
+
return route.abort('blockedbyclient');
|
|
84
|
+
}
|
|
85
|
+
if (request.method() === 'GET') {
|
|
86
|
+
if (url.pathname === '/') return respond(200, host?.html || DEFAULT_HTML, 'text/html');
|
|
87
|
+
if (url.pathname === '/host.js') return respond(200, hostSource, 'text/javascript');
|
|
88
|
+
if (url.pathname === '/host.css') return respond(200, snapshot.hostCss || '', 'text/css');
|
|
89
|
+
if (url.pathname === '/theme.css') return respond(200, snapshot.theme_css || '', 'text/css');
|
|
90
|
+
if (url.pathname === '/__snapshot') return respond(200, publicSnapshot(snapshot));
|
|
91
|
+
return respond(404, { error: 'render_resource_unavailable' });
|
|
92
|
+
}
|
|
93
|
+
if (url.pathname !== '/__read' || request.method() !== 'POST' || (request.postData()?.length || 0) > 262144) return respond(403, { error: 'render_read_only' });
|
|
94
|
+
payload = request.postDataJSON();
|
|
95
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !READ_OPERATIONS.has(payload.operation)) {
|
|
96
|
+
if (blocked.length < 100) blocked.push({ code: 'render_read_only' });
|
|
97
|
+
return respond(403, { error: 'render_read_only', message: 'Rendering never changes records or runs write actions.' });
|
|
98
|
+
}
|
|
99
|
+
if (++readRequests > 128) throw new ViewRenderError('render_read_limit', 'This render exceeded its read limit.', 429);
|
|
100
|
+
// The server independently reconstructs and classifies every call.
|
|
101
|
+
const result = await broker.read(payload, { signal: cancellation });
|
|
102
|
+
return respond(200, { result });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const code = ['render_read_only', 'render_paid_background_denied'].includes(error?.code) ? error.code : 'render_read_unavailable';
|
|
105
|
+
if (['render_read_only', 'render_paid_background_denied'].includes(code)) {
|
|
106
|
+
if (blocked.length < 100) blocked.push({ code, ...(typeof payload?.action_id === 'string' && /^[a-zA-Z0-9_-]{1,100}$/.test(payload.action_id) ? { action_id: payload.action_id } : {}) });
|
|
107
|
+
} else if (error?.status >= 500 || error?.status === 429) {
|
|
108
|
+
// Backend load is not an access change: fail the capture as retryable.
|
|
109
|
+
transientFailure ||= error;
|
|
110
|
+
} else { authorityLost = true; authorityFailure ||= error; }
|
|
111
|
+
await respond(error?.status === 401 || error?.status === 404 ? error.status : 403, { error: code, message: 'This read is unavailable in the current render.' }).catch(() => {});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const page = await context.newPage();
|
|
115
|
+
context.on('page', child => { if (child !== page) void child.close(); });
|
|
116
|
+
stage = 'host_navigation';
|
|
117
|
+
await page.goto(RENDER_ORIGIN, { waitUntil: 'domcontentloaded' });
|
|
118
|
+
stage = 'host_ready';
|
|
119
|
+
await page.waitForFunction(() => window.__notisRenderState?.mounted && window.__notisRenderState.pending === 0 && performance.now() - window.__notisRenderState.lastActivity > 100, undefined, { timeout: config.timeoutMs });
|
|
120
|
+
// A committed data response must settle React layout and browser fonts.
|
|
121
|
+
stage = 'fonts_layout';
|
|
122
|
+
await page.evaluate(async () => {
|
|
123
|
+
await Promise.race([document.fonts.ready, new Promise(resolve => setTimeout(resolve, 2000))]);
|
|
124
|
+
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
|
125
|
+
});
|
|
126
|
+
let lastHeight = -1, settled = false;
|
|
127
|
+
const frozenFrames = new WeakSet();
|
|
128
|
+
stage = 'layout_expansion';
|
|
129
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
130
|
+
for (const frame of [...page.frames()].reverse()) {
|
|
131
|
+
if (!await frame.evaluate(settleFrameImages)) throw renderFailure('render_asset_unavailable');
|
|
132
|
+
if (frame !== page.mainFrame() && !frozenFrames.has(frame)) {
|
|
133
|
+
await frame.evaluate(freezeFrameViewportSizes); frozenFrames.add(frame);
|
|
134
|
+
}
|
|
135
|
+
const size = await frame.evaluate(expandScrollAreas, { maxHeight });
|
|
136
|
+
if (size.tooLarge) throw new ViewRenderError('render_too_large', 'This view exceeds the full-page render limit; nothing was cropped.', 413);
|
|
137
|
+
if (frame !== page.mainFrame()) await (await frame.frameElement()).evaluate((node, height) => {
|
|
138
|
+
node.style.setProperty('height', `${height}px`, 'important'); node.style.setProperty('min-height', `${height}px`, 'important');
|
|
139
|
+
node.style.setProperty('max-height', 'none', 'important'); node.style.setProperty('flex-shrink', '0', 'important');
|
|
140
|
+
node.style.setProperty('flex-basis', 'auto', 'important');
|
|
141
|
+
}, size.height);
|
|
142
|
+
}
|
|
143
|
+
// Expansion can reveal lazy rows and trigger additional declared reads.
|
|
144
|
+
// Wait for their committed DOM before deciding that the full page fits.
|
|
145
|
+
await page.waitForFunction(() => window.__notisRenderState.pending === 0 && performance.now() - window.__notisRenderState.lastActivity > 100,
|
|
146
|
+
undefined, { timeout: config.timeoutMs });
|
|
147
|
+
const height = await page.evaluate(() => Math.max(document.documentElement.scrollHeight, document.body.scrollHeight));
|
|
148
|
+
if (height === lastHeight) { settled = true; break; }
|
|
149
|
+
lastHeight = height;
|
|
150
|
+
}
|
|
151
|
+
if (!settled) throw new ViewRenderError('render_unstable', 'This view did not settle into a full-page capture; nothing was cropped.', 409);
|
|
152
|
+
if (blocked.some(value => value.code === 'render_paid_background_denied')) throw new ViewRenderError('render_paid_background_denied', 'Background snapshots cannot run paid reads.', 403);
|
|
153
|
+
const dimensions = await page.evaluate(() => ({ width: innerWidth, height: Math.max(document.documentElement.scrollHeight, document.body.scrollHeight) }));
|
|
154
|
+
if (dimensions.height > maxHeight || dimensions.width * dimensions.height > maxPixels) throw new ViewRenderError('render_too_large', 'This view exceeds the full-page render limit; nothing was cropped.', 413);
|
|
155
|
+
let markdown, screenshot;
|
|
156
|
+
if (config.outputs.includes('markdown')) {
|
|
157
|
+
stage = 'markdown';
|
|
158
|
+
let override;
|
|
159
|
+
try { override = await page.evaluate(async () => {
|
|
160
|
+
if (typeof window.__notisRenderMarkdown === 'function') return await window.__notisRenderMarkdown();
|
|
161
|
+
return null;
|
|
162
|
+
}); } catch { throw renderFailure('render_markdown_invalid'); }
|
|
163
|
+
if (override !== null && typeof override !== 'string') throw renderFailure('render_markdown_invalid');
|
|
164
|
+
markdown = override ?? (await Promise.all(page.frames().map(frame => frame.evaluate(extractRenderedMarkdown, { baseUrl: snapshot.url })))).filter(Boolean).join('\n\n');
|
|
165
|
+
if (Buffer.byteLength(markdown, 'utf8') > 2000000) throw new ViewRenderError('render_too_large', 'The Markdown exceeds the render limit.', 413);
|
|
166
|
+
markdown = markdown.replace(/\r\n/g, '\n').trim() + '\n';
|
|
167
|
+
}
|
|
168
|
+
if (config.outputs.includes('screenshot')) {
|
|
169
|
+
stage = 'screenshot';
|
|
170
|
+
const png = await page.screenshot({ type: 'png', fullPage: true, animations: 'disabled', caret: 'hide', scale: 'css' });
|
|
171
|
+
if (png.length > 32 * 1024 * 1024) throw new ViewRenderError('render_too_large', 'The PNG exceeds the render transport limit; nothing was cropped.', 413);
|
|
172
|
+
screenshot = { mime_type: 'image/png', data_base64: png.toString('base64'), width: png.readUInt32BE(16), height: png.readUInt32BE(20) };
|
|
173
|
+
}
|
|
174
|
+
stage = 'read_completion';
|
|
175
|
+
if (blocked.some(value => value.code === 'render_paid_background_denied')) throw new ViewRenderError('render_paid_background_denied', 'Background snapshots cannot run paid reads.', 403);
|
|
176
|
+
for (const code of await page.evaluate(() => (window.__notisRenderState.blocked || []).filter(value => value === 'render_read_only').slice(0, 100))) {
|
|
177
|
+
if (blocked.length < 100) blocked.push({ code });
|
|
178
|
+
}
|
|
179
|
+
if (transientFailure && !authorityLost && !cancellation.aborted) {
|
|
180
|
+
const failure = new ViewRenderError('render_temporarily_unavailable', 'The view could not be rendered right now. Try again in a moment.', 503);
|
|
181
|
+
Object.defineProperty(failure, 'cause', { value: transientFailure });
|
|
182
|
+
throw failure;
|
|
183
|
+
}
|
|
184
|
+
if (authorityLost || cancellation.aborted || await page.evaluate(() => Boolean(window.__notisRenderState.failed))) {
|
|
185
|
+
const failure = renderFailure('render_access_changed');
|
|
186
|
+
if (authorityFailure) Object.defineProperty(failure, 'cause', { value: authorityFailure });
|
|
187
|
+
throw failure;
|
|
188
|
+
}
|
|
189
|
+
// Do not release Markdown or image bytes after revocation/source changes.
|
|
190
|
+
stage = 'authorize_finish';
|
|
191
|
+
const finalAuthority = await broker.finish({ signal: cancellation });
|
|
192
|
+
if (finalAuthority?.valid !== true) throw renderFailure('render_access_changed');
|
|
193
|
+
return { url: snapshot.url, view: snapshot.view, ...(snapshot.record_key ? { record_key: snapshot.record_key } : {}),
|
|
194
|
+
...(typeof finalAuthority.render_id === 'string' ? { render_id: finalAuthority.render_id } : {}),
|
|
195
|
+
params: snapshot.params || {}, ...(markdown !== undefined ? { markdown } : {}), ...(screenshot ? { screenshot } : {}),
|
|
196
|
+
rendered_at: new Date().toISOString(), ...(blocked.length ? { blocked_calls: blocked } : {}) };
|
|
197
|
+
}, { signal: cancellation, width: config.width, locale: snapshot.locale || 'en', timezone: snapshot.timezone || 'UTC' });
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw diagnosticFailure(error, stage, cancellation.aborted ? 'render_timeout' : 'render_failed');
|
|
200
|
+
} finally { if (ownPool) await pool.close(); }
|
|
201
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notis_ai/cli",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.196.1",
|
|
4
4
|
"description": "Agent-first Notis CLI for apps and generic tool execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,12 +30,14 @@
|
|
|
30
30
|
"node": ">=22.12.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@babel/parser": "^7.29.9",
|
|
34
|
+
"ajv": "^8.20.0",
|
|
35
|
+
"ajv-formats": "^3.0.1",
|
|
33
36
|
"commander": "^15.0.0",
|
|
37
|
+
"esbuild": "^0.28.2",
|
|
38
|
+
"playwright-core": "1.63.0",
|
|
34
39
|
"sharp": "^0.35.4"
|
|
35
40
|
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"esbuild": "^0.28.2"
|
|
38
|
-
},
|
|
39
41
|
"keywords": [
|
|
40
42
|
"notis",
|
|
41
43
|
"cli",
|
|
@@ -45,5 +47,9 @@
|
|
|
45
47
|
"publishConfig": {
|
|
46
48
|
"access": "public"
|
|
47
49
|
},
|
|
48
|
-
"license": "MIT"
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"react": "19.3.0",
|
|
53
|
+
"react-dom": "19.3.0"
|
|
54
|
+
}
|
|
49
55
|
}
|