@agent-native/core 0.120.4 → 0.121.0
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/use-db-sync.ts +23 -21
- package/corpus/core/src/deploy/build.ts +1 -0
- package/corpus/core/src/server/core-routes-plugin.ts +3 -0
- package/corpus/core/src/server/gateway-access-check.ts +67 -0
- package/corpus/core/src/server/poll.ts +295 -9
- package/corpus/core/src/server/short-lived-token.ts +122 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +29 -4
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +365 -9
- package/corpus/templates/slides/app/components/editor/bullet-editing.ts +350 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +5 -0
- package/corpus/templates/slides/changelog/2026-07-23-new-bullet-rows-created-with-enter-now-keep-the-list-item-s-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-23-pressing-enter-in-generated-checkbox-shape-marker-lists-now-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-24-typing-a-markdown-style-dash-space-at-the-start-of-a-text-bl.md +6 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +27 -22
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +1 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +6 -6
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +3 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/gateway-access-check.d.ts +12 -0
- package/dist/server/gateway-access-check.d.ts.map +1 -0
- package/dist/server/gateway-access-check.js +49 -0
- package/dist/server/gateway-access-check.js.map +1 -0
- package/dist/server/poll.d.ts +55 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +254 -7
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/short-lived-token.d.ts +28 -0
- package/dist/server/short-lived-token.d.ts.map +1 -1
- package/dist/server/short-lived-token.js +78 -0
- package/dist/server/short-lived-token.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
- package/src/client/use-db-sync.ts +23 -21
- package/src/deploy/build.ts +1 -0
- package/src/server/core-routes-plugin.ts +3 -0
- package/src/server/gateway-access-check.ts +67 -0
- package/src/server/poll.ts +295 -9
- package/src/server/short-lived-token.ts +122 -0
|
@@ -324,3 +324,125 @@ export function verifyRealtimeSubscribeToken(
|
|
|
324
324
|
exp: claims.exp,
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
|
+
|
|
328
|
+
// ── Gateway access-check tokens ──────────────────────────────────────────────
|
|
329
|
+
//
|
|
330
|
+
// The hosted gateway has no access to an app's shareable-resource registry, so
|
|
331
|
+
// it cannot resolve sharee visibility itself. It signs one of these with the
|
|
332
|
+
// app's per-project key and calls the app's `/_agent-native/can-see`, which runs
|
|
333
|
+
// `resolveAccess` and answers. The full access query is bound into the token so
|
|
334
|
+
// the app authenticates the params, not merely the caller.
|
|
335
|
+
|
|
336
|
+
/** Payload `typ` discriminator for gateway access-check tokens. */
|
|
337
|
+
export const GATEWAY_ACCESS_TOKEN_TYPE = "rt-access-check";
|
|
338
|
+
/** Short TTL: minted per check, used immediately server-to-server. */
|
|
339
|
+
const DEFAULT_GATEWAY_ACCESS_TTL_SECONDS = 60;
|
|
340
|
+
|
|
341
|
+
/** Inputs for {@link signGatewayAccessToken}. */
|
|
342
|
+
export interface GatewayAccessClaims {
|
|
343
|
+
projectId: string;
|
|
344
|
+
resourceType: string;
|
|
345
|
+
resourceId: string;
|
|
346
|
+
/** App end-user whose visibility of the resource is being checked. */
|
|
347
|
+
userEmail: string;
|
|
348
|
+
orgId?: string;
|
|
349
|
+
ttlSeconds?: number;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface DecodedGatewayAccessClaims {
|
|
353
|
+
typ: string;
|
|
354
|
+
projectId: string;
|
|
355
|
+
resourceType: string;
|
|
356
|
+
resourceId: string;
|
|
357
|
+
userEmail: string;
|
|
358
|
+
orgId?: string;
|
|
359
|
+
exp: number;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Result of {@link verifyGatewayAccessToken}; on success carries the bound query. */
|
|
363
|
+
export type GatewayAccessVerifyResult =
|
|
364
|
+
| {
|
|
365
|
+
ok: true;
|
|
366
|
+
projectId: string;
|
|
367
|
+
resourceType: string;
|
|
368
|
+
resourceId: string;
|
|
369
|
+
userEmail: string;
|
|
370
|
+
orgId?: string;
|
|
371
|
+
}
|
|
372
|
+
| { ok: false; reason: string };
|
|
373
|
+
|
|
374
|
+
/** Mint a gateway access-check token, signed with the app's per-project `key`. */
|
|
375
|
+
export function signGatewayAccessToken(
|
|
376
|
+
claims: GatewayAccessClaims,
|
|
377
|
+
key: string,
|
|
378
|
+
): string {
|
|
379
|
+
if (!key) throw new Error("signGatewayAccessToken requires a key");
|
|
380
|
+
const ttl = claims.ttlSeconds ?? DEFAULT_GATEWAY_ACCESS_TTL_SECONDS;
|
|
381
|
+
const payload: DecodedGatewayAccessClaims = {
|
|
382
|
+
typ: GATEWAY_ACCESS_TOKEN_TYPE,
|
|
383
|
+
projectId: claims.projectId,
|
|
384
|
+
resourceType: claims.resourceType,
|
|
385
|
+
resourceId: claims.resourceId,
|
|
386
|
+
userEmail: claims.userEmail,
|
|
387
|
+
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
388
|
+
};
|
|
389
|
+
if (claims.orgId) payload.orgId = claims.orgId;
|
|
390
|
+
const payloadStr = base64UrlEncode(JSON.stringify(payload));
|
|
391
|
+
return `${payloadStr}.${hmacB64(payloadStr, key)}`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** Verify a gateway access-check token against the app's per-project `key`. */
|
|
395
|
+
export function verifyGatewayAccessToken(
|
|
396
|
+
token: string,
|
|
397
|
+
key: string,
|
|
398
|
+
expectedProjectId?: string,
|
|
399
|
+
): GatewayAccessVerifyResult {
|
|
400
|
+
if (!key) return { ok: false, reason: "no_key" };
|
|
401
|
+
if (typeof token !== "string" || !token.includes(".")) {
|
|
402
|
+
return { ok: false, reason: "malformed" };
|
|
403
|
+
}
|
|
404
|
+
const [payloadStr, sig] = token.split(".", 2);
|
|
405
|
+
if (!payloadStr || !sig) return { ok: false, reason: "malformed" };
|
|
406
|
+
|
|
407
|
+
if (!timingSafeEqualB64(sig, hmacB64(payloadStr, key))) {
|
|
408
|
+
return { ok: false, reason: "bad_signature" };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let claims: DecodedGatewayAccessClaims;
|
|
412
|
+
try {
|
|
413
|
+
claims = JSON.parse(base64UrlDecode(payloadStr).toString("utf8"));
|
|
414
|
+
} catch {
|
|
415
|
+
return { ok: false, reason: "bad_payload" };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (claims.typ !== GATEWAY_ACCESS_TOKEN_TYPE) {
|
|
419
|
+
return { ok: false, reason: "wrong_type" };
|
|
420
|
+
}
|
|
421
|
+
if (typeof claims.exp !== "number")
|
|
422
|
+
return { ok: false, reason: "bad_payload" };
|
|
423
|
+
if (claims.exp * 1000 < Date.now()) return { ok: false, reason: "expired" };
|
|
424
|
+
if (
|
|
425
|
+
!claims.projectId ||
|
|
426
|
+
!claims.resourceType ||
|
|
427
|
+
!claims.resourceId ||
|
|
428
|
+
!claims.userEmail
|
|
429
|
+
) {
|
|
430
|
+
return { ok: false, reason: "bad_payload" };
|
|
431
|
+
}
|
|
432
|
+
// Optional channel binding, mirroring verifyRealtimeSubscribeToken. The
|
|
433
|
+
// per-project key already scopes verification to one app; this is belt-and-
|
|
434
|
+
// suspenders for a future multi-tenant secret store. Skipped when the caller
|
|
435
|
+
// can't cheaply resolve its own project id (scoped-secret apps).
|
|
436
|
+
if (expectedProjectId && claims.projectId !== expectedProjectId) {
|
|
437
|
+
return { ok: false, reason: "wrong_project" };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
ok: true,
|
|
442
|
+
projectId: claims.projectId,
|
|
443
|
+
resourceType: claims.resourceType,
|
|
444
|
+
resourceId: claims.resourceId,
|
|
445
|
+
userEmail: claims.userEmail,
|
|
446
|
+
orgId: claims.orgId,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
IconAdjustments,
|
|
30
30
|
IconPencilPlus,
|
|
31
31
|
IconPin,
|
|
32
|
+
IconLetterT,
|
|
32
33
|
IconWand,
|
|
33
34
|
IconUpload,
|
|
34
35
|
IconSun,
|
|
@@ -123,6 +124,10 @@ interface EditorToolbarProps {
|
|
|
123
124
|
pinMode?: boolean;
|
|
124
125
|
/** Toggle comment-pin drop mode */
|
|
125
126
|
onTogglePinMode?: () => void;
|
|
127
|
+
/** Whether the add-text-box tool is active */
|
|
128
|
+
textBoxMode?: boolean;
|
|
129
|
+
/** Toggle the add-text-box tool */
|
|
130
|
+
onToggleTextBoxMode?: () => void;
|
|
126
131
|
/** Duplicate the current deck */
|
|
127
132
|
onDuplicateDeck?: () => void;
|
|
128
133
|
/** Export the deck as PDF */
|
|
@@ -247,6 +252,8 @@ export default function EditorToolbar({
|
|
|
247
252
|
onToggleDrawMode,
|
|
248
253
|
pinMode,
|
|
249
254
|
onTogglePinMode,
|
|
255
|
+
textBoxMode,
|
|
256
|
+
onToggleTextBoxMode,
|
|
250
257
|
onDuplicateDeck,
|
|
251
258
|
onExportPdf,
|
|
252
259
|
onExportPptx,
|
|
@@ -305,10 +312,10 @@ export default function EditorToolbar({
|
|
|
305
312
|
const [themeMounted, setThemeMounted] = useState(false);
|
|
306
313
|
useEffect(() => setThemeMounted(true), []);
|
|
307
314
|
const isDark = themeMounted ? resolvedTheme === "dark" : false;
|
|
308
|
-
// The
|
|
309
|
-
//
|
|
315
|
+
// The secondary tools share an "active when something is on" indicator so
|
|
316
|
+
// the dot on the consolidated button reflects any of them.
|
|
310
317
|
const anyToolActive = Boolean(
|
|
311
|
-
animationsOpen || tweaksOpen || drawMode || pinMode,
|
|
318
|
+
animationsOpen || tweaksOpen || drawMode || pinMode || textBoxMode,
|
|
312
319
|
);
|
|
313
320
|
|
|
314
321
|
const closeAll = () => {
|
|
@@ -731,7 +738,8 @@ graph TD
|
|
|
731
738
|
(onToggleAnimations ||
|
|
732
739
|
onToggleTweaks ||
|
|
733
740
|
onToggleDrawMode ||
|
|
734
|
-
onTogglePinMode
|
|
741
|
+
onTogglePinMode ||
|
|
742
|
+
onToggleTextBoxMode) && (
|
|
735
743
|
<>
|
|
736
744
|
<Tooltip>
|
|
737
745
|
<TooltipTrigger asChild>
|
|
@@ -834,6 +842,23 @@ graph TD
|
|
|
834
842
|
</span>
|
|
835
843
|
</button>
|
|
836
844
|
)}
|
|
845
|
+
{onToggleTextBoxMode && (
|
|
846
|
+
<button
|
|
847
|
+
onClick={() => {
|
|
848
|
+
onToggleTextBoxMode();
|
|
849
|
+
setToolsOpen(false);
|
|
850
|
+
}}
|
|
851
|
+
data-toolbar-textbox-button
|
|
852
|
+
className={`flex items-center gap-2 w-full px-3 py-1.5 text-xs transition-colors ${
|
|
853
|
+
textBoxMode
|
|
854
|
+
? "text-foreground bg-accent/50"
|
|
855
|
+
: "text-muted-foreground hover:text-foreground hover:bg-accent/50"
|
|
856
|
+
}`}
|
|
857
|
+
>
|
|
858
|
+
<IconLetterT className="w-3.5 h-3.5" />
|
|
859
|
+
{t("editorToolbar.addTextBox")}
|
|
860
|
+
</button>
|
|
861
|
+
)}
|
|
837
862
|
</div>
|
|
838
863
|
</ToolbarPopover>
|
|
839
864
|
</>
|