@irtio/cli 0.5.2 → 0.6.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/dist/api.d.ts +52 -0
- package/dist/api.js +15 -0
- package/dist/bundle.js +1 -1
- package/dist/{chunk-GBNHBWES.js → chunk-3HQMVCYA.js} +10 -6
- package/dist/{chunk-32QTPKVT.js → chunk-DKWG7MGO.js} +1 -1
- package/dist/{chunk-KRQUAEN2.js → chunk-OTSFRVJN.js} +12 -6
- package/dist/chunk-RNAH5T4W.js +96 -0
- package/dist/chunk-RQSJZWQC.js +452 -0
- package/dist/chunk-UPHQM6NZ.js +72 -0
- package/dist/chunk-ZD4ND6X6.js +31 -0
- package/dist/chunk-ZK5JLUD4.js +94 -0
- package/dist/credentials.d.ts +61 -0
- package/dist/credentials.js +20 -0
- package/dist/delete-project-VENS2B44.js +118 -0
- package/dist/deploy.d.ts +149 -0
- package/dist/{deploy-3SABPL3T.js → deploy.js} +166 -44
- package/dist/{dev-QJOGXLKM.js → dev-QM26ONKS.js} +2957 -231
- package/dist/index.js +97 -25
- package/dist/init.d.ts +1 -1
- package/dist/init.js +20 -4
- package/dist/{keys-XBORZAPI.js → keys-JHLMEGRA.js} +8 -4
- package/dist/leaderboard-SYPSBPS3.js +352 -0
- package/dist/{login-3EXB4CGX.js → login-2M73HBZT.js} +12 -5
- package/dist/{logs-2EOXLNWF.js → logs-2W7CPZO5.js} +9 -5
- package/dist/{migrate-WUO2GBMX.js → migrate-T3DZJREY.js} +12 -8
- package/dist/ratings-VG32WFDG.js +297 -0
- package/dist/{rollback-GA6UY772.js → rollback-SO74MVZV.js} +9 -5
- package/dist/{rooms-B66LQIIF.js → rooms-VI33P4RA.js} +36 -10
- package/dist/simulate.d.ts +147 -4
- package/dist/simulate.js +680 -53
- package/dist/{static-deploy-5TBH4VNA.js → static-deploy-KOWFKWZA.js} +6 -4
- package/dist/status-HF3ZEKB7.js +219 -0
- package/dist/usage-4G23QXCH.js +213 -0
- package/dist/{whoami-CI5D5RCC.js → whoami-KTMTQNHM.js} +8 -4
- package/package.json +23 -7
- package/dist/chunk-BPE452KF.js +0 -180
- package/dist/chunk-TV66QHFP.js +0 -167
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A thin typed `fetch` wrapper around `@irtio/control`'s `/v1` API (contract.ts). Two things
|
|
3
|
+
* earn this its own module rather than inlined `fetch` calls in every command:
|
|
4
|
+
*
|
|
5
|
+
* 1. **`ApiError` becomes a real thrown error.** The control plane's error bodies (`code`,
|
|
6
|
+
* `message`, and — on `E_BREAKING_SCHEMA` — `changes`/`hint`) are exactly what `irtio deploy`
|
|
7
|
+
* needs to print verbatim; `ApiClientError` carries them through instead of a generic
|
|
8
|
+
* "request failed" the command layer would have to re-parse.
|
|
9
|
+
* 2. **401 is a command, not an error.** Every command that hits the control plane wants the same
|
|
10
|
+
* "run: irtio login" line and exit code 1, so it lives here once.
|
|
11
|
+
*/
|
|
12
|
+
declare class ApiClientError extends Error {
|
|
13
|
+
readonly name = "ApiClientError";
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly code: string;
|
|
16
|
+
readonly changes: readonly unknown[] | undefined;
|
|
17
|
+
readonly hint: string | undefined;
|
|
18
|
+
constructor(status: number, body: {
|
|
19
|
+
code: string;
|
|
20
|
+
message: string;
|
|
21
|
+
changes?: readonly unknown[];
|
|
22
|
+
hint?: string;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** Thrown by `requireApiClient`/callers when there is no stored token: the login nudge. */
|
|
26
|
+
declare class NotLoggedInError extends Error {
|
|
27
|
+
readonly controlUrl: string;
|
|
28
|
+
readonly name = "NotLoggedInError";
|
|
29
|
+
constructor(controlUrl: string);
|
|
30
|
+
}
|
|
31
|
+
interface ApiClient {
|
|
32
|
+
readonly controlUrl: string;
|
|
33
|
+
get<T>(path: string, query?: Record<string, string | undefined>): Promise<T>;
|
|
34
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
35
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
36
|
+
/**
|
|
37
|
+
* `DELETE`, with the same query-string handling as `get`. The one route that needs it today is
|
|
38
|
+
* `DELETE /v1/projects/:id/origins`, which names the origin to remove in the query rather than
|
|
39
|
+
* the body, so a client without this method cannot remove an origin at all.
|
|
40
|
+
*/
|
|
41
|
+
del<T>(path: string, query?: Record<string, string | undefined>): Promise<T>;
|
|
42
|
+
/** Uploads raw bytes (a bundle) rather than JSON. */
|
|
43
|
+
postBytes<T>(path: string, bytes: Uint8Array): Promise<T>;
|
|
44
|
+
}
|
|
45
|
+
/** Builds a client bound to one control URL, using whatever token `irtio login` last stored. */
|
|
46
|
+
declare function createApiClient(controlUrl: string): Promise<ApiClient>;
|
|
47
|
+
/** Same as `createApiClient`, but with an explicit token — what tests use. */
|
|
48
|
+
declare function createApiClientWithToken(controlUrl: string, token: string): ApiClient;
|
|
49
|
+
/** True when `err` is the "please log in" case — the shape every command checks for. */
|
|
50
|
+
declare function isLoginRequired(err: unknown): boolean;
|
|
51
|
+
|
|
52
|
+
export { type ApiClient, ApiClientError, NotLoggedInError, createApiClient, createApiClientWithToken, isLoginRequired };
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApiClientError,
|
|
3
|
+
NotLoggedInError,
|
|
4
|
+
createApiClient,
|
|
5
|
+
createApiClientWithToken,
|
|
6
|
+
isLoginRequired
|
|
7
|
+
} from "./chunk-RNAH5T4W.js";
|
|
8
|
+
import "./chunk-UPHQM6NZ.js";
|
|
9
|
+
export {
|
|
10
|
+
ApiClientError,
|
|
11
|
+
NotLoggedInError,
|
|
12
|
+
createApiClient,
|
|
13
|
+
createApiClientWithToken,
|
|
14
|
+
isLoginRequired
|
|
15
|
+
};
|
package/dist/bundle.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
STATIC_LIMITS,
|
|
3
3
|
staticPathProblem
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RQSJZWQC.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureProject,
|
|
7
7
|
readProjectConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DKWG7MGO.js";
|
|
9
9
|
import {
|
|
10
10
|
HelpRequested,
|
|
11
|
-
createApiClient,
|
|
12
11
|
helpFor,
|
|
13
|
-
helpRequested
|
|
14
|
-
|
|
12
|
+
helpRequested
|
|
13
|
+
} from "./chunk-ZD4ND6X6.js";
|
|
14
|
+
import {
|
|
15
|
+
createApiClient,
|
|
16
|
+
isLoginRequired
|
|
17
|
+
} from "./chunk-RNAH5T4W.js";
|
|
18
|
+
import {
|
|
15
19
|
resolveControlUrlForUser
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-UPHQM6NZ.js";
|
|
17
21
|
|
|
18
22
|
// src/static-deploy.ts
|
|
19
23
|
import { existsSync } from "fs";
|
|
@@ -5,8 +5,14 @@ import * as path from "path";
|
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
6
|
import { Worker } from "worker_threads";
|
|
7
7
|
import * as esbuild from "esbuild";
|
|
8
|
-
var ALLOWED_IMPORTS = [
|
|
9
|
-
|
|
8
|
+
var ALLOWED_IMPORTS = [
|
|
9
|
+
"@irtio/server",
|
|
10
|
+
"@irtio/schema",
|
|
11
|
+
"@dimforge/rapier3d-compat",
|
|
12
|
+
// D45: the second blessed engine. Exactly one import added, as the decision says.
|
|
13
|
+
"matter-js"
|
|
14
|
+
];
|
|
15
|
+
var EXTERNAL_IMPORTS = ["@dimforge/rapier3d-compat", "matter-js"];
|
|
10
16
|
var BundleError = class extends Error {
|
|
11
17
|
name = "BundleError";
|
|
12
18
|
};
|
|
@@ -85,8 +91,8 @@ async function bundleRoom(options) {
|
|
|
85
91
|
const out = result.outputFiles[0];
|
|
86
92
|
if (!out) throw new BundleError("esbuild produced no output");
|
|
87
93
|
const warnings = result.warnings.map((w) => w.text);
|
|
88
|
-
const
|
|
89
|
-
(o) => o.imports.some((i) => i.path === "@dimforge/rapier3d-compat")
|
|
94
|
+
const importsEngine = Object.values(result.metafile?.outputs ?? {}).some(
|
|
95
|
+
(o) => o.imports.some((i) => i.path === "@dimforge/rapier3d-compat" || i.path === "matter-js")
|
|
90
96
|
);
|
|
91
97
|
const hash = createHash("sha256").update(out.contents).digest("hex");
|
|
92
98
|
const file = path.join(options.outDir, `room.${hash.slice(0, 16)}.mjs`);
|
|
@@ -108,9 +114,9 @@ async function bundleRoom(options) {
|
|
|
108
114
|
}
|
|
109
115
|
if (options.verify === false) return { file, hash, warnings };
|
|
110
116
|
const verified = await verifyBundle(file);
|
|
111
|
-
if (
|
|
117
|
+
if (importsEngine && !verified.physics) {
|
|
112
118
|
throw new BundleError(
|
|
113
|
-
"irtio: this room imports
|
|
119
|
+
"irtio: this room imports a physics engine but declares no physics. Add\n physics: { engine: 'rapier3d', gravity: { x: 0, y: -9.81, z: 0 }, bodies: { ... } }\nor, for the 2D engine,\n physics: { engine: 'matter2d', gravity: { x: 0, y: 1 }, bodies: { ... } }\nto defineRoom(...), and mark the body-backed fields with the schema's physics option. Without it the runtime never creates a world, and nothing you build with the engine steps."
|
|
114
120
|
);
|
|
115
121
|
}
|
|
116
122
|
const fullHash = createHash("sha256").update(out.contents).update(verified.canonical).digest("hex");
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readCredential
|
|
3
|
+
} from "./chunk-UPHQM6NZ.js";
|
|
4
|
+
|
|
5
|
+
// src/api-client.ts
|
|
6
|
+
var ApiClientError = class extends Error {
|
|
7
|
+
name = "ApiClientError";
|
|
8
|
+
status;
|
|
9
|
+
code;
|
|
10
|
+
changes;
|
|
11
|
+
hint;
|
|
12
|
+
constructor(status, body) {
|
|
13
|
+
super(body.message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.code = body.code;
|
|
16
|
+
this.changes = body.changes;
|
|
17
|
+
this.hint = body.hint;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var NotLoggedInError = class extends Error {
|
|
21
|
+
constructor(controlUrl) {
|
|
22
|
+
super(`not logged in to ${controlUrl} \u2014 run: irtio login`);
|
|
23
|
+
this.controlUrl = controlUrl;
|
|
24
|
+
}
|
|
25
|
+
controlUrl;
|
|
26
|
+
name = "NotLoggedInError";
|
|
27
|
+
};
|
|
28
|
+
async function createApiClient(controlUrl) {
|
|
29
|
+
const credential = await readCredential(controlUrl);
|
|
30
|
+
if (!credential) throw new NotLoggedInError(controlUrl);
|
|
31
|
+
return createApiClientWithToken(controlUrl, credential.token);
|
|
32
|
+
}
|
|
33
|
+
function createApiClientWithToken(controlUrl, token) {
|
|
34
|
+
async function request(method, path, init = {}) {
|
|
35
|
+
const headers = {
|
|
36
|
+
Authorization: `Bearer ${token}`,
|
|
37
|
+
Accept: "application/json"
|
|
38
|
+
};
|
|
39
|
+
let requestBody;
|
|
40
|
+
if (init.bytes !== void 0) {
|
|
41
|
+
headers["content-type"] = "application/octet-stream";
|
|
42
|
+
requestBody = init.bytes;
|
|
43
|
+
} else if (init.body !== void 0) {
|
|
44
|
+
headers["content-type"] = "application/json";
|
|
45
|
+
requestBody = JSON.stringify(init.body);
|
|
46
|
+
}
|
|
47
|
+
const response = await fetch(`${controlUrl}${path}`, {
|
|
48
|
+
method,
|
|
49
|
+
headers,
|
|
50
|
+
...requestBody !== void 0 ? { body: requestBody } : {}
|
|
51
|
+
});
|
|
52
|
+
const text = await response.text();
|
|
53
|
+
const parsed = text.length > 0 ? JSON.parse(text) : void 0;
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const body = parsed !== void 0 && typeof parsed === "object" && parsed !== null && "code" in parsed && "message" in parsed ? parsed : {
|
|
56
|
+
code: "E_UNKNOWN",
|
|
57
|
+
message: text || `${method} ${path} failed with ${response.status}`
|
|
58
|
+
};
|
|
59
|
+
throw new ApiClientError(response.status, body);
|
|
60
|
+
}
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
function withQuery(path, query) {
|
|
64
|
+
const qs = query ? Object.entries(query).filter((e) => e[1] !== void 0).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : "";
|
|
65
|
+
return qs ? `${path}?${qs}` : path;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
controlUrl,
|
|
69
|
+
get(path, query) {
|
|
70
|
+
return request("GET", withQuery(path, query));
|
|
71
|
+
},
|
|
72
|
+
del(path, query) {
|
|
73
|
+
return request("DELETE", withQuery(path, query));
|
|
74
|
+
},
|
|
75
|
+
post(path, body) {
|
|
76
|
+
return request("POST", path, { body });
|
|
77
|
+
},
|
|
78
|
+
patch(path, body) {
|
|
79
|
+
return request("PATCH", path, { body });
|
|
80
|
+
},
|
|
81
|
+
postBytes(path, bytes) {
|
|
82
|
+
return request("POST", path, { bytes });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function isLoginRequired(err) {
|
|
87
|
+
return err instanceof NotLoggedInError || err instanceof ApiClientError && err.status === 401;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
ApiClientError,
|
|
92
|
+
NotLoggedInError,
|
|
93
|
+
createApiClient,
|
|
94
|
+
createApiClientWithToken,
|
|
95
|
+
isLoginRequired
|
|
96
|
+
};
|