life-pulse 2.4.0 → 2.4.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/dist/cli.js +127 -3
- package/dist/ui/theme.js +17 -17
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,6 +87,22 @@ function normalizePhoneCandidate(raw) {
|
|
|
87
87
|
return '+' + t.slice(1).replace(/\D/g, '');
|
|
88
88
|
return t.replace(/\D/g, '');
|
|
89
89
|
}
|
|
90
|
+
function normalizeHostOrIp(raw) {
|
|
91
|
+
let t = raw.trim();
|
|
92
|
+
if (!t)
|
|
93
|
+
return '';
|
|
94
|
+
t = t.replace(/^https?:\/\//i, '');
|
|
95
|
+
t = t.split('/')[0] || '';
|
|
96
|
+
t = t.replace(/:\d+$/, '');
|
|
97
|
+
return t.trim();
|
|
98
|
+
}
|
|
99
|
+
function extractExecOutput(error) {
|
|
100
|
+
const e = error;
|
|
101
|
+
const stdout = typeof e?.stdout === 'string' ? e.stdout : Buffer.isBuffer(e?.stdout) ? e.stdout.toString('utf-8') : '';
|
|
102
|
+
const stderr = typeof e?.stderr === 'string' ? e.stderr : Buffer.isBuffer(e?.stderr) ? e.stderr.toString('utf-8') : '';
|
|
103
|
+
const message = typeof e?.message === 'string' ? e.message : '';
|
|
104
|
+
return [stdout, stderr, message].filter(Boolean).join('\n');
|
|
105
|
+
}
|
|
90
106
|
async function promptLine(question, fallback = '') {
|
|
91
107
|
if (!process.stdin.isTTY)
|
|
92
108
|
return fallback;
|
|
@@ -120,6 +136,112 @@ function detectOpenClawToken() {
|
|
|
120
136
|
}
|
|
121
137
|
return '';
|
|
122
138
|
}
|
|
139
|
+
function detectTailscaleHostOrIp() {
|
|
140
|
+
const dns = getTailscaleHostname();
|
|
141
|
+
if (dns)
|
|
142
|
+
return normalizeHostOrIp(dns);
|
|
143
|
+
try {
|
|
144
|
+
const out = execSync('tailscale ip -4', {
|
|
145
|
+
stdio: 'pipe',
|
|
146
|
+
timeout: 5000,
|
|
147
|
+
encoding: 'utf-8',
|
|
148
|
+
});
|
|
149
|
+
const ip = out
|
|
150
|
+
.split('\n')
|
|
151
|
+
.map(s => s.trim())
|
|
152
|
+
.find(s => /^100\.\d+\.\d+\.\d+$/.test(s));
|
|
153
|
+
if (ip)
|
|
154
|
+
return ip;
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
function hasHomebrew() {
|
|
160
|
+
try {
|
|
161
|
+
execSync('command -v brew', { stdio: 'pipe', timeout: 3000, encoding: 'utf-8' });
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function installTailscaleIfMissing() {
|
|
169
|
+
if (hasTailscale())
|
|
170
|
+
return true;
|
|
171
|
+
if (!hasHomebrew())
|
|
172
|
+
return false;
|
|
173
|
+
try {
|
|
174
|
+
execSync('brew list --cask tailscale >/dev/null 2>&1 || brew install --cask tailscale', {
|
|
175
|
+
stdio: 'pipe',
|
|
176
|
+
timeout: 8 * 60 * 1000,
|
|
177
|
+
shell: '/bin/zsh',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch { }
|
|
181
|
+
return hasTailscale();
|
|
182
|
+
}
|
|
183
|
+
function extractTailscaleAuthUrl(output) {
|
|
184
|
+
const m = output.match(/https:\/\/login\.tailscale\.com\/[^\s"']+/i);
|
|
185
|
+
return m ? m[0] : '';
|
|
186
|
+
}
|
|
187
|
+
function kickOffTailscaleAuth() {
|
|
188
|
+
try {
|
|
189
|
+
execSync('open -g -a Tailscale', { stdio: 'pipe', timeout: 5000 });
|
|
190
|
+
}
|
|
191
|
+
catch { }
|
|
192
|
+
let output = '';
|
|
193
|
+
try {
|
|
194
|
+
output = execSync('tailscale up --accept-dns=false', {
|
|
195
|
+
stdio: 'pipe',
|
|
196
|
+
timeout: 15_000,
|
|
197
|
+
encoding: 'utf-8',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
output = extractExecOutput(err);
|
|
202
|
+
}
|
|
203
|
+
const authUrl = extractTailscaleAuthUrl(output);
|
|
204
|
+
if (authUrl) {
|
|
205
|
+
try {
|
|
206
|
+
execSync(`open ${JSON.stringify(authUrl)}`, { stdio: 'pipe', timeout: 5000 });
|
|
207
|
+
}
|
|
208
|
+
catch { }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function waitForTailscaleHost(maxMs = 60_000) {
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
while (Date.now() - start < maxMs) {
|
|
214
|
+
const host = detectTailscaleHostOrIp();
|
|
215
|
+
if (host)
|
|
216
|
+
return host;
|
|
217
|
+
await sleep(1500);
|
|
218
|
+
}
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
221
|
+
async function ensureTailscaleHostForPair(spinner) {
|
|
222
|
+
let host = detectTailscaleHostOrIp();
|
|
223
|
+
if (host)
|
|
224
|
+
return host;
|
|
225
|
+
spinner?.start('setting up secure link');
|
|
226
|
+
const installed = installTailscaleIfMissing();
|
|
227
|
+
if (!installed) {
|
|
228
|
+
spinner?.stop();
|
|
229
|
+
return '';
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
execSync('open -g -a Tailscale', { stdio: 'pipe', timeout: 5000 });
|
|
233
|
+
}
|
|
234
|
+
catch { }
|
|
235
|
+
host = await waitForTailscaleHost(12_000);
|
|
236
|
+
if (host) {
|
|
237
|
+
spinner?.stop();
|
|
238
|
+
return host;
|
|
239
|
+
}
|
|
240
|
+
kickOffTailscaleAuth();
|
|
241
|
+
host = await waitForTailscaleHost(60_000);
|
|
242
|
+
spinner?.stop();
|
|
243
|
+
return host;
|
|
244
|
+
}
|
|
123
245
|
function writeNoxRouteJson(payload) {
|
|
124
246
|
const desktop = join(homedir(), 'Desktop');
|
|
125
247
|
mkdirSync(desktop, { recursive: true });
|
|
@@ -268,12 +390,13 @@ async function main() {
|
|
|
268
390
|
// --pair: generate Desktop/nox-route.json for NOX routing
|
|
269
391
|
if (pairMode) {
|
|
270
392
|
const defaultName = getUserName() || '';
|
|
271
|
-
const detectedHost = getTailscaleHostname() || '';
|
|
272
393
|
const detectedToken = detectOpenClawToken();
|
|
273
394
|
const envPhone = normalizePhoneCandidate(process.env.LIFE_PULSE_SELF_PHONE
|
|
274
395
|
|| process.env.NOX_OWNER_PHONE
|
|
275
396
|
|| process.env.LIFE_PULSE_BRIEF_SMS_PHONE
|
|
276
397
|
|| '');
|
|
398
|
+
const pairSpinner = process.stdin.isTTY ? new Spinner() : undefined;
|
|
399
|
+
const detectedHost = normalizeHostOrIp(await ensureTailscaleHostForPair(pairSpinner));
|
|
277
400
|
console.log();
|
|
278
401
|
console.log(chalk.bold.hex('#c0caf5')(' pair with nox'));
|
|
279
402
|
console.log(chalk.dim(' we will create Desktop/nox-route.json'));
|
|
@@ -285,9 +408,10 @@ async function main() {
|
|
|
285
408
|
console.log(chalk.red(' missing phone number'));
|
|
286
409
|
return;
|
|
287
410
|
}
|
|
288
|
-
const host =
|
|
411
|
+
const host = detectedHost;
|
|
289
412
|
if (!host) {
|
|
290
|
-
console.log(chalk.red('
|
|
413
|
+
console.log(chalk.red(' secure link not ready yet'));
|
|
414
|
+
console.log(chalk.dim(' keep tailscale signed in, then rerun: npx life-pulse --pair'));
|
|
291
415
|
return;
|
|
292
416
|
}
|
|
293
417
|
const token = await promptLine('openclaw token', detectedToken);
|
package/dist/ui/theme.js
CHANGED
|
@@ -7,23 +7,23 @@
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
// ─── Palette ──────────────────────────────────────
|
|
9
9
|
export const C = {
|
|
10
|
-
// Brand —
|
|
11
|
-
accent: chalk.hex('#
|
|
12
|
-
brand: chalk.bold.hex('#
|
|
13
|
-
// Semantic — purple
|
|
14
|
-
ok: chalk.hex('#
|
|
15
|
-
warn: chalk.hex('#
|
|
16
|
-
err: chalk.bold.hex('#
|
|
17
|
-
info: chalk.hex('#
|
|
18
|
-
// Text hierarchy (
|
|
19
|
-
hd: chalk.bold.hex('#
|
|
20
|
-
heading: chalk.bold.hex('#
|
|
21
|
-
bright: chalk.hex('#
|
|
22
|
-
text: chalk.hex('#
|
|
23
|
-
mid: chalk.hex('#
|
|
24
|
-
dim: chalk.hex('#
|
|
25
|
-
faint: chalk.hex('#
|
|
26
|
-
rule: chalk.hex('#
|
|
10
|
+
// Brand — dark purple accent
|
|
11
|
+
accent: chalk.hex('#6b21a8'),
|
|
12
|
+
brand: chalk.bold.hex('#6b21a8'),
|
|
13
|
+
// Semantic — dark purple tones
|
|
14
|
+
ok: chalk.hex('#4a1d6e'),
|
|
15
|
+
warn: chalk.hex('#6b3fa0'),
|
|
16
|
+
err: chalk.bold.hex('#4a1d6e'),
|
|
17
|
+
info: chalk.hex('#6b3fa0'),
|
|
18
|
+
// Text hierarchy (darkest → lightest)
|
|
19
|
+
hd: chalk.bold.hex('#3b0764'),
|
|
20
|
+
heading: chalk.bold.hex('#3b0764'),
|
|
21
|
+
bright: chalk.hex('#4a1d6e'),
|
|
22
|
+
text: chalk.hex('#581c87'),
|
|
23
|
+
mid: chalk.hex('#7e22ce'),
|
|
24
|
+
dim: chalk.hex('#9333ea'),
|
|
25
|
+
faint: chalk.hex('#a855f7'),
|
|
26
|
+
rule: chalk.hex('#c084fc'),
|
|
27
27
|
};
|
|
28
28
|
// Lowercase alias (backward compat)
|
|
29
29
|
export const c = C;
|