gambi 0.2.3 → 0.3.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/LICENSE +21 -0
- package/README.md +12 -1
- package/bin/gambi +135 -0
- package/package.json +9 -28
- package/postinstall.mjs +85 -0
- package/src/cli.ts +0 -32
- package/src/commands/create.ts +0 -160
- package/src/commands/join.ts +0 -937
- package/src/commands/list.ts +0 -104
- package/src/commands/monitor.ts +0 -27
- package/src/commands/serve.ts +0 -116
- package/src/utils/network-endpoint.test.ts +0 -40
- package/src/utils/network-endpoint.ts +0 -136
- package/src/utils/option.ts +0 -10
- package/src/utils/prompt.ts +0 -24
- package/src/utils/runtime-config.ts +0 -211
- package/src/utils/specs.ts +0 -149
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arthur BM
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Gambi CLI
|
|
2
|
+
|
|
3
|
+
This directory is the source workspace for the Gambi CLI.
|
|
4
|
+
|
|
5
|
+
Releases publish the CLI through a generated distribution:
|
|
6
|
+
|
|
7
|
+
- `gambi`: public wrapper package
|
|
8
|
+
- `gambi-<os>-<arch>`: platform-specific binary packages
|
|
9
|
+
|
|
10
|
+
The source workspace itself is `private` and is not published directly to npm.
|
|
11
|
+
|
|
12
|
+
For user-facing installation instructions, see the root `README.md` and `https://gambi.sh`.
|
package/bin/gambi
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const childProcess = require("node:child_process");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const PLATFORM_MAP = {
|
|
8
|
+
darwin: "darwin",
|
|
9
|
+
linux: "linux",
|
|
10
|
+
win32: "windows",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ARCH_MAP = {
|
|
14
|
+
arm64: "arm64",
|
|
15
|
+
x64: "x64",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function run(target) {
|
|
19
|
+
const result = childProcess.spawnSync(target, process.argv.slice(2), {
|
|
20
|
+
stdio: "inherit",
|
|
21
|
+
windowsHide: false,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (result.error) {
|
|
25
|
+
console.error(result.error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const code = typeof result.status === "number" ? result.status : 1;
|
|
30
|
+
process.exit(code);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getCandidates() {
|
|
34
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
35
|
+
const arch = ARCH_MAP[process.arch];
|
|
36
|
+
|
|
37
|
+
if (!platform || !arch) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return [`gambi-${platform}-${arch}`];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getBinaryName() {
|
|
45
|
+
return process.platform === "win32" ? "gambi.exe" : "gambi";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function tryPath(candidate) {
|
|
49
|
+
return fs.existsSync(candidate) ? candidate : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveViaRequire(packageName) {
|
|
53
|
+
try {
|
|
54
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
55
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
56
|
+
return tryPath(path.join(packageDir, "bin", getBinaryName()));
|
|
57
|
+
} catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveViaNodeModules(startDir, packageNames) {
|
|
63
|
+
let current = startDir;
|
|
64
|
+
|
|
65
|
+
for (;;) {
|
|
66
|
+
const modulesDir = path.join(current, "node_modules");
|
|
67
|
+
for (const packageName of packageNames) {
|
|
68
|
+
const resolved = tryPath(
|
|
69
|
+
path.join(modulesDir, packageName, "bin", getBinaryName())
|
|
70
|
+
);
|
|
71
|
+
if (resolved) {
|
|
72
|
+
return resolved;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const parent = path.dirname(current);
|
|
77
|
+
if (parent === current) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
current = parent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveViaSiblingPackages(scriptDir, packageNames) {
|
|
85
|
+
const npmDistDir = path.resolve(scriptDir, "..", "..");
|
|
86
|
+
|
|
87
|
+
for (const packageName of packageNames) {
|
|
88
|
+
const resolved = tryPath(
|
|
89
|
+
path.join(npmDistDir, packageName, "bin", getBinaryName())
|
|
90
|
+
);
|
|
91
|
+
if (resolved) {
|
|
92
|
+
return resolved;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const overridePath = process.env.GAMBI_BIN_PATH;
|
|
100
|
+
if (overridePath) {
|
|
101
|
+
run(overridePath);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const scriptPath = fs.realpathSync(__filename);
|
|
105
|
+
const scriptDir = path.dirname(scriptPath);
|
|
106
|
+
const cachedBinary = path.join(
|
|
107
|
+
scriptDir,
|
|
108
|
+
process.platform === "win32" ? ".gambi.exe" : ".gambi"
|
|
109
|
+
);
|
|
110
|
+
if (fs.existsSync(cachedBinary)) {
|
|
111
|
+
run(cachedBinary);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const packageNames = getCandidates();
|
|
115
|
+
if (packageNames.length === 0) {
|
|
116
|
+
console.error(
|
|
117
|
+
`Gambi does not ship a packaged binary for ${process.platform}/${process.arch} yet.`,
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const resolved =
|
|
123
|
+
packageNames.map(resolveViaRequire).find(Boolean) ||
|
|
124
|
+
resolveViaSiblingPackages(scriptDir, packageNames) ||
|
|
125
|
+
resolveViaNodeModules(scriptDir, packageNames);
|
|
126
|
+
|
|
127
|
+
if (!resolved) {
|
|
128
|
+
console.error(
|
|
129
|
+
"Gambi could not find a compatible platform binary. Expected one of: " +
|
|
130
|
+
packageNames.map((name) => `"${name}"`).join(" or "),
|
|
131
|
+
);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
run(resolved);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gambi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "CLI for Gambi - Share local LLMs across your network",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -22,37 +22,18 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/arthurbm/gambi",
|
|
24
24
|
"bugs": "https://github.com/arthurbm/gambi/issues",
|
|
25
|
-
"type": "module",
|
|
26
25
|
"bin": {
|
|
27
|
-
"gambi": "./
|
|
26
|
+
"gambi": "./bin/gambi"
|
|
28
27
|
},
|
|
29
|
-
"exports": {
|
|
30
|
-
"./serve": "./src/commands/serve.ts",
|
|
31
|
-
"./create": "./src/commands/create.ts",
|
|
32
|
-
"./join": "./src/commands/join.ts",
|
|
33
|
-
"./list": "./src/commands/list.ts",
|
|
34
|
-
"./monitor": "./src/commands/monitor.ts"
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"src",
|
|
38
|
-
"README.md"
|
|
39
|
-
],
|
|
40
28
|
"scripts": {
|
|
41
|
-
"
|
|
42
|
-
"build": "bun run build.ts",
|
|
43
|
-
"check-types": "tsc --noEmit"
|
|
44
|
-
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"@clack/prompts": "^1.1.0",
|
|
47
|
-
"@gambi/core": "workspace:*",
|
|
48
|
-
"clipanion": "^4.0.0-rc.4",
|
|
49
|
-
"nanoid": "^5.1.5"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@types/bun": "latest"
|
|
29
|
+
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
53
30
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
31
|
+
"optionalDependencies": {
|
|
32
|
+
"gambi-linux-x64": "0.3.1",
|
|
33
|
+
"gambi-linux-arm64": "0.3.1",
|
|
34
|
+
"gambi-darwin-arm64": "0.3.1",
|
|
35
|
+
"gambi-darwin-x64": "0.3.1",
|
|
36
|
+
"gambi-windows-x64": "0.3.1"
|
|
56
37
|
},
|
|
57
38
|
"publishConfig": {
|
|
58
39
|
"access": "public"
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const wrapperDir = path.resolve(__dirname, "..");
|
|
12
|
+
const wrapperBinDir = path.join(wrapperDir, "bin");
|
|
13
|
+
|
|
14
|
+
function detectPackageName() {
|
|
15
|
+
const platformMap = {
|
|
16
|
+
darwin: "darwin",
|
|
17
|
+
linux: "linux",
|
|
18
|
+
win32: "windows",
|
|
19
|
+
};
|
|
20
|
+
const archMap = {
|
|
21
|
+
arm64: "arm64",
|
|
22
|
+
x64: "x64",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const platform = platformMap[os.platform()];
|
|
26
|
+
const arch = archMap[os.arch()];
|
|
27
|
+
|
|
28
|
+
if (!(platform && arch)) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return `gambi-${platform}-${arch}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findBinary(packageName) {
|
|
36
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
37
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
38
|
+
const binaryName = os.platform() === "win32" ? "gambi.exe" : "gambi";
|
|
39
|
+
const binaryPath = path.join(packageDir, "bin", binaryName);
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(binaryPath)) {
|
|
42
|
+
throw new Error(`Binary not found at ${binaryPath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return binaryPath;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function main() {
|
|
49
|
+
const packageName = detectPackageName();
|
|
50
|
+
if (!packageName) {
|
|
51
|
+
console.warn(
|
|
52
|
+
`[gambi] No packaged binary is available for ${os.platform()}/${os.arch()}.`
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const binaryPath = findBinary(packageName);
|
|
59
|
+
const cachePath = path.join(
|
|
60
|
+
wrapperBinDir,
|
|
61
|
+
os.platform() === "win32" ? ".gambi.exe" : ".gambi"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
fs.mkdirSync(wrapperBinDir, { recursive: true });
|
|
65
|
+
if (fs.existsSync(cachePath)) {
|
|
66
|
+
fs.unlinkSync(cachePath);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (os.platform() === "win32") {
|
|
70
|
+
fs.copyFileSync(binaryPath, cachePath);
|
|
71
|
+
} else {
|
|
72
|
+
try {
|
|
73
|
+
fs.linkSync(binaryPath, cachePath);
|
|
74
|
+
} catch {
|
|
75
|
+
fs.copyFileSync(binaryPath, cachePath);
|
|
76
|
+
}
|
|
77
|
+
fs.chmodSync(cachePath, 0o755);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
console.warn(`[gambi] Failed to cache the platform binary: ${message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|
package/src/cli.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
import { Builtins, Cli } from "./utils/option.ts";
|
|
3
|
-
import { CreateCommand } from "./commands/create.ts";
|
|
4
|
-
import { JoinCommand } from "./commands/join.ts";
|
|
5
|
-
import { ListCommand } from "./commands/list.ts";
|
|
6
|
-
import { MonitorCommand } from "./commands/monitor.ts";
|
|
7
|
-
import { ServeCommand } from "./commands/serve.ts";
|
|
8
|
-
|
|
9
|
-
const cli = new Cli({
|
|
10
|
-
binaryLabel: "gambi",
|
|
11
|
-
binaryName: "gambi",
|
|
12
|
-
binaryVersion: "0.0.1",
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
cli.register(ServeCommand);
|
|
16
|
-
cli.register(CreateCommand);
|
|
17
|
-
cli.register(JoinCommand);
|
|
18
|
-
cli.register(ListCommand);
|
|
19
|
-
cli.register(MonitorCommand);
|
|
20
|
-
cli.register(Builtins.HelpCommand);
|
|
21
|
-
cli.register(Builtins.VersionCommand);
|
|
22
|
-
|
|
23
|
-
// TODO: Re-add TUI support as a separate optional package.
|
|
24
|
-
// Previously, running `gambi` with no args launched the TUI,
|
|
25
|
-
// but bundling OpenTUI + React inflated the binary from ~50MB to ~110MB.
|
|
26
|
-
// See: https://github.com/arthurbm/gambi/issues — create issue for TUI separation
|
|
27
|
-
const args = process.argv.slice(2);
|
|
28
|
-
if (args.length === 0) {
|
|
29
|
-
cli.runExit(["--help"]);
|
|
30
|
-
} else {
|
|
31
|
-
cli.runExit(args);
|
|
32
|
-
}
|
package/src/commands/create.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { intro, outro, password as passwordPrompt, text } from "@clack/prompts";
|
|
2
|
-
import { Command, Option } from "../utils/option.ts";
|
|
3
|
-
import { handleCancel, isInteractive } from "../utils/prompt.ts";
|
|
4
|
-
import {
|
|
5
|
-
hasRuntimeConfig,
|
|
6
|
-
loadRuntimeConfigFile,
|
|
7
|
-
promptRuntimeConfig,
|
|
8
|
-
} from "../utils/runtime-config.ts";
|
|
9
|
-
|
|
10
|
-
interface CreateRoomResponse {
|
|
11
|
-
room: {
|
|
12
|
-
id: string;
|
|
13
|
-
code: string;
|
|
14
|
-
name: string;
|
|
15
|
-
hostId: string;
|
|
16
|
-
createdAt: number;
|
|
17
|
-
};
|
|
18
|
-
hostId: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ErrorResponse {
|
|
22
|
-
error: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class CreateCommand extends Command {
|
|
26
|
-
static override paths = [["create"]];
|
|
27
|
-
|
|
28
|
-
static override usage = Command.Usage({
|
|
29
|
-
description: "Create a new room on a hub",
|
|
30
|
-
examples: [
|
|
31
|
-
["Create a room (interactive)", "gambi create"],
|
|
32
|
-
["Create a room", "gambi create --name 'My Room'"],
|
|
33
|
-
[
|
|
34
|
-
"Create on custom hub",
|
|
35
|
-
"gambi create --name 'My Room' --hub http://192.168.1.10:3000",
|
|
36
|
-
],
|
|
37
|
-
[
|
|
38
|
-
"Create a password-protected room",
|
|
39
|
-
"gambi create --name 'My Room' --password secret123",
|
|
40
|
-
],
|
|
41
|
-
],
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
name = Option.String("--name,-n", {
|
|
45
|
-
description: "Room name",
|
|
46
|
-
required: false,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
password = Option.String("--password,-p", {
|
|
50
|
-
description: "Optional password to protect the room",
|
|
51
|
-
required: false,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
configPath = Option.String("--config", {
|
|
55
|
-
description: "Path to a JSON file with room defaults",
|
|
56
|
-
required: false,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
hub = Option.String("--hub,-H", "http://localhost:3000", {
|
|
60
|
-
description: "Hub URL",
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
async execute(): Promise<number> {
|
|
64
|
-
let name = this.name;
|
|
65
|
-
let password = this.password;
|
|
66
|
-
let defaults = this.configPath
|
|
67
|
-
? await loadRuntimeConfigFile(this.configPath).catch((error) => {
|
|
68
|
-
this.context.stderr.write(`${error}\n`);
|
|
69
|
-
return null;
|
|
70
|
-
})
|
|
71
|
-
: {};
|
|
72
|
-
|
|
73
|
-
if (defaults === null) {
|
|
74
|
-
return 1;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!name && isInteractive()) {
|
|
78
|
-
intro("gambi create");
|
|
79
|
-
|
|
80
|
-
const nameResult = await text({
|
|
81
|
-
message: "Room name:",
|
|
82
|
-
validate: (v) => (v ? undefined : "Room name is required"),
|
|
83
|
-
});
|
|
84
|
-
handleCancel(nameResult);
|
|
85
|
-
name = nameResult as string;
|
|
86
|
-
|
|
87
|
-
if (password === undefined) {
|
|
88
|
-
const passwordResult = await passwordPrompt({
|
|
89
|
-
message: "Room password (leave empty for no password):",
|
|
90
|
-
});
|
|
91
|
-
handleCancel(passwordResult);
|
|
92
|
-
const pwd = passwordResult as string;
|
|
93
|
-
if (pwd) {
|
|
94
|
-
password = pwd;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
defaults = await promptRuntimeConfig("room", defaults);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
this.context.stderr.write(`${error}\n`);
|
|
102
|
-
return 1;
|
|
103
|
-
}
|
|
104
|
-
} else if (!name) {
|
|
105
|
-
this.context.stderr.write(
|
|
106
|
-
"Error: --name is required (or run in a terminal for interactive mode)\n"
|
|
107
|
-
);
|
|
108
|
-
return 1;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const body: {
|
|
113
|
-
defaults?: typeof defaults;
|
|
114
|
-
name: string;
|
|
115
|
-
password?: string;
|
|
116
|
-
} = { name };
|
|
117
|
-
if (password) {
|
|
118
|
-
body.password = password;
|
|
119
|
-
}
|
|
120
|
-
if (hasRuntimeConfig(defaults)) {
|
|
121
|
-
body.defaults = defaults;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const response = await fetch(`${this.hub}/rooms`, {
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers: { "Content-Type": "application/json" },
|
|
127
|
-
body: JSON.stringify(body),
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
if (!response.ok) {
|
|
131
|
-
const data = (await response.json()) as ErrorResponse;
|
|
132
|
-
this.context.stderr.write(`Error: ${data.error}\n`);
|
|
133
|
-
return 1;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const data = (await response.json()) as CreateRoomResponse;
|
|
137
|
-
|
|
138
|
-
const successMsg = [
|
|
139
|
-
"Room created!",
|
|
140
|
-
` Code: ${data.room.code}`,
|
|
141
|
-
` ID: ${data.room.id}`,
|
|
142
|
-
...(password ? [" Protection: Password-protected"] : []),
|
|
143
|
-
"",
|
|
144
|
-
"Share the code with participants to join.",
|
|
145
|
-
].join("\n");
|
|
146
|
-
|
|
147
|
-
if (isInteractive() && !this.name) {
|
|
148
|
-
outro(successMsg);
|
|
149
|
-
} else {
|
|
150
|
-
this.context.stdout.write(`${successMsg}\n`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return 0;
|
|
154
|
-
} catch (err) {
|
|
155
|
-
this.context.stderr.write(`Failed to connect to hub at ${this.hub}\n`);
|
|
156
|
-
this.context.stderr.write(`${err}\n`);
|
|
157
|
-
return 1;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|