@neta-art/cohub 5.7.0 → 5.8.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/README.md +18 -5
- package/dist/board/core/palette.d.ts +3 -2
- package/dist/board/core/shape-types.d.ts +3 -1
- package/dist/board/core/shape-types.js +3 -7
- package/dist/board/core/tool-styles.d.ts +2 -1
- package/dist/board/image-key.js +3 -5
- package/dist/board/index.d.ts +9 -4
- package/dist/board/index.js +7 -3
- package/dist/board/media-playback.d.ts +22 -0
- package/dist/board/media-playback.js +67 -0
- package/dist/board/media.d.ts +7 -0
- package/dist/board/media.js +70 -0
- package/dist/board/nodes.d.ts +113 -0
- package/dist/board/nodes.js +154 -0
- package/dist/board/render/index.d.ts +3 -1
- package/dist/board/render/index.js +4 -2
- package/dist/board/render/media-interaction.d.ts +19 -0
- package/dist/board/render/media-interaction.js +26 -0
- package/dist/board/render/renderers/audio-card-renderer.js +1 -1
- package/dist/board/render/renderers/board-renderer-registry.js +2 -2
- package/dist/board/render/renderers/draw-card-renderer.js +1 -1
- package/dist/board/render/renderers/file-card-renderer.js +1 -1
- package/dist/board/render/renderers/frame-card-renderer.js +1 -1
- package/dist/board/render/renderers/geo-card-renderer.js +1 -1
- package/dist/board/render/renderers/image-card-renderer.js +1 -1
- package/dist/board/render/renderers/task-card-renderer.js +16 -13
- package/dist/board/render/renderers/text-card-renderer.js +1 -1
- package/dist/board/render/renderers/unknown-card-renderer.js +1 -1
- package/dist/board/render/renderers/video-card-renderer.js +1 -1
- package/dist/board/render/video-thumbnail.d.ts +16 -0
- package/dist/board/render/video-thumbnail.js +86 -0
- package/dist/board/task.d.ts +10 -5
- package/dist/board/task.js +159 -103
- package/dist/chunks/environment.d.ts +6 -6
- package/dist/chunks/environment.js +6 -6
- package/dist/chunks/http.d.ts +71 -5
- package/dist/chunks/http.js +1535 -167
- package/dist/chunks/transport.js +8 -1
- package/dist/chunks/websocket.d.ts +138 -2
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +245 -4
- package/dist/index.js +438 -788
- package/dist/protocol/dist/board-document.d.ts +155 -48
- package/dist/protocol/dist/board-document.js +40 -19
- package/dist/protocol/dist/board-node.d.ts +18 -0
- package/dist/protocol/dist/board-node.js +239 -0
- package/dist/protocol/dist/board-url.d.ts +12 -0
- package/dist/protocol/dist/board-url.js +83 -0
- package/dist/protocol/dist/board.d.ts +5 -0
- package/dist/protocol/dist/index.d.ts +2 -1
- package/dist/protocol/dist/index.js +2 -1
- package/dist/protocol/dist/provenance.js +1 -0
- package/docs/work-runtime-guide.md +7 -7
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region ../protocol/dist/board-url.js
|
|
3
|
+
const BOARD_REMOTE_URL_MAX_LENGTH = 4096;
|
|
4
|
+
function parseIpv4(host) {
|
|
5
|
+
const parts = host.split(".").map(Number);
|
|
6
|
+
return parts.length === 4 && parts.every((part) => Number.isInteger(part) && part >= 0 && part <= 255) ? parts : null;
|
|
7
|
+
}
|
|
8
|
+
function isBlockedIpv4(host) {
|
|
9
|
+
const parts = parseIpv4(host);
|
|
10
|
+
if (!parts) return false;
|
|
11
|
+
const [first, second, third] = parts;
|
|
12
|
+
if (first === 0 || first === 10 || first === 127) return true;
|
|
13
|
+
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
14
|
+
if (first === 169 && second === 254) return true;
|
|
15
|
+
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
16
|
+
if (first === 192 && second === 168) return true;
|
|
17
|
+
if (first === 192 && second === 0 && (third === 0 || third === 2)) return true;
|
|
18
|
+
if (first === 192 && second === 88 && third === 99) return true;
|
|
19
|
+
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
20
|
+
if (first === 198 && second === 51 && third === 100) return true;
|
|
21
|
+
if (first === 203 && second === 0 && third === 113) return true;
|
|
22
|
+
return first >= 224;
|
|
23
|
+
}
|
|
24
|
+
function expandIpv6(host) {
|
|
25
|
+
const [head, tail, extra] = host.toLowerCase().split("::");
|
|
26
|
+
if (extra !== void 0) return null;
|
|
27
|
+
const headParts = head ? head.split(":").filter(Boolean) : [];
|
|
28
|
+
const tailParts = tail ? tail.split(":").filter(Boolean) : [];
|
|
29
|
+
const missing = 8 - headParts.length - tailParts.length;
|
|
30
|
+
if (missing < 0 || tail === void 0 && missing !== 0) return null;
|
|
31
|
+
const parts = [
|
|
32
|
+
...headParts,
|
|
33
|
+
...Array.from({ length: missing }, () => "0"),
|
|
34
|
+
...tailParts
|
|
35
|
+
];
|
|
36
|
+
if (parts.length !== 8 || parts.some((part) => !/^[0-9a-f]{1,4}$/.test(part))) return null;
|
|
37
|
+
return parts.map((part) => part.padStart(4, "0"));
|
|
38
|
+
}
|
|
39
|
+
function isBlockedIpv6(host) {
|
|
40
|
+
const parts = expandIpv6(host);
|
|
41
|
+
if (!parts) return true;
|
|
42
|
+
if (parts.every((part) => part === "0000")) return true;
|
|
43
|
+
if (parts.slice(0, 7).every((part) => part === "0000") && parts[7] === "0001") return true;
|
|
44
|
+
if (parts.slice(0, 5).every((part) => part === "0000") && parts[5] === "ffff") {
|
|
45
|
+
const high = Number.parseInt(parts[6] ?? "0", 16);
|
|
46
|
+
const low = Number.parseInt(parts[7] ?? "0", 16);
|
|
47
|
+
return isBlockedIpv4(`${high >> 8}.${high & 255}.${low >> 8}.${low & 255}`);
|
|
48
|
+
}
|
|
49
|
+
if (parts.slice(0, 6).every((part) => part === "0000")) return true;
|
|
50
|
+
const first = Number.parseInt(parts[0] ?? "0", 16);
|
|
51
|
+
if ((first & 65024) === 64512) return true;
|
|
52
|
+
if ((first & 65472) === 65152 || (first & 65472) === 65216) return true;
|
|
53
|
+
if ((first & 65280) === 65280) return true;
|
|
54
|
+
return parts[0] === "2001" && parts[1] === "0db8";
|
|
55
|
+
}
|
|
56
|
+
function isBlockedHost(hostname) {
|
|
57
|
+
const host = hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
58
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal")) return true;
|
|
59
|
+
if (parseIpv4(host)) return isBlockedIpv4(host);
|
|
60
|
+
return host.includes(":") && isBlockedIpv6(host);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Normalize a browser-loadable public HTTP(S) URL. This blocks explicit local
|
|
64
|
+
* addresses; any future server-side fetcher must additionally validate DNS
|
|
65
|
+
* resolution to defend against rebinding.
|
|
66
|
+
*/
|
|
67
|
+
function normalizeBoardRemoteUrl(value) {
|
|
68
|
+
if (typeof value !== "string") return void 0;
|
|
69
|
+
const input = value.trim();
|
|
70
|
+
if (!input || input.length > 4096) return void 0;
|
|
71
|
+
try {
|
|
72
|
+
const url = new URL(input);
|
|
73
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return void 0;
|
|
74
|
+
if (url.username || url.password || isBlockedHost(url.hostname)) return;
|
|
75
|
+
const normalized = url.toString();
|
|
76
|
+
return normalized.length <= 4096 ? normalized : void 0;
|
|
77
|
+
} catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const BoardRemoteUrlSchema = z.string().max(BOARD_REMOTE_URL_MAX_LENGTH).refine((value) => normalizeBoardRemoteUrl(value) !== void 0, { message: "URL must be a public HTTP(S) URL without credentials" });
|
|
82
|
+
//#endregion
|
|
83
|
+
export { BOARD_REMOTE_URL_MAX_LENGTH, BoardRemoteUrlSchema, normalizeBoardRemoteUrl };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BoardCapability, BoardRenderCost } from "./board-constants.js";
|
|
2
2
|
import "./board-connection.js";
|
|
3
|
+
import "./board-node.js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
//#region ../protocol/dist/board.d.ts
|
|
5
6
|
declare const BOARD_EXTENSION: ".board";
|
|
@@ -176,6 +177,10 @@ type BoardDiagnostic = {
|
|
|
176
177
|
message: string;
|
|
177
178
|
path?: string;
|
|
178
179
|
adaptation?: Record<string, unknown>;
|
|
180
|
+
expected?: string;
|
|
181
|
+
received?: unknown;
|
|
182
|
+
allowedValues?: readonly string[];
|
|
183
|
+
coordinateSpace?: "frame-local" | "world";
|
|
179
184
|
};
|
|
180
185
|
type BoardValidationResult = {
|
|
181
186
|
valid: boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BoardCapability, BoardRenderCost } from "./board-constants.js";
|
|
2
2
|
import { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BoardConnection, BoardConnectionAnchor, BoardConnectionAnchorSchema, BoardConnectionDirection, BoardConnectionEndpoint, BoardConnectionEndpointSchema, BoardConnectionInput, BoardConnectionLine, BoardConnectionPatch, BoardConnectionPatchSchema, BoardConnectionRecord, BoardConnectionRouting, BoardConnectionRoutingConfig, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionSide, BoardConnectionStyle, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardRelationSchema, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, normalizeBoardConnectionStyle } from "./board-connection.js";
|
|
3
|
+
import { BOARD_COLOR_IDS, BOARD_GEO_KINDS, BoardColorId, BoardGeoKind, BoardNodeValidationDiagnostic } from "./board-node.js";
|
|
3
4
|
import { BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BoardAssetRef, BoardAssetRefSchema, BoardClip, BoardClipSchema, BoardDiagnostic, BoardEffect, BoardEffectSchema, BoardManifest, BoardManifestSchema, BoardNodeInput, BoardNodeRecord, BoardRecord, BoardSequence, BoardSequenceSchema, BoardTarget, BoardTargetSchema, BoardValidationResult, InvalidBoardFileError, isBoardPath, parseBoardManifest, serializeBoardManifest } from "./board.js";
|
|
4
5
|
import "./realtime/board-awareness.js";
|
|
5
6
|
import "./work.js";
|
|
6
7
|
import "./realtime/types.js";
|
|
7
|
-
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BoardAssetRef, BoardAssetRefSchema, BoardClip, BoardClipSchema, BoardConnection, BoardConnectionAnchor, BoardConnectionAnchorSchema, BoardConnectionDirection, BoardConnectionEndpoint, BoardConnectionEndpointSchema, BoardConnectionInput, BoardConnectionLine, BoardConnectionPatch, BoardConnectionPatchSchema, BoardConnectionRecord, BoardConnectionRouting, BoardConnectionRoutingConfig, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionSide, BoardConnectionStyle, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardDiagnostic, BoardEffect, BoardEffectSchema, BoardManifest, BoardManifestSchema, BoardNodeInput, BoardNodeRecord, BoardRecord, BoardRelationSchema, BoardSequence, BoardSequenceSchema, BoardTarget, BoardTargetSchema, BoardValidationResult, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, InvalidBoardFileError, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, isBoardPath, normalizeBoardConnectionStyle, parseBoardManifest, serializeBoardManifest };
|
|
8
|
+
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_COLOR_IDS, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BOARD_GEO_KINDS, BoardAssetRef, BoardAssetRefSchema, BoardClip, BoardClipSchema, BoardColorId, BoardConnection, BoardConnectionAnchor, BoardConnectionAnchorSchema, BoardConnectionDirection, BoardConnectionEndpoint, BoardConnectionEndpointSchema, BoardConnectionInput, BoardConnectionLine, BoardConnectionPatch, BoardConnectionPatchSchema, BoardConnectionRecord, BoardConnectionRouting, BoardConnectionRoutingConfig, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionSide, BoardConnectionStyle, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardDiagnostic, BoardEffect, BoardEffectSchema, BoardGeoKind, BoardManifest, BoardManifestSchema, BoardNodeInput, BoardNodeRecord, BoardNodeValidationDiagnostic, BoardRecord, BoardRelationSchema, BoardSequence, BoardSequenceSchema, BoardTarget, BoardTargetSchema, BoardValidationResult, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, InvalidBoardFileError, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, isBoardPath, normalizeBoardConnectionStyle, parseBoardManifest, serializeBoardManifest };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import "./board-constants.js";
|
|
2
2
|
import { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BoardConnectionAnchorSchema, BoardConnectionEndpointSchema, BoardConnectionPatchSchema, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardRelationSchema, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, normalizeBoardConnectionStyle } from "./board-connection.js";
|
|
3
3
|
import { BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BOARD_MANIFEST_KIND, BoardAssetRefSchema, BoardClipSchema, BoardEffectSchema, BoardKeyframeSchema, BoardManifestSchema, BoardNodeInputSchema, BoardSequenceSchema, BoardTargetSchema, InvalidBoardFileError, isBoardPath, parseBoardManifest, serializeBoardManifest } from "./board.js";
|
|
4
|
+
import { BOARD_COLOR_IDS, BOARD_GEO_KINDS, BOARD_NATIVE_NODE_TYPES, BoardColorIdSchema, BoardGeoKindSchema, validateBoardNodeInput } from "./board-node.js";
|
|
4
5
|
import { BoardAwarenessDrawPointSchema, BoardAwarenessFrameSchema, BoardAwarenessGestureSchema, BoardAwarenessNodePreviewSchema, BoardAwarenessPointSchema, BoardAwarenessStateUpdateSchema, BoardAwarenessUpdateSchema } from "./realtime/board-awareness.js";
|
|
5
6
|
import { COHUB_SOURCE_HEADER } from "./provenance.js";
|
|
6
7
|
import { RESERVED_PLATFORM_PATH_SEGMENTS } from "./public-identifiers.js";
|
|
7
8
|
import "./work-view-stats.js";
|
|
8
|
-
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BOARD_MANIFEST_KIND, BoardAssetRefSchema, BoardAwarenessDrawPointSchema, BoardAwarenessFrameSchema, BoardAwarenessGestureSchema, BoardAwarenessNodePreviewSchema, BoardAwarenessPointSchema, BoardAwarenessStateUpdateSchema, BoardAwarenessUpdateSchema, BoardClipSchema, BoardConnectionAnchorSchema, BoardConnectionEndpointSchema, BoardConnectionPatchSchema, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardEffectSchema, BoardKeyframeSchema, BoardManifestSchema, BoardNodeInputSchema, BoardRelationSchema, BoardSequenceSchema, BoardTargetSchema, COHUB_SOURCE_HEADER, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, InvalidBoardFileError, RESERVED_PLATFORM_PATH_SEGMENTS, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, isBoardPath, normalizeBoardConnectionStyle, parseBoardManifest, serializeBoardManifest };
|
|
9
|
+
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_COLOR_IDS, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BOARD_DOCUMENT_KIND, BOARD_EXTENSION, BOARD_GEO_KINDS, BOARD_MANIFEST_KIND, BOARD_NATIVE_NODE_TYPES, BoardAssetRefSchema, BoardAwarenessDrawPointSchema, BoardAwarenessFrameSchema, BoardAwarenessGestureSchema, BoardAwarenessNodePreviewSchema, BoardAwarenessPointSchema, BoardAwarenessStateUpdateSchema, BoardAwarenessUpdateSchema, BoardClipSchema, BoardColorIdSchema, BoardConnectionAnchorSchema, BoardConnectionEndpointSchema, BoardConnectionPatchSchema, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardEffectSchema, BoardGeoKindSchema, BoardKeyframeSchema, BoardManifestSchema, BoardNodeInputSchema, BoardRelationSchema, BoardSequenceSchema, BoardTargetSchema, COHUB_SOURCE_HEADER, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, InvalidBoardFileError, RESERVED_PLATFORM_PATH_SEGMENTS, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, isBoardPath, normalizeBoardConnectionStyle, parseBoardManifest, serializeBoardManifest, validateBoardNodeInput };
|
|
@@ -218,7 +218,7 @@ result needs `taskrun.view` (a work scope).
|
|
|
218
218
|
| Read task run detail | `client.tasks.get(taskRunId)` | `taskrun.view` | work |
|
|
219
219
|
| List viewer's spaces | `client.spaces.list()` | `user.space.list` | viewer |
|
|
220
220
|
| List viewer's sessions | `client.user.listSessions()` | `user.session.list` | viewer |
|
|
221
|
-
| Read viewer's
|
|
221
|
+
| Read viewer's activity | `client.user.getActivity()` | `user.usage.read` | viewer |
|
|
222
222
|
| Commerce: entitlements | `client.work.commerce.getEntitlements()` | *(runtime only, no scope)* | — |
|
|
223
223
|
| Commerce: consume credits | `client.work.commerce.consumeCredits()` | *(runtime only, no scope)* | — |
|
|
224
224
|
| Commerce: purchase | `client.work.commerce.purchase()` | *(runtime only, no scope)* | — |
|
|
@@ -280,7 +280,7 @@ when a deployment needs reproducible dependency updates.
|
|
|
280
280
|
### Environment detection — critical
|
|
281
281
|
|
|
282
282
|
The SDK defaults to **production**. A Work running on a dev/staging host
|
|
283
|
-
(e.g. `dev.cohub.
|
|
283
|
+
(e.g. `dev.cohub.live`, a `/dev/` path prefix) **must** pass `env: "dev"`
|
|
284
284
|
explicitly — browsers do not inject `ENV` like Node does. If you omit this,
|
|
285
285
|
your Work will call the production API while the runtime host expects dev,
|
|
286
286
|
causing silent auth failures.
|
|
@@ -304,7 +304,7 @@ iframe), pass the `work` option so the SDK can fall back to broker mode:
|
|
|
304
304
|
const client = createCohubClient({
|
|
305
305
|
env: isDevWork ? "dev" : "prod",
|
|
306
306
|
work: {
|
|
307
|
-
brokerOrigin: isDevWork ? "https://dev.cohub.
|
|
307
|
+
brokerOrigin: isDevWork ? "https://dev.cohub.live" : "https://cohub.live",
|
|
308
308
|
workId: "<your-published-work-id>",
|
|
309
309
|
},
|
|
310
310
|
});
|
|
@@ -332,7 +332,7 @@ All three values are known before publishing:
|
|
|
332
332
|
const client = createCohubClient({
|
|
333
333
|
env: isDevWork ? "dev" : "prod",
|
|
334
334
|
work: {
|
|
335
|
-
brokerOrigin: isDevWork ? "https://dev.cohub.
|
|
335
|
+
brokerOrigin: isDevWork ? "https://dev.cohub.live" : "https://cohub.live",
|
|
336
336
|
ownerUsername,
|
|
337
337
|
spaceSlug,
|
|
338
338
|
workSlug,
|
|
@@ -618,12 +618,12 @@ await client.auth.request({
|
|
|
618
618
|
});
|
|
619
619
|
const { sessions } = await client.user.listSessions({ limit: 20 });
|
|
620
620
|
|
|
621
|
-
// Read
|
|
621
|
+
// Read activity — needs user.usage.read
|
|
622
622
|
await client.auth.request({
|
|
623
623
|
scopes: ["user.usage.read"],
|
|
624
|
-
reason: "Show your
|
|
624
|
+
reason: "Show your activity.",
|
|
625
625
|
});
|
|
626
|
-
const
|
|
626
|
+
const activity = await client.user.getActivity({ days: 30 }); // last 30 days
|
|
627
627
|
```
|
|
628
628
|
|
|
629
629
|
### Commerce (`work.commerce`)
|