ttsd-colabcli 1.0.2 → 1.0.4
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/cli.js +20 -17
- package/core/daemon.py +25 -2
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
5
6
|
const { spawn, execSync } = require('child_process');
|
|
6
7
|
|
|
8
|
+
const HOME_DIR = path.join(os.homedir(), '.colabcli');
|
|
9
|
+
if (!fs.existsSync(HOME_DIR)) {
|
|
10
|
+
fs.mkdirSync(HOME_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
const CMD = process.argv[2] || 'start';
|
|
8
|
-
const ENV_PATH = path.join(
|
|
14
|
+
const ENV_PATH = path.join(HOME_DIR, '.env');
|
|
9
15
|
const CORE_DIR = path.join(__dirname, 'core');
|
|
10
|
-
const PID_FILE = path.join(
|
|
16
|
+
const PID_FILE = path.join(HOME_DIR, '.daemon.pid');
|
|
17
|
+
const VENV_DIR = path.join(HOME_DIR, 'venv');
|
|
11
18
|
|
|
12
19
|
async function promptConfig() {
|
|
13
20
|
const inquirer = require('inquirer');
|
|
@@ -35,21 +42,20 @@ async function promptConfig() {
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
function setupPythonEnv() {
|
|
38
|
-
|
|
39
|
-
if (!fs.existsSync(venvDir)) {
|
|
45
|
+
if (!fs.existsSync(VENV_DIR)) {
|
|
40
46
|
console.log('Creating Python virtual environment...');
|
|
41
47
|
try {
|
|
42
|
-
execSync(`python3 -m venv "${
|
|
48
|
+
execSync(`python3 -m venv "${VENV_DIR}"`, { stdio: 'inherit' });
|
|
43
49
|
} catch (e) {
|
|
44
50
|
console.log('python3 not found, trying python...');
|
|
45
|
-
execSync(`python -m venv "${
|
|
51
|
+
execSync(`python -m venv "${VENV_DIR}"`, { stdio: 'inherit' });
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
console.log('Installing dependencies...');
|
|
50
56
|
const pip = process.platform === 'win32'
|
|
51
|
-
? path.join(
|
|
52
|
-
: path.join(
|
|
57
|
+
? path.join(VENV_DIR, 'Scripts', 'pip')
|
|
58
|
+
: path.join(VENV_DIR, 'bin', 'pip');
|
|
53
59
|
execSync(`"${pip}" install -r "${path.join(CORE_DIR, 'requirements.txt')}" -q`, { stdio: 'inherit' });
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -75,17 +81,14 @@ async function start() {
|
|
|
75
81
|
console.log('Starting Satellite Daemon in background...');
|
|
76
82
|
|
|
77
83
|
const pythonBin = process.platform === 'win32'
|
|
78
|
-
? path.join(
|
|
79
|
-
: path.join(
|
|
80
|
-
|
|
81
|
-
// Copy .env to core dir so daemon.py can load it
|
|
82
|
-
fs.copyFileSync(ENV_PATH, path.join(CORE_DIR, '.env'));
|
|
84
|
+
? path.join(VENV_DIR, 'Scripts', 'python')
|
|
85
|
+
: path.join(VENV_DIR, 'bin', 'python');
|
|
83
86
|
|
|
84
|
-
const out = fs.openSync(path.join(
|
|
85
|
-
const err = fs.openSync(path.join(
|
|
87
|
+
const out = fs.openSync(path.join(HOME_DIR, 'daemon.log'), 'a');
|
|
88
|
+
const err = fs.openSync(path.join(HOME_DIR, 'daemon.error.log'), 'a');
|
|
86
89
|
|
|
87
|
-
const child = spawn(pythonBin, ['daemon.py'], {
|
|
88
|
-
cwd:
|
|
90
|
+
const child = spawn(pythonBin, [path.join(CORE_DIR, 'daemon.py')], {
|
|
91
|
+
cwd: HOME_DIR,
|
|
89
92
|
detached: true,
|
|
90
93
|
stdio: ['ignore', out, err]
|
|
91
94
|
});
|
package/core/daemon.py
CHANGED
|
@@ -106,7 +106,19 @@ async def launch_worker(email: str, public_url: str):
|
|
|
106
106
|
|
|
107
107
|
try:
|
|
108
108
|
from app.colab_cli.runtime import ColabRuntime
|
|
109
|
-
|
|
109
|
+
# Try local first (satellite_node/daemon.py), fallback to packaged structure
|
|
110
|
+
possible_paths = [
|
|
111
|
+
os.path.join(os.path.dirname(__file__), "../colab/worker.py"),
|
|
112
|
+
os.path.join(os.path.dirname(__file__), "colab", "worker.py")
|
|
113
|
+
]
|
|
114
|
+
_worker_bytes = None
|
|
115
|
+
for p in possible_paths:
|
|
116
|
+
if os.path.exists(p):
|
|
117
|
+
_worker_bytes = open(p, "rb").read()
|
|
118
|
+
break
|
|
119
|
+
if not _worker_bytes:
|
|
120
|
+
raise FileNotFoundError(f"Could not find worker.py in {possible_paths}")
|
|
121
|
+
|
|
110
122
|
_worker_b64 = base64.b64encode(_worker_bytes).decode()
|
|
111
123
|
|
|
112
124
|
_rt = ColabRuntime(url, token, session_name=name)
|
|
@@ -133,7 +145,18 @@ print("DEPLOYED PID=" + str(proc.pid) + " ALIVE=" + str(ret is None), flush=True
|
|
|
133
145
|
await loop.run_in_executor(None, lambda: _rt.execute_code(_deploy_code, timeout=300))
|
|
134
146
|
logger.info("Worker deployed on Colab runtime for %s", email)
|
|
135
147
|
except Exception as e:
|
|
136
|
-
logger.error("Worker deploy failed: %s", e)
|
|
148
|
+
logger.error("Worker deploy failed: %s, cleaning up Colab assignment", e)
|
|
149
|
+
# Rollback: unassign the runtime so quota isn't wasted
|
|
150
|
+
try:
|
|
151
|
+
from app.colab_cli.common import kill_process
|
|
152
|
+
if pid:
|
|
153
|
+
kill_process(pid)
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
try:
|
|
157
|
+
await loop.run_in_executor(None, _client.unassign, endpoint)
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
137
160
|
await report_status(email, "FAILED", error=str(e))
|
|
138
161
|
return
|
|
139
162
|
|