@dcl/sdk-commands 7.31.1-40962203448.commit-c68a75f → 7.31.1-41498712919.commit-5e4cada
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 +33 -0
- package/dist/commands/scene-insights/actions.d.ts +63 -0
- package/dist/commands/scene-insights/actions.js +106 -0
- package/dist/commands/scene-insights/actions.js.map +1 -0
- package/dist/commands/scene-insights/client.d.ts +51 -0
- package/dist/commands/scene-insights/client.js +294 -0
- package/dist/commands/scene-insights/client.js.map +1 -0
- package/dist/commands/scene-insights/format.d.ts +23 -0
- package/dist/commands/scene-insights/format.js +143 -0
- package/dist/commands/scene-insights/format.js.map +1 -0
- package/dist/commands/scene-insights/index.d.ts +51 -0
- package/dist/commands/scene-insights/index.js +315 -0
- package/dist/commands/scene-insights/index.js.map +1 -0
- package/dist/commands/scene-insights/inputs.d.ts +49 -0
- package/dist/commands/scene-insights/inputs.js +166 -0
- package/dist/commands/scene-insights/inputs.js.map +1 -0
- package/dist/commands/scene-insights/session-store.d.ts +43 -0
- package/dist/commands/scene-insights/session-store.js +132 -0
- package/dist/commands/scene-insights/session-store.js.map +1 -0
- package/dist/commands/scene-insights/session.d.ts +46 -0
- package/dist/commands/scene-insights/session.js +92 -0
- package/dist/commands/scene-insights/session.js.map +1 -0
- package/dist/commands/scene-insights/sign-in.d.ts +48 -0
- package/dist/commands/scene-insights/sign-in.js +125 -0
- package/dist/commands/scene-insights/sign-in.js.map +1 -0
- package/dist/commands/scene-insights/signature-validation.d.ts +6 -0
- package/dist/commands/scene-insights/signature-validation.js +100 -0
- package/dist/commands/scene-insights/signature-validation.js.map +1 -0
- package/dist/commands/scene-insights/types.d.ts +61 -0
- package/dist/commands/scene-insights/types.js +4 -0
- package/dist/commands/scene-insights/types.js.map +1 -0
- package/dist/commands/sdk-server-logs/index.d.ts +24 -0
- package/dist/commands/sdk-server-logs/index.js +313 -0
- package/dist/commands/sdk-server-logs/index.js.map +1 -0
- package/dist/commands/start/dcl-env.d.ts +16 -0
- package/dist/commands/start/dcl-env.js +51 -0
- package/dist/commands/start/dcl-env.js.map +1 -0
- package/dist/commands/start/index.d.ts +1 -0
- package/dist/commands/start/index.js +24 -6
- package/dist/commands/start/index.js.map +1 -1
- package/dist/commands/start/multiplayer-server.d.ts +35 -0
- package/dist/commands/start/multiplayer-server.js +244 -0
- package/dist/commands/start/multiplayer-server.js.map +1 -0
- package/dist/commands/start/server/routes.d.ts +1 -1
- package/dist/commands/start/server/routes.js +6 -2
- package/dist/commands/start/server/routes.js.map +1 -1
- package/dist/commands/start/server/runtime-env.d.ts +84 -0
- package/dist/commands/start/server/runtime-env.js +415 -0
- package/dist/commands/start/server/runtime-env.js.map +1 -0
- package/dist/commands/start/server/storage-service.d.ts +8 -0
- package/dist/commands/start/server/storage-service.js +243 -0
- package/dist/commands/start/server/storage-service.js.map +1 -0
- package/dist/commands/start/utils.d.ts +32 -0
- package/dist/commands/start/utils.js +72 -0
- package/dist/commands/start/utils.js.map +1 -1
- package/dist/commands/storage/env.d.ts +5 -0
- package/dist/commands/storage/env.js +86 -0
- package/dist/commands/storage/env.js.map +1 -0
- package/dist/commands/storage/index.d.ts +26 -0
- package/dist/commands/storage/index.js +142 -0
- package/dist/commands/storage/index.js.map +1 -0
- package/dist/commands/storage/player.d.ts +5 -0
- package/dist/commands/storage/player.js +143 -0
- package/dist/commands/storage/player.js.map +1 -0
- package/dist/commands/storage/scene.d.ts +5 -0
- package/dist/commands/storage/scene.js +105 -0
- package/dist/commands/storage/scene.js.map +1 -0
- package/dist/commands/storage/shared.d.ts +62 -0
- package/dist/commands/storage/shared.js +260 -0
- package/dist/commands/storage/shared.js.map +1 -0
- package/dist/commands/storage/types.d.ts +56 -0
- package/dist/commands/storage/types.js +23 -0
- package/dist/commands/storage/types.js.map +1 -0
- package/dist/components/analytics.d.ts +75 -1
- package/dist/components/analytics.js +3 -2
- package/dist/components/analytics.js.map +1 -1
- package/dist/logic/auth-chain-headers.d.ts +11 -0
- package/dist/logic/auth-chain-headers.js +25 -0
- package/dist/logic/auth-chain-headers.js.map +1 -0
- package/dist/logic/bundle.js +31 -0
- package/dist/logic/bundle.js.map +1 -1
- package/dist/logic/config.d.ts +1 -0
- package/dist/logic/config.js +7 -0
- package/dist/logic/config.js.map +1 -1
- package/dist/logic/error.d.ts +1 -1
- package/dist/logic/error.js.map +1 -1
- package/dist/logic/exec.d.ts +1 -0
- package/dist/logic/exec.js +2 -2
- package/dist/logic/exec.js.map +1 -1
- package/dist/logic/lang.js +1 -1
- package/dist/logic/lsd-realm.d.ts +5 -0
- package/dist/logic/lsd-realm.js +28 -0
- package/dist/logic/lsd-realm.js.map +1 -0
- package/dist/logic/project-files.js +2 -0
- package/dist/logic/project-files.js.map +1 -1
- package/dist/logic/scene-validations.d.ts +8 -1
- package/dist/logic/scene-validations.js.map +1 -1
- package/dist/run-command.js +3 -2
- package/dist/run-command.js.map +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getSession = getSession;
|
|
7
|
+
exports.isLinkerOrigin = isLinkerOrigin;
|
|
8
|
+
exports.setSignInRoutes = setSignInRoutes;
|
|
9
|
+
const fp_future_1 = __importDefault(require("fp-future"));
|
|
10
|
+
const signature_validation_1 = require("./signature-validation");
|
|
11
|
+
const error_1 = require("../../logic/error");
|
|
12
|
+
const routes_1 = require("../../linker-dapp/routes");
|
|
13
|
+
const run_dapp_1 = require("../../run-dapp");
|
|
14
|
+
const session_1 = require("./session");
|
|
15
|
+
const session_store_1 = require("./session-store");
|
|
16
|
+
/**
|
|
17
|
+
* Gets a session to sign requests with, in order:
|
|
18
|
+
* 1. `DCL_PRIVATE_KEY`, signed locally and never saved;
|
|
19
|
+
* 2. the saved session, while it is valid (unless `forceNew`);
|
|
20
|
+
* 3. a new browser signature through the linker dApp, which is then saved.
|
|
21
|
+
*
|
|
22
|
+
* @throws CliError `SCENE_INSIGHTS_SESSION_REQUIRED` when it would have to open the browser but
|
|
23
|
+
* nobody is there to sign (an agent or script): `scene-insights login` has to be run first.
|
|
24
|
+
* @throws CliError `SCENE_INSIGHTS_UNSAFE_SESSION_FILE` when git tracks the session file, before
|
|
25
|
+
* asking for a signature.
|
|
26
|
+
*/
|
|
27
|
+
async function getSession(components, label, options) {
|
|
28
|
+
if (process.env.DCL_PRIVATE_KEY) {
|
|
29
|
+
return { session: (0, session_1.createSessionFromPrivateKey)(process.env.DCL_PRIVATE_KEY), source: 'private-key' };
|
|
30
|
+
}
|
|
31
|
+
const provider = (0, signature_validation_1.createInsightsProvider)(options.targetUrl);
|
|
32
|
+
if (!options.forceNew) {
|
|
33
|
+
const saved = await (0, session_store_1.loadSession)(components, options.file, Date.now(), provider);
|
|
34
|
+
if (saved)
|
|
35
|
+
return { session: saved.session, source: 'saved' };
|
|
36
|
+
if (!options.interactive) {
|
|
37
|
+
throw new error_1.CliError('SCENE_INSIGHTS_SESSION_REQUIRED', 'No signed-in session. Ask the user to run "sdk-commands scene-insights login" (it opens the browser to sign once, for 12 hours), or set DCL_PRIVATE_KEY');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// before the browser opens: nobody should sign a session that can't be saved
|
|
41
|
+
await (0, session_store_1.assertSessionFileIsNotTracked)(options.file);
|
|
42
|
+
const ephemeralIdentity = (0, session_1.createEphemeralIdentity)();
|
|
43
|
+
const expiration = new Date(Date.now() + session_1.SESSION_MINUTES * 60 * 1000);
|
|
44
|
+
const message = (0, session_1.getInsightsSessionMessage)(ephemeralIdentity.address, expiration);
|
|
45
|
+
const signed = (0, fp_future_1.default)();
|
|
46
|
+
// A browser callback can fail while runDapp is still starting; await signed below still propagates it.
|
|
47
|
+
void signed.catch(() => undefined);
|
|
48
|
+
const { router: commonRouter } = (0, routes_1.setRoutes)(components, {
|
|
49
|
+
rootCID: message,
|
|
50
|
+
baseParcel: label.parcel ?? '0,0',
|
|
51
|
+
parcels: [label.parcel ?? '0,0'],
|
|
52
|
+
skipValidations: true,
|
|
53
|
+
debug: !!process.env.DEBUG,
|
|
54
|
+
isWorld: !!label.world,
|
|
55
|
+
world: label.label,
|
|
56
|
+
targetUrl: options.targetUrl,
|
|
57
|
+
action: 'view-logs'
|
|
58
|
+
});
|
|
59
|
+
const router = setSignInRoutes(commonRouter, ephemeralIdentity.address, message, (chain) => signed.resolve(chain), provider, (error) => signed.reject(error));
|
|
60
|
+
components.logger.info(`Sign read-only access to logs, metrics and executions for the next ${session_1.SESSION_MINUTES / 60} hours`);
|
|
61
|
+
components.logger.info('This credential works across Insights environments sharing the insights service identifier where the wallet signatures verify on the configured network.');
|
|
62
|
+
const { program } = await (0, run_dapp_1.runDapp)(components, router, { ...options.linkOptions, uri: '/' });
|
|
63
|
+
let validated;
|
|
64
|
+
try {
|
|
65
|
+
validated = await signed;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
// Cleanup must not replace a signature validation error.
|
|
69
|
+
await program.stop().catch(() => undefined);
|
|
70
|
+
}
|
|
71
|
+
const { authChain, scope } = validated;
|
|
72
|
+
expiration.setTime(Math.min(expiration.getTime(), scope.expiration));
|
|
73
|
+
await (0, session_store_1.saveSession)(components, options.file, ephemeralIdentity, expiration, authChain);
|
|
74
|
+
return { session: (0, session_1.createSessionFromChain)(ephemeralIdentity, expiration, authChain), source: 'signed' };
|
|
75
|
+
}
|
|
76
|
+
const LOOPBACK_HOSTNAMES = ['localhost', '127.0.0.1', '[::1]'];
|
|
77
|
+
/**
|
|
78
|
+
* Whether a request comes from the linker dApp's own page. The browser sets Origin and Host, so
|
|
79
|
+
* another page open in it (or a rebinding domain) can't pass for the dApp.
|
|
80
|
+
*/
|
|
81
|
+
function isLinkerOrigin(origin, host) {
|
|
82
|
+
if (!origin || !host)
|
|
83
|
+
return false;
|
|
84
|
+
try {
|
|
85
|
+
const url = new URL(origin);
|
|
86
|
+
return url.host === host && LOOPBACK_HOSTNAMES.includes(url.hostname);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The linker dApp posts `view-logs` signatures to `/api/logs`. Only its own page is accepted: any
|
|
94
|
+
* other page could sign the message with its own wallet and sign the CLI in as that address.
|
|
95
|
+
*/
|
|
96
|
+
function setSignInRoutes(router, ephemeralAddress, message, onSigned, provider, onError) {
|
|
97
|
+
router.post('/api/logs', async (ctx) => {
|
|
98
|
+
if (!isLinkerOrigin(ctx.request.headers.get('origin'), ctx.request.headers.get('host'))) {
|
|
99
|
+
return { status: 403, body: { success: false, error: 'Sign from the page the CLI opened on localhost' } };
|
|
100
|
+
}
|
|
101
|
+
const value = (await ctx.request.json());
|
|
102
|
+
try {
|
|
103
|
+
if (!value.address || !Array.isArray(value.authChain)) {
|
|
104
|
+
throw new error_1.CliError('SCENE_INSIGHTS_INVALID_SIGNATURE', 'The wallet did not sign the insights session');
|
|
105
|
+
}
|
|
106
|
+
const delegation = await (0, session_1.delegationFromLinkerChain)(value.authChain, ephemeralAddress, message, Date.now(), provider);
|
|
107
|
+
// answer the dApp before its server is stopped
|
|
108
|
+
setTimeout(() => onSigned(delegation), 100);
|
|
109
|
+
return { body: { success: true } };
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
if (e instanceof error_1.CliError &&
|
|
113
|
+
(e.name === 'SCENE_INSIGHTS_UNAVAILABLE' || e.name === 'SCENE_INSIGHTS_INVALID_ARGUMENT')) {
|
|
114
|
+
setTimeout(() => onError?.(e), 100);
|
|
115
|
+
return {
|
|
116
|
+
status: e.name === 'SCENE_INSIGHTS_UNAVAILABLE' ? 503 : 400,
|
|
117
|
+
body: { success: false, error: e.message }
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return { status: 400, body: { success: false, error: e.message } };
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return router;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=sign-in.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-in.js","sourceRoot":"","sources":["../../../src/commands/scene-insights/sign-in.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AuthChain, Authenticator } from '@dcl/crypto';
|
|
2
|
+
import { AuthChainProvider } from '@dcl/crypto-middleware';
|
|
3
|
+
/** Select RPC from trusted CLI configuration, never from the wallet response. */
|
|
4
|
+
export declare function createInsightsProvider(target: string): AuthChainProvider;
|
|
5
|
+
/** Validate a saved or newly signed delegation without unbounded RPC fallback work. */
|
|
6
|
+
export declare function validateInsightsSignature(expected: string, chain: AuthChain, now?: number, provider?: AuthChainProvider): ReturnType<typeof Authenticator.validateSignature>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createInsightsProvider = createInsightsProvider;
|
|
4
|
+
exports.validateInsightsSignature = validateInsightsSignature;
|
|
5
|
+
const crypto_1 = require("@dcl/crypto");
|
|
6
|
+
const crypto_middleware_1 = require("@dcl/crypto-middleware");
|
|
7
|
+
const error_1 = require("../../logic/error");
|
|
8
|
+
/** Select RPC from trusted CLI configuration, never from the wallet response. */
|
|
9
|
+
function createInsightsProvider(target) {
|
|
10
|
+
let provider;
|
|
11
|
+
return {
|
|
12
|
+
sendAsync(payload, callback, signal) {
|
|
13
|
+
if (!provider) {
|
|
14
|
+
const hostname = new URL(target).hostname;
|
|
15
|
+
const network = hostname === 'decentraland.zone' || hostname.endsWith('.decentraland.zone')
|
|
16
|
+
? 'sepolia'
|
|
17
|
+
: hostname === 'decentraland.org' || hostname.endsWith('.decentraland.org')
|
|
18
|
+
? 'mainnet'
|
|
19
|
+
: undefined;
|
|
20
|
+
const url = network
|
|
21
|
+
? `https://rpc.decentraland.org/${network}?project=sdk-commands`
|
|
22
|
+
: process.env.DCL_SCENE_INSIGHTS_RPC_URL;
|
|
23
|
+
if (!url) {
|
|
24
|
+
callback(new error_1.CliError('SCENE_INSIGHTS_INVALID_ARGUMENT', 'Set DCL_SCENE_INSIGHTS_RPC_URL for contract wallets on a custom Insights target'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
provider = (0, crypto_middleware_1.createSignatureRpcProvider)(url);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
callback(new error_1.CliError('SCENE_INSIGHTS_INVALID_ARGUMENT', 'DCL_SCENE_INSIGHTS_RPC_URL must be an HTTP(S) URL without user information'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
provider.sendAsync(payload, callback, signal);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Validate a saved or newly signed delegation without unbounded RPC fallback work. */
|
|
40
|
+
async function validateInsightsSignature(expected, chain, now = Date.now(), provider) {
|
|
41
|
+
if (chain.length < 2 || chain.length > 10)
|
|
42
|
+
return { ok: false, message: 'Invalid auth chain length' };
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
let rejectValidation = () => undefined;
|
|
45
|
+
let invalidate = () => undefined;
|
|
46
|
+
const interrupted = new Promise((resolve, reject) => {
|
|
47
|
+
invalidate = resolve;
|
|
48
|
+
rejectValidation = reject;
|
|
49
|
+
});
|
|
50
|
+
const interrupt = (error) => {
|
|
51
|
+
if (!controller.signal.aborted) {
|
|
52
|
+
rejectValidation(error);
|
|
53
|
+
controller.abort();
|
|
54
|
+
}
|
|
55
|
+
return error;
|
|
56
|
+
};
|
|
57
|
+
let calls = 0;
|
|
58
|
+
const bounded = provider && {
|
|
59
|
+
sendAsync(payload, callback) {
|
|
60
|
+
if (controller.signal.aborted) {
|
|
61
|
+
callback(new Error('Signature validation stopped'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (++calls > 16) {
|
|
65
|
+
const message = 'Signature validation exceeded its RPC budget';
|
|
66
|
+
invalidate({ ok: false, message });
|
|
67
|
+
controller.abort();
|
|
68
|
+
callback(new Error(message));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
provider.sendAsync(payload, (error, response) => {
|
|
73
|
+
if (error) {
|
|
74
|
+
callback(interrupt(error instanceof error_1.CliError
|
|
75
|
+
? error
|
|
76
|
+
: new error_1.CliError('SCENE_INSIGHTS_UNAVAILABLE', 'Contract-wallet signature verification unavailable; retry shortly')));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
callback(null, response);
|
|
80
|
+
}
|
|
81
|
+
}, controller.signal);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
callback(interrupt(new error_1.CliError('SCENE_INSIGHTS_UNAVAILABLE', 'Contract-wallet signature verification unavailable; retry shortly')));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const timer = setTimeout(() => interrupt(new error_1.CliError('SCENE_INSIGHTS_UNAVAILABLE', 'Contract-wallet signature verification timed out; retry shortly')), 15000);
|
|
89
|
+
try {
|
|
90
|
+
return await Promise.race([
|
|
91
|
+
crypto_1.Authenticator.validateSignature(expected, chain, bounded ?? null, now, { service: 'insights' }),
|
|
92
|
+
interrupted
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
controller.abort();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=signature-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-validation.js","sourceRoot":"","sources":["../../../src/commands/scene-insights/signature-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface LogEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
5
|
+
source: 'scene' | 'engine' | 'stderr' | 'unknown';
|
|
6
|
+
executionId: string;
|
|
7
|
+
entityId: string;
|
|
8
|
+
message: string;
|
|
9
|
+
truncated: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface LogSearchResult {
|
|
12
|
+
sceneKey: string;
|
|
13
|
+
logs: LogEntry[];
|
|
14
|
+
nextCursor: string | null;
|
|
15
|
+
retentionStart: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Execution {
|
|
18
|
+
executionId: string;
|
|
19
|
+
entityId: string;
|
|
20
|
+
firstSeen: string;
|
|
21
|
+
lastSeen: string;
|
|
22
|
+
instanceId: string;
|
|
23
|
+
logLines: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ExecutionsResult {
|
|
26
|
+
sceneKey: string;
|
|
27
|
+
executions: Execution[];
|
|
28
|
+
retentionStart: string;
|
|
29
|
+
}
|
|
30
|
+
export interface MetricName {
|
|
31
|
+
name: string;
|
|
32
|
+
type: 'gauge' | 'sum';
|
|
33
|
+
unit: string;
|
|
34
|
+
}
|
|
35
|
+
export interface MetricNamesResult {
|
|
36
|
+
sceneKey: string;
|
|
37
|
+
from: string;
|
|
38
|
+
to: string;
|
|
39
|
+
metrics: MetricName[];
|
|
40
|
+
}
|
|
41
|
+
export interface GaugePoint {
|
|
42
|
+
t: string;
|
|
43
|
+
avg: number | null;
|
|
44
|
+
max: number | null;
|
|
45
|
+
}
|
|
46
|
+
export interface SumPoint {
|
|
47
|
+
t: string;
|
|
48
|
+
sum: number | null;
|
|
49
|
+
rate: number | null;
|
|
50
|
+
}
|
|
51
|
+
export interface MetricSeries {
|
|
52
|
+
sceneKey: string;
|
|
53
|
+
metric: string;
|
|
54
|
+
type: 'gauge' | 'sum' | null;
|
|
55
|
+
unit: string;
|
|
56
|
+
rateUnit?: string;
|
|
57
|
+
stepSeconds: number;
|
|
58
|
+
from: string;
|
|
59
|
+
to: string;
|
|
60
|
+
points: Array<GaugePoint | SumPoint>;
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/commands/scene-insights/types.ts"],"names":[],"mappings":";AAAA,kFAAkF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Result } from 'arg';
|
|
2
|
+
import { CliComponents } from '../../components';
|
|
3
|
+
interface Options {
|
|
4
|
+
args: Result<typeof args>;
|
|
5
|
+
components: CliComponents;
|
|
6
|
+
}
|
|
7
|
+
export declare const args: {
|
|
8
|
+
'--help': BooleanConstructor;
|
|
9
|
+
'-h': string;
|
|
10
|
+
'--dir': StringConstructor;
|
|
11
|
+
'--world': StringConstructor;
|
|
12
|
+
'-w': string;
|
|
13
|
+
'--position': StringConstructor;
|
|
14
|
+
'--target': StringConstructor;
|
|
15
|
+
'-t': string;
|
|
16
|
+
'--port': NumberConstructor;
|
|
17
|
+
'-p': string;
|
|
18
|
+
'--https': BooleanConstructor;
|
|
19
|
+
'--no-browser': BooleanConstructor;
|
|
20
|
+
'-b': string;
|
|
21
|
+
};
|
|
22
|
+
export declare function help(options: Options): void;
|
|
23
|
+
export declare function main(options: Options): Promise<void>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.args = void 0;
|
|
7
|
+
exports.help = help;
|
|
8
|
+
exports.main = main;
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
const fp_future_1 = __importDefault(require("fp-future"));
|
|
11
|
+
const crypto_1 = require("@dcl/crypto");
|
|
12
|
+
const crypto_2 = require("@dcl/crypto/dist/crypto");
|
|
13
|
+
const eth_connect_1 = require("eth-connect");
|
|
14
|
+
const args_1 = require("../../logic/args");
|
|
15
|
+
const error_1 = require("../../logic/error");
|
|
16
|
+
const beautiful_logs_1 = require("../../logic/beautiful-logs");
|
|
17
|
+
const account_1 = require("../../logic/account");
|
|
18
|
+
const auth_chain_headers_1 = require("../../logic/auth-chain-headers");
|
|
19
|
+
const routes_1 = require("../../linker-dapp/routes");
|
|
20
|
+
const run_dapp_1 = require("../../run-dapp");
|
|
21
|
+
const workspace_validations_1 = require("../../logic/workspace-validations");
|
|
22
|
+
const scene_validations_1 = require("../../logic/scene-validations");
|
|
23
|
+
exports.args = (0, args_1.declareArgs)({
|
|
24
|
+
'--help': Boolean,
|
|
25
|
+
'-h': '--help',
|
|
26
|
+
'--dir': String,
|
|
27
|
+
'--world': String,
|
|
28
|
+
'-w': '--world',
|
|
29
|
+
'--position': String,
|
|
30
|
+
'--target': String,
|
|
31
|
+
'-t': '--target',
|
|
32
|
+
'--port': Number,
|
|
33
|
+
'-p': '--port',
|
|
34
|
+
'--https': Boolean,
|
|
35
|
+
'--no-browser': Boolean,
|
|
36
|
+
'-b': '--no-browser'
|
|
37
|
+
});
|
|
38
|
+
const DEFAULT_SERVER = 'https://multiplayer-server.decentraland.org';
|
|
39
|
+
function help(options) {
|
|
40
|
+
options.components.logger.log(`
|
|
41
|
+
Usage: 'sdk-commands sdk-server-logs [options]'
|
|
42
|
+
Streams real-time logs from the multiplayer server.
|
|
43
|
+
Runs from any directory when --world and/or --position are provided.
|
|
44
|
+
Falls back to scene.json (worldConfiguration.name) when neither is passed.
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
-h, --help Displays complete help
|
|
48
|
+
-w, --world [name] World name. When omitted, --position targets Genesis City.
|
|
49
|
+
--position [x,y] Parcel coordinates. Optional for worlds (selects a scene
|
|
50
|
+
in a multi-scene world). Required for Genesis City.
|
|
51
|
+
-t, --target [URL] Target multiplayer server URL (default: ${DEFAULT_SERVER})
|
|
52
|
+
--dir [path] Path to the project directory (used with scene.json fallback)
|
|
53
|
+
-p, --port [port] Select a custom port for the linker dApp
|
|
54
|
+
-b, --no-browser Do not open a new browser window
|
|
55
|
+
--https Use HTTPS for the linker dApp
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
- View logs using the world in the current scene.json:
|
|
59
|
+
$ sdk-commands sdk-server-logs
|
|
60
|
+
|
|
61
|
+
- View logs for a world from any directory:
|
|
62
|
+
$ sdk-commands sdk-server-logs --world myworld.dcl.eth
|
|
63
|
+
|
|
64
|
+
- View logs for a specific scene in a multi-scene world:
|
|
65
|
+
$ sdk-commands sdk-server-logs --world myworld.dcl.eth --position 10,10
|
|
66
|
+
|
|
67
|
+
- View logs for a Genesis City scene (use =value form for negative coords):
|
|
68
|
+
$ sdk-commands sdk-server-logs --position=-125,-96
|
|
69
|
+
|
|
70
|
+
- Connect to a custom server:
|
|
71
|
+
$ sdk-commands sdk-server-logs --world myworld --target https://multiplayer-server.decentraland.zone
|
|
72
|
+
|
|
73
|
+
- Use private key for authentication (no browser):
|
|
74
|
+
$ DCL_PRIVATE_KEY=0x... sdk-commands sdk-server-logs --world myworld
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
function setServerLogsRoutes(router, components, awaitResponse, signCallback) {
|
|
78
|
+
const { logger } = components;
|
|
79
|
+
const resolveLinkerPromise = () => setTimeout(() => awaitResponse.resolve(), 100);
|
|
80
|
+
const rejectLinkerPromise = (e) => setTimeout(() => awaitResponse.reject(e), 100);
|
|
81
|
+
router.post('/api/logs', async (ctx) => {
|
|
82
|
+
const value = (await ctx.request.json());
|
|
83
|
+
if (!value.address || !value.authChain) {
|
|
84
|
+
const errorMessage = `Invalid payload: ${Object.keys(value).join(' - ')}`;
|
|
85
|
+
logger.error(errorMessage);
|
|
86
|
+
resolveLinkerPromise();
|
|
87
|
+
return { status: 400, body: { success: false, error: errorMessage } };
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await signCallback(value);
|
|
91
|
+
resolveLinkerPromise();
|
|
92
|
+
return { body: { success: true } };
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
rejectLinkerPromise(e);
|
|
96
|
+
return { status: 400, body: { success: false, error: e.message } };
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return router;
|
|
100
|
+
}
|
|
101
|
+
async function getAddressAndSignature(components, awaitResponse, payload, displayLabel, targetUrl, linkOptions, signCallback) {
|
|
102
|
+
// If DCL_PRIVATE_KEY is set, sign directly without the linker dapp
|
|
103
|
+
if (process.env.DCL_PRIVATE_KEY) {
|
|
104
|
+
const wallet = (0, account_1.createWallet)(process.env.DCL_PRIVATE_KEY);
|
|
105
|
+
const authChain = crypto_1.Authenticator.createSimpleAuthChain(payload, wallet.address, (0, crypto_2.ethSign)((0, eth_connect_1.hexToBytes)(wallet.privateKey), payload));
|
|
106
|
+
const linkerResponse = { authChain, address: wallet.address };
|
|
107
|
+
await signCallback(linkerResponse);
|
|
108
|
+
awaitResponse.resolve();
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
// Use linker dapp for signing - pass the payload as the rootCID
|
|
112
|
+
const { router: commonRouter } = (0, routes_1.setRoutes)(components, {
|
|
113
|
+
rootCID: payload,
|
|
114
|
+
baseParcel: '0,0',
|
|
115
|
+
parcels: ['0,0'],
|
|
116
|
+
skipValidations: true,
|
|
117
|
+
debug: !!process.env.DEBUG,
|
|
118
|
+
isWorld: true,
|
|
119
|
+
world: displayLabel,
|
|
120
|
+
targetUrl,
|
|
121
|
+
action: 'view-logs'
|
|
122
|
+
});
|
|
123
|
+
const router = setServerLogsRoutes(commonRouter, components, awaitResponse, signCallback);
|
|
124
|
+
components.logger.info('You need to sign to access server logs');
|
|
125
|
+
const { program } = await (0, run_dapp_1.runDapp)(components, router, { ...linkOptions, uri: `/` });
|
|
126
|
+
return { program };
|
|
127
|
+
}
|
|
128
|
+
async function streamLogs(components, logsUrl, authHeaders) {
|
|
129
|
+
const { logger, fetch: fetchComponent } = components;
|
|
130
|
+
logger.info('\nConnecting to server logs...');
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetchComponent.fetch(logsUrl, {
|
|
133
|
+
method: 'GET',
|
|
134
|
+
headers: {
|
|
135
|
+
...authHeaders,
|
|
136
|
+
Accept: 'text/event-stream'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
const errorText = await response.text();
|
|
141
|
+
throw new Error(`Server returned ${response.status}: ${errorText}`);
|
|
142
|
+
}
|
|
143
|
+
const contentType = response.headers.get('content-type') || '';
|
|
144
|
+
if (!contentType.includes('text/event-stream') && !contentType.includes('application/stream')) {
|
|
145
|
+
throw new Error('Server does not support SSE streaming');
|
|
146
|
+
}
|
|
147
|
+
logger.info('Streaming logs in real-time (press CTRL+C to stop)');
|
|
148
|
+
if (response.body) {
|
|
149
|
+
const decoder = new TextDecoder();
|
|
150
|
+
let buffer = '';
|
|
151
|
+
for await (const chunk of response.body) {
|
|
152
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
153
|
+
const lines = buffer.split('\n');
|
|
154
|
+
buffer = lines.pop() || '';
|
|
155
|
+
for (const line of lines) {
|
|
156
|
+
if (line.trim()) {
|
|
157
|
+
formatAndPrintLog(logger, line);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
logger.info('\n======================= End Scene Logs =======================');
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
(0, beautiful_logs_1.printError)(logger, 'Failed to stream logs:', e);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function formatAndPrintLog(logger, log) {
|
|
170
|
+
// If log is a string, try to parse as JSON
|
|
171
|
+
let logObj = log;
|
|
172
|
+
if (typeof log === 'string') {
|
|
173
|
+
try {
|
|
174
|
+
logObj = JSON.parse(log);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// If not JSON, just print the string
|
|
178
|
+
logger.log(log);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Extract common log fields
|
|
183
|
+
const timestamp = logObj.timestamp || logObj.time || new Date().toISOString();
|
|
184
|
+
const level = (logObj.level || logObj.severity || 'INFO').toUpperCase();
|
|
185
|
+
const message = logObj.message || logObj.msg || JSON.stringify(logObj);
|
|
186
|
+
// Format timestamp
|
|
187
|
+
const date = new Date(timestamp);
|
|
188
|
+
const formattedTime = date.toISOString().replace('T', ' ').substring(0, 19);
|
|
189
|
+
// Format the log message with timestamp
|
|
190
|
+
const formattedMessage = `[${formattedTime}] [${level}] ${message}`;
|
|
191
|
+
// Map log levels to logger methods
|
|
192
|
+
const normalizedLevel = level.toLowerCase();
|
|
193
|
+
switch (normalizedLevel) {
|
|
194
|
+
case 'error':
|
|
195
|
+
logger.error(formattedMessage);
|
|
196
|
+
break;
|
|
197
|
+
case 'warn':
|
|
198
|
+
case 'warning':
|
|
199
|
+
logger.warn(formattedMessage);
|
|
200
|
+
break;
|
|
201
|
+
case 'debug':
|
|
202
|
+
logger.debug(formattedMessage);
|
|
203
|
+
break;
|
|
204
|
+
case 'info':
|
|
205
|
+
case 'trace':
|
|
206
|
+
default:
|
|
207
|
+
logger.info(formattedMessage);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
// If there are additional fields, print them
|
|
211
|
+
const additionalFields = Object.keys(logObj).filter((key) => !['timestamp', 'time', 'level', 'severity', 'message', 'msg'].includes(key));
|
|
212
|
+
if (additionalFields.length > 0) {
|
|
213
|
+
const additional = {};
|
|
214
|
+
additionalFields.forEach((key) => {
|
|
215
|
+
additional[key] = logObj[key];
|
|
216
|
+
});
|
|
217
|
+
logger.log(` ${JSON.stringify(additional)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function normalizePosition(raw) {
|
|
221
|
+
const match = raw.trim().match(/^(-?\d+)\s*,\s*(-?\d+)$/);
|
|
222
|
+
if (!match) {
|
|
223
|
+
throw new error_1.CliError('SERVER_LOGS_INVALID_POSITION', `Invalid --position "${raw}"; expected "x,y" with integer coordinates`);
|
|
224
|
+
}
|
|
225
|
+
return `${parseInt(match[1], 10)},${parseInt(match[2], 10)}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Build the HTTP request shape for the multiplayer-server `/logs` endpoint.
|
|
229
|
+
*
|
|
230
|
+
* Every call targets the same `/logs` route; the server picks the scene from
|
|
231
|
+
* the signed metadata:
|
|
232
|
+
* - worlds → `sceneId: "<world>.dcl.eth"` (+ `parcel` for multi-scene worlds)
|
|
233
|
+
* - Genesis → `parcel: "x,y"` only
|
|
234
|
+
*
|
|
235
|
+
* `parcel` and `realmName` are always set. `realmName` defaults to `"main"` for
|
|
236
|
+
* Genesis, matching the convention used by other signed-fetch emitters
|
|
237
|
+
* (hammurabi worker, unity explorer, kernel).
|
|
238
|
+
*/
|
|
239
|
+
function buildLogsRequest(baseURL, world, position) {
|
|
240
|
+
const metadata = JSON.stringify({
|
|
241
|
+
parcel: position ?? '0,0',
|
|
242
|
+
realmName: world ? `${world}.dcl.eth` : 'main',
|
|
243
|
+
...(world && { sceneId: `${world}.dcl.eth` })
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
logsUrl: `${baseURL}/logs`,
|
|
247
|
+
pathname: '/logs',
|
|
248
|
+
metadata
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async function main(options) {
|
|
252
|
+
const { logger } = options.components;
|
|
253
|
+
const projectRoot = (0, path_1.resolve)(process.cwd(), options.args['--dir'] || '.');
|
|
254
|
+
const positionArg = options.args['--position'];
|
|
255
|
+
const worldArg = options.args['--world'];
|
|
256
|
+
const position = positionArg ? normalizePosition(positionArg) : undefined;
|
|
257
|
+
let world;
|
|
258
|
+
if (worldArg) {
|
|
259
|
+
world = worldArg.replace(/\.dcl\.eth$/i, '');
|
|
260
|
+
}
|
|
261
|
+
else if (!position) {
|
|
262
|
+
// no --world nor --position: fall back to scene.json in the current directory
|
|
263
|
+
await (0, workspace_validations_1.getValidWorkspace)(options.components, projectRoot);
|
|
264
|
+
const sceneJson = await (0, scene_validations_1.getValidSceneJson)(options.components, projectRoot);
|
|
265
|
+
const worldName = sceneJson.worldConfiguration?.name;
|
|
266
|
+
if (!worldName) {
|
|
267
|
+
throw new error_1.CliError('SERVER_LOGS_MISSING_WORLD', 'Provide --world (with optional --position) for worlds, or --position alone for Genesis City scenes. ' +
|
|
268
|
+
'Alternatively, run from a project whose scene.json defines worldConfiguration.name.');
|
|
269
|
+
}
|
|
270
|
+
world = worldName.replace(/\.dcl\.eth$/i, '');
|
|
271
|
+
}
|
|
272
|
+
const displayLabel = world
|
|
273
|
+
? position
|
|
274
|
+
? `${world}.dcl.eth ${position}`
|
|
275
|
+
: `${world}.dcl.eth`
|
|
276
|
+
: `Genesis City ${position}`;
|
|
277
|
+
logger.info(`Viewing logs for ${displayLabel}`);
|
|
278
|
+
const baseURL = options.args['--target'] || DEFAULT_SERVER;
|
|
279
|
+
const { logsUrl, pathname, metadata } = buildLogsRequest(baseURL, world, position);
|
|
280
|
+
// Linker dApp options
|
|
281
|
+
const linkerPort = options.args['--port'];
|
|
282
|
+
const openBrowser = !options.args['--no-browser'];
|
|
283
|
+
const isHttps = !!options.args['--https'];
|
|
284
|
+
const linkOptions = { linkerPort, openBrowser, isHttps };
|
|
285
|
+
const awaitResponse = (0, fp_future_1.default)();
|
|
286
|
+
const timestamp = String(Date.now());
|
|
287
|
+
// Build the payload to sign: method:path:timestamp:metadata
|
|
288
|
+
const payload = ['get', pathname, timestamp, metadata].join(':').toLowerCase();
|
|
289
|
+
let authHeaders = {};
|
|
290
|
+
const { program } = await getAddressAndSignature(options.components, awaitResponse, payload, displayLabel, baseURL, linkOptions, async (linkerResponse) => {
|
|
291
|
+
authHeaders = (0, auth_chain_headers_1.createAuthChainHeaders)(linkerResponse.authChain, timestamp, metadata);
|
|
292
|
+
});
|
|
293
|
+
try {
|
|
294
|
+
await awaitResponse;
|
|
295
|
+
logger.info('Authentication successful!');
|
|
296
|
+
// Close the browser window
|
|
297
|
+
if (program) {
|
|
298
|
+
await program.stop();
|
|
299
|
+
}
|
|
300
|
+
// Start streaming logs
|
|
301
|
+
await streamLogs(options.components, logsUrl, authHeaders);
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
(0, beautiful_logs_1.printError)(logger, 'Failed to authenticate:', e);
|
|
305
|
+
throw e;
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
if (program) {
|
|
309
|
+
void program.stop();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=index.js.map
|