@tongil_kim/clautunnel 1.0.0 → 1.2.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 +9 -3
- package/dist/index.js +219 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,15 +26,21 @@ clautunnel setup
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
You'll need:
|
|
29
|
-
- **Supabase Project
|
|
30
|
-
- **Supabase Anon Key**: Dashboard → Settings → API →
|
|
29
|
+
- **Supabase Project ID**: Dashboard → Settings → General → Project ID
|
|
30
|
+
- **Supabase Anon Key**: Dashboard → Settings → API Keys → Legacy anon Tab → Copy anon key
|
|
31
31
|
|
|
32
32
|
## Usage
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
#
|
|
35
|
+
# Create account (first time)
|
|
36
|
+
clautunnel signup
|
|
37
|
+
|
|
38
|
+
# Login (returning user)
|
|
36
39
|
clautunnel login
|
|
37
40
|
|
|
41
|
+
# Logout
|
|
42
|
+
clautunnel logout
|
|
43
|
+
|
|
38
44
|
# Start a session
|
|
39
45
|
clautunnel start
|
|
40
46
|
|
package/dist/index.js
CHANGED
|
@@ -155,7 +155,7 @@ var require_dist = __commonJS({
|
|
|
155
155
|
|
|
156
156
|
// src/index.ts
|
|
157
157
|
import { config } from "dotenv";
|
|
158
|
-
import { resolve, dirname as dirname2 } from "path";
|
|
158
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
159
159
|
import { readFileSync as readFileSync5 } from "fs";
|
|
160
160
|
import { fileURLToPath } from "url";
|
|
161
161
|
|
|
@@ -236,7 +236,7 @@ var Config = class {
|
|
|
236
236
|
requireConfiguration() {
|
|
237
237
|
if (!this.isConfigured()) {
|
|
238
238
|
throw new ConfigurationError(
|
|
239
|
-
'ClauTunnel is not configured.\n\
|
|
239
|
+
'ClauTunnel is not configured.\n\nRun "clautunnel setup" to configure your Supabase credentials.\n\nOr set environment variables in your shell profile (~/.zshrc or ~/.bashrc):\n export SUPABASE_URL=https://<project-id>.supabase.co\n export SUPABASE_ANON_KEY=<your-anon-key>'
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
242
|
}
|
|
@@ -324,26 +324,28 @@ import { EventEmitter } from "events";
|
|
|
324
324
|
// src/realtime/utils.ts
|
|
325
325
|
var DEFAULT_TIMEOUT = 1e4;
|
|
326
326
|
function subscribeWithTimeout(channel, channelName, timeout = DEFAULT_TIMEOUT) {
|
|
327
|
-
return new Promise((
|
|
327
|
+
return new Promise((resolve3) => {
|
|
328
|
+
let lastStatus = "unknown";
|
|
328
329
|
const timer = setTimeout(() => {
|
|
329
330
|
console.warn(
|
|
330
|
-
`[WARN] Realtime subscription timeout for ${channelName}
|
|
331
|
+
`[WARN] Realtime subscription timeout for ${channelName} (last status: ${lastStatus}).`
|
|
331
332
|
);
|
|
332
|
-
|
|
333
|
+
resolve3(false);
|
|
333
334
|
}, timeout);
|
|
334
335
|
channel.subscribe((status, err) => {
|
|
336
|
+
lastStatus = status;
|
|
335
337
|
if (status === "SUBSCRIBED") {
|
|
336
338
|
clearTimeout(timer);
|
|
337
|
-
|
|
339
|
+
resolve3(true);
|
|
338
340
|
} else if (status === "CHANNEL_ERROR" || status === "CLOSED" || status === "TIMED_OUT") {
|
|
339
341
|
clearTimeout(timer);
|
|
340
342
|
console.warn(
|
|
341
|
-
`[WARN] Channel ${channelName} ${status.toLowerCase()}
|
|
343
|
+
`[WARN] Channel ${channelName} ${status.toLowerCase()}.`
|
|
342
344
|
);
|
|
343
345
|
if (err) {
|
|
344
346
|
console.warn(`[WARN] Error details: ${err.message || err}`);
|
|
345
347
|
}
|
|
346
|
-
|
|
348
|
+
resolve3(false);
|
|
347
349
|
}
|
|
348
350
|
});
|
|
349
351
|
});
|
|
@@ -1042,8 +1044,8 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1042
1044
|
this.pendingQuestionData = questionData;
|
|
1043
1045
|
this.emit("user-question", questionData);
|
|
1044
1046
|
const answers = await new Promise(
|
|
1045
|
-
(
|
|
1046
|
-
this.pendingAnswerResolve =
|
|
1047
|
+
(resolve3, reject) => {
|
|
1048
|
+
this.pendingAnswerResolve = resolve3;
|
|
1047
1049
|
options.signal.addEventListener("abort", () => {
|
|
1048
1050
|
this.pendingAnswerResolve = null;
|
|
1049
1051
|
this.pendingQuestionData = null;
|
|
@@ -1098,9 +1100,9 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1098
1100
|
};
|
|
1099
1101
|
this.pendingPermissionData = requestData;
|
|
1100
1102
|
this.emit("permission-request", requestData);
|
|
1101
|
-
return new Promise((
|
|
1103
|
+
return new Promise((resolve3, reject) => {
|
|
1102
1104
|
this.pendingPermissionRequests.set(requestId, {
|
|
1103
|
-
resolve:
|
|
1105
|
+
resolve: resolve3,
|
|
1104
1106
|
reject,
|
|
1105
1107
|
signal: options.signal
|
|
1106
1108
|
});
|
|
@@ -2301,7 +2303,7 @@ ${confirmationMsg}
|
|
|
2301
2303
|
};
|
|
2302
2304
|
|
|
2303
2305
|
// src/index.ts
|
|
2304
|
-
import { Command as
|
|
2306
|
+
import { Command as Command9 } from "commander";
|
|
2305
2307
|
|
|
2306
2308
|
// src/commands/start.ts
|
|
2307
2309
|
import { Command } from "commander";
|
|
@@ -2501,10 +2503,10 @@ async function promptYesNo(question) {
|
|
|
2501
2503
|
input: process.stdin,
|
|
2502
2504
|
output: process.stdout
|
|
2503
2505
|
});
|
|
2504
|
-
return new Promise((
|
|
2506
|
+
return new Promise((resolve3) => {
|
|
2505
2507
|
rl.question(question, (answer) => {
|
|
2506
2508
|
rl.close();
|
|
2507
|
-
|
|
2509
|
+
resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
2508
2510
|
});
|
|
2509
2511
|
});
|
|
2510
2512
|
}
|
|
@@ -2628,10 +2630,13 @@ function createStartCommand() {
|
|
|
2628
2630
|
const session = await restoreSession(supabase, config2);
|
|
2629
2631
|
if (!session) {
|
|
2630
2632
|
spinner.fail("Not authenticated");
|
|
2631
|
-
logger.error(
|
|
2633
|
+
logger.error(
|
|
2634
|
+
'Run "clautunnel login" or "clautunnel signup" first.'
|
|
2635
|
+
);
|
|
2632
2636
|
process.exit(1);
|
|
2633
2637
|
}
|
|
2634
2638
|
const { user } = session;
|
|
2639
|
+
spinner.update(`Authenticated as ${user.email}...`);
|
|
2635
2640
|
let fdaStatus = null;
|
|
2636
2641
|
if (isMacOS()) {
|
|
2637
2642
|
spinner.stop();
|
|
@@ -2754,9 +2759,12 @@ function createStartCommand() {
|
|
|
2754
2759
|
const connected = await machineClient.connect();
|
|
2755
2760
|
spinner.stop();
|
|
2756
2761
|
if (!connected) {
|
|
2757
|
-
logger.error(
|
|
2758
|
-
|
|
2759
|
-
);
|
|
2762
|
+
logger.error("Failed to connect to Supabase Realtime.");
|
|
2763
|
+
logger.error("");
|
|
2764
|
+
logger.error("This may be a temporary issue. Try the following:");
|
|
2765
|
+
logger.error(' 1. Open a new terminal and run "clautunnel start" again');
|
|
2766
|
+
logger.error(" 2. Check your network connection");
|
|
2767
|
+
logger.error(' 3. Try "clautunnel login" to refresh your session');
|
|
2760
2768
|
process.exit(1);
|
|
2761
2769
|
}
|
|
2762
2770
|
logger.info("");
|
|
@@ -2897,7 +2905,7 @@ function createStopCommand() {
|
|
|
2897
2905
|
logger.info(`Sent stop signal to daemon (PID: ${pid})`);
|
|
2898
2906
|
let attempts = 0;
|
|
2899
2907
|
while (attempts < 10) {
|
|
2900
|
-
await new Promise((
|
|
2908
|
+
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
2901
2909
|
try {
|
|
2902
2910
|
process.kill(pid, 0);
|
|
2903
2911
|
attempts++;
|
|
@@ -2985,10 +2993,10 @@ function prompt(question) {
|
|
|
2985
2993
|
input: process.stdin,
|
|
2986
2994
|
output: process.stdout
|
|
2987
2995
|
});
|
|
2988
|
-
return new Promise((
|
|
2996
|
+
return new Promise((resolve3) => {
|
|
2989
2997
|
rl.question(question, (answer) => {
|
|
2990
2998
|
rl.close();
|
|
2991
|
-
|
|
2999
|
+
resolve3(answer);
|
|
2992
3000
|
});
|
|
2993
3001
|
});
|
|
2994
3002
|
}
|
|
@@ -2997,7 +3005,7 @@ function promptHidden(question) {
|
|
|
2997
3005
|
input: process.stdin,
|
|
2998
3006
|
output: process.stdout
|
|
2999
3007
|
});
|
|
3000
|
-
return new Promise((
|
|
3008
|
+
return new Promise((resolve3) => {
|
|
3001
3009
|
process.stdout.write(question);
|
|
3002
3010
|
if (process.stdin.isTTY) {
|
|
3003
3011
|
process.stdin.setRawMode(true);
|
|
@@ -3012,7 +3020,7 @@ function promptHidden(question) {
|
|
|
3012
3020
|
}
|
|
3013
3021
|
process.stdout.write("\n");
|
|
3014
3022
|
rl.close();
|
|
3015
|
-
|
|
3023
|
+
resolve3(password);
|
|
3016
3024
|
} else if (c === "") {
|
|
3017
3025
|
process.exit(0);
|
|
3018
3026
|
} else if (c === "\x7F" || c === "\b") {
|
|
@@ -3063,6 +3071,11 @@ function createLoginCommand() {
|
|
|
3063
3071
|
});
|
|
3064
3072
|
logger.info("Session saved");
|
|
3065
3073
|
}
|
|
3074
|
+
logger.info("");
|
|
3075
|
+
logger.info("Next steps:");
|
|
3076
|
+
logger.info(' 1. Run "clautunnel start" to begin a session');
|
|
3077
|
+
logger.info(" 2. Set up the mobile app:");
|
|
3078
|
+
logger.info(" https://github.com/TongilKim/ClauTunnel#mobile-app-setup");
|
|
3066
3079
|
}
|
|
3067
3080
|
} catch (error) {
|
|
3068
3081
|
if (error instanceof ConfigurationError) {
|
|
@@ -3078,10 +3091,100 @@ function createLoginCommand() {
|
|
|
3078
3091
|
return command;
|
|
3079
3092
|
}
|
|
3080
3093
|
|
|
3081
|
-
// src/commands/
|
|
3094
|
+
// src/commands/logout.ts
|
|
3082
3095
|
import { Command as Command5 } from "commander";
|
|
3096
|
+
function createLogoutCommand() {
|
|
3097
|
+
const command = new Command5("logout");
|
|
3098
|
+
command.description("Log out of ClauTunnel").action(async () => {
|
|
3099
|
+
const config2 = new Config();
|
|
3100
|
+
const logger = new Logger();
|
|
3101
|
+
const session = config2.getSessionTokens();
|
|
3102
|
+
if (!session) {
|
|
3103
|
+
logger.info("Not currently logged in.");
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
config2.clearSessionTokens();
|
|
3107
|
+
logger.info("Logged out successfully.");
|
|
3108
|
+
});
|
|
3109
|
+
return command;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// src/commands/signup.ts
|
|
3113
|
+
import { Command as Command6 } from "commander";
|
|
3114
|
+
function createSignupCommand() {
|
|
3115
|
+
const command = new Command6("signup");
|
|
3116
|
+
command.description("Create a new ClauTunnel account").action(async () => {
|
|
3117
|
+
const config2 = new Config();
|
|
3118
|
+
const logger = new Logger();
|
|
3119
|
+
try {
|
|
3120
|
+
config2.requireConfiguration();
|
|
3121
|
+
const supabase = createSupabaseClient(
|
|
3122
|
+
config2.getSupabaseUrl(),
|
|
3123
|
+
config2.getSupabaseAnonKey()
|
|
3124
|
+
);
|
|
3125
|
+
logger.info("Create a new ClauTunnel account");
|
|
3126
|
+
logger.info("");
|
|
3127
|
+
const email = await prompt("Email: ");
|
|
3128
|
+
if (!email) {
|
|
3129
|
+
logger.error("Email is required");
|
|
3130
|
+
process.exit(1);
|
|
3131
|
+
}
|
|
3132
|
+
const password = await promptHidden("Password: ");
|
|
3133
|
+
if (!password) {
|
|
3134
|
+
logger.error("Password is required");
|
|
3135
|
+
process.exit(1);
|
|
3136
|
+
}
|
|
3137
|
+
if (password.length < 6) {
|
|
3138
|
+
logger.error("Password must be at least 6 characters");
|
|
3139
|
+
process.exit(1);
|
|
3140
|
+
}
|
|
3141
|
+
const confirmPassword = await promptHidden("Confirm Password: ");
|
|
3142
|
+
if (password !== confirmPassword) {
|
|
3143
|
+
logger.error("Passwords do not match");
|
|
3144
|
+
process.exit(1);
|
|
3145
|
+
}
|
|
3146
|
+
const { data, error } = await supabase.auth.signUp({
|
|
3147
|
+
email,
|
|
3148
|
+
password
|
|
3149
|
+
});
|
|
3150
|
+
if (error) {
|
|
3151
|
+
logger.error(`Signup failed: ${error.message}`);
|
|
3152
|
+
process.exit(1);
|
|
3153
|
+
}
|
|
3154
|
+
if (data.user) {
|
|
3155
|
+
logger.info("");
|
|
3156
|
+
logger.info(`Account created for ${data.user.email}`);
|
|
3157
|
+
if (data.session) {
|
|
3158
|
+
config2.setSession({
|
|
3159
|
+
accessToken: data.session.access_token,
|
|
3160
|
+
refreshToken: data.session.refresh_token
|
|
3161
|
+
});
|
|
3162
|
+
logger.info("Logged in automatically");
|
|
3163
|
+
}
|
|
3164
|
+
logger.info("");
|
|
3165
|
+
logger.info("Next steps:");
|
|
3166
|
+
logger.info(' 1. Run "clautunnel start" to begin a session');
|
|
3167
|
+
logger.info(" 2. Set up the mobile app:");
|
|
3168
|
+
logger.info(" https://github.com/TongilKim/ClauTunnel#mobile-app-setup");
|
|
3169
|
+
}
|
|
3170
|
+
} catch (error) {
|
|
3171
|
+
if (error instanceof ConfigurationError) {
|
|
3172
|
+
logger.error(error.message);
|
|
3173
|
+
process.exit(1);
|
|
3174
|
+
}
|
|
3175
|
+
logger.error(
|
|
3176
|
+
`Signup failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3177
|
+
);
|
|
3178
|
+
process.exit(1);
|
|
3179
|
+
}
|
|
3180
|
+
});
|
|
3181
|
+
return command;
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
// src/commands/setup.ts
|
|
3185
|
+
import { Command as Command7 } from "commander";
|
|
3083
3186
|
function createSetupCommand() {
|
|
3084
|
-
const command = new
|
|
3187
|
+
const command = new Command7("setup");
|
|
3085
3188
|
command.description("Configure ClauTunnel with Supabase credentials").action(async () => {
|
|
3086
3189
|
const config2 = new Config();
|
|
3087
3190
|
const logger = new Logger();
|
|
@@ -3089,43 +3192,41 @@ function createSetupCommand() {
|
|
|
3089
3192
|
logger.info("ClauTunnel Setup");
|
|
3090
3193
|
logger.info("================");
|
|
3091
3194
|
logger.info("");
|
|
3092
|
-
logger.info("
|
|
3195
|
+
logger.info("[Step 1/2] Supabase Project ID");
|
|
3093
3196
|
logger.info("");
|
|
3094
|
-
logger.info("To find your credentials:");
|
|
3095
3197
|
logger.info(" 1. Go to your Supabase project dashboard");
|
|
3096
|
-
logger.info(' 2. Settings > General > Copy "Project
|
|
3097
|
-
logger.info(' 3. Settings > API Keys > Copy "anon public" key');
|
|
3198
|
+
logger.info(' 2. Settings > General > Copy "Project ID"');
|
|
3098
3199
|
logger.info("");
|
|
3099
|
-
const
|
|
3100
|
-
if (!
|
|
3101
|
-
logger.error("Supabase
|
|
3200
|
+
const projectId = await prompt("Project ID: ");
|
|
3201
|
+
if (!projectId) {
|
|
3202
|
+
logger.error("Supabase Project ID is required");
|
|
3102
3203
|
process.exit(1);
|
|
3103
3204
|
}
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
logger.error("");
|
|
3119
|
-
logger.error("Invalid Supabase URL format.");
|
|
3120
|
-
logger.error("The URL should end with .supabase.co");
|
|
3121
|
-
logger.error("Example: https://your-project-id.supabase.co");
|
|
3122
|
-
process.exit(1);
|
|
3123
|
-
}
|
|
3124
|
-
} catch {
|
|
3125
|
-
logger.error("Invalid URL format. Please enter a valid URL (e.g., https://xxxx.supabase.co)");
|
|
3205
|
+
if (projectId.includes("supabase.co") || projectId.startsWith("http")) {
|
|
3206
|
+
logger.error("");
|
|
3207
|
+
logger.error("Please enter only the Project ID, not the full URL.");
|
|
3208
|
+
logger.error("");
|
|
3209
|
+
logger.error("Example: abcdefghijklmnop");
|
|
3210
|
+
logger.error("NOT: https://abcdefghijklmnop.supabase.co");
|
|
3211
|
+
process.exit(1);
|
|
3212
|
+
}
|
|
3213
|
+
if (!/^[a-zA-Z0-9-]+$/.test(projectId)) {
|
|
3214
|
+
logger.error("");
|
|
3215
|
+
logger.error("Invalid Project ID format.");
|
|
3216
|
+
logger.error("The Project ID should only contain letters, numbers, and hyphens.");
|
|
3217
|
+
logger.error("");
|
|
3218
|
+
logger.error("You can find it at: Settings > General > Project ID");
|
|
3126
3219
|
process.exit(1);
|
|
3127
3220
|
}
|
|
3128
|
-
const
|
|
3221
|
+
const url = `https://${projectId}.supabase.co`;
|
|
3222
|
+
logger.info("\u2713 Project ID saved");
|
|
3223
|
+
logger.info("");
|
|
3224
|
+
logger.info("[Step 2/2] Supabase Anon Key");
|
|
3225
|
+
logger.info("");
|
|
3226
|
+
logger.info(" 1. Go to your Supabase project dashboard");
|
|
3227
|
+
logger.info(" 2. Settings > API Keys > Legacy anon Tab > Copy anon key");
|
|
3228
|
+
logger.info("");
|
|
3229
|
+
const anonKey = await prompt("Anon Key: ");
|
|
3129
3230
|
if (!anonKey) {
|
|
3130
3231
|
logger.error("Supabase Anon Key is required");
|
|
3131
3232
|
process.exit(1);
|
|
@@ -3135,8 +3236,8 @@ function createSetupCommand() {
|
|
|
3135
3236
|
logger.info("\u2713 Configuration saved successfully!");
|
|
3136
3237
|
logger.info("");
|
|
3137
3238
|
logger.info("Next steps:");
|
|
3138
|
-
logger.info('
|
|
3139
|
-
logger.info('
|
|
3239
|
+
logger.info(' - New user? Run "clautunnel signup" to create an account');
|
|
3240
|
+
logger.info(' - Have account? Run "clautunnel login" to authenticate');
|
|
3140
3241
|
} catch (error) {
|
|
3141
3242
|
logger.error(
|
|
3142
3243
|
`Setup failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -3147,21 +3248,78 @@ function createSetupCommand() {
|
|
|
3147
3248
|
return command;
|
|
3148
3249
|
}
|
|
3149
3250
|
|
|
3251
|
+
// src/commands/mobile-setup.ts
|
|
3252
|
+
import { Command as Command8 } from "commander";
|
|
3253
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
3254
|
+
import { join as join6, resolve } from "path";
|
|
3255
|
+
function createMobileSetupCommand() {
|
|
3256
|
+
const command = new Command8("mobile-setup");
|
|
3257
|
+
command.description("Generate mobile app .env file from CLI credentials").action(async () => {
|
|
3258
|
+
const config2 = new Config();
|
|
3259
|
+
const logger = new Logger();
|
|
3260
|
+
try {
|
|
3261
|
+
config2.requireConfiguration();
|
|
3262
|
+
const supabaseUrl = config2.getSupabaseUrl();
|
|
3263
|
+
const supabaseAnonKey = config2.getSupabaseAnonKey();
|
|
3264
|
+
const mobileDir = resolve(process.cwd(), "apps", "mobile");
|
|
3265
|
+
if (!existsSync5(mobileDir)) {
|
|
3266
|
+
logger.error("Could not find apps/mobile directory.");
|
|
3267
|
+
logger.error("");
|
|
3268
|
+
logger.error("Make sure you run this command from the ClauTunnel project root:");
|
|
3269
|
+
logger.error(" cd clautunnel");
|
|
3270
|
+
logger.error(" clautunnel mobile-setup");
|
|
3271
|
+
process.exit(1);
|
|
3272
|
+
}
|
|
3273
|
+
const envPath = join6(mobileDir, ".env");
|
|
3274
|
+
if (existsSync5(envPath)) {
|
|
3275
|
+
logger.warn("apps/mobile/.env already exists and will be overwritten.");
|
|
3276
|
+
}
|
|
3277
|
+
const envContent = [
|
|
3278
|
+
`EXPO_PUBLIC_SUPABASE_URL=${supabaseUrl}`,
|
|
3279
|
+
`EXPO_PUBLIC_SUPABASE_ANON_KEY=${supabaseAnonKey}`,
|
|
3280
|
+
""
|
|
3281
|
+
].join("\n");
|
|
3282
|
+
writeFileSync3(envPath, envContent);
|
|
3283
|
+
logger.info("");
|
|
3284
|
+
logger.info("Mobile app .env file created successfully!");
|
|
3285
|
+
logger.info(` ${envPath}`);
|
|
3286
|
+
logger.info("");
|
|
3287
|
+
logger.info("Next steps:");
|
|
3288
|
+
logger.info(" 1. cd apps/mobile");
|
|
3289
|
+
logger.info(" 2. pnpm start");
|
|
3290
|
+
logger.info(" 3. Scan the QR code with Expo Go");
|
|
3291
|
+
} catch (error) {
|
|
3292
|
+
if (error instanceof ConfigurationError) {
|
|
3293
|
+
logger.error(error.message);
|
|
3294
|
+
process.exit(1);
|
|
3295
|
+
}
|
|
3296
|
+
logger.error(
|
|
3297
|
+
`Mobile setup failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3298
|
+
);
|
|
3299
|
+
process.exit(1);
|
|
3300
|
+
}
|
|
3301
|
+
});
|
|
3302
|
+
return command;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3150
3305
|
// src/index.ts
|
|
3151
3306
|
var __filename = fileURLToPath(import.meta.url);
|
|
3152
3307
|
var __dirname = dirname2(__filename);
|
|
3153
|
-
config({ path:
|
|
3308
|
+
config({ path: resolve2(__dirname, "../.env"), quiet: true });
|
|
3154
3309
|
var packageJson = JSON.parse(
|
|
3155
|
-
readFileSync5(
|
|
3310
|
+
readFileSync5(resolve2(__dirname, "../package.json"), "utf-8")
|
|
3156
3311
|
);
|
|
3157
3312
|
var version = packageJson.version || "0.0.0";
|
|
3158
|
-
var program = new
|
|
3313
|
+
var program = new Command9();
|
|
3159
3314
|
program.name("clautunnel").description("Remote control for Claude Code CLI").version(version);
|
|
3160
3315
|
program.addCommand(createSetupCommand());
|
|
3161
3316
|
program.addCommand(createStartCommand());
|
|
3162
3317
|
program.addCommand(createStopCommand());
|
|
3163
3318
|
program.addCommand(createStatusCommand());
|
|
3164
3319
|
program.addCommand(createLoginCommand());
|
|
3320
|
+
program.addCommand(createLogoutCommand());
|
|
3321
|
+
program.addCommand(createSignupCommand());
|
|
3322
|
+
program.addCommand(createMobileSetupCommand());
|
|
3165
3323
|
if (process.argv[1]?.includes("clautunnel") || process.argv[1]?.endsWith("/index.js") || process.argv[1]?.endsWith("/index.ts")) {
|
|
3166
3324
|
program.parse();
|
|
3167
3325
|
}
|