idlewatch 0.1.5 → 0.1.6
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 +2 -0
- package/bin/idlewatch-agent.js +17 -3
- package/package.json +1 -1
- package/src/enrollment.js +22 -4
- package/tui/bin/darwin-arm64/idlewatch-setup +0 -0
- package/tui/src/main.rs +33 -5
package/README.md
CHANGED
|
@@ -57,6 +57,8 @@ Use `gpuSource` + `gpuConfidence` in dashboards to decide whether to trust value
|
|
|
57
57
|
npx idlewatch quickstart
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
`idlewatch` is the primary package/command name. `idlewatch-skill` still works as a compatibility alias, but treat it as legacy in user-facing docs.
|
|
61
|
+
|
|
60
62
|
The wizard keeps setup small:
|
|
61
63
|
- asks for a **device name**
|
|
62
64
|
- asks for your **API key** from `idlewatch.com/api`
|
package/bin/idlewatch-agent.js
CHANGED
|
@@ -374,6 +374,10 @@ if (quickstartRequested) {
|
|
|
374
374
|
try {
|
|
375
375
|
const result = await runEnrollmentWizard({ noTui: args.has('--no-tui') })
|
|
376
376
|
|
|
377
|
+
if (!result?.outputEnvFile || !fs.existsSync(result.outputEnvFile)) {
|
|
378
|
+
throw new Error(`setup_did_not_write_env_file:${result?.outputEnvFile || 'unknown'}`)
|
|
379
|
+
}
|
|
380
|
+
|
|
377
381
|
const enrolledEnv = parseEnvFileToObject(result.outputEnvFile)
|
|
378
382
|
const onceRun = spawnSync(process.execPath, [process.argv[1], '--once'], {
|
|
379
383
|
stdio: 'inherit',
|
|
@@ -395,7 +399,13 @@ if (quickstartRequested) {
|
|
|
395
399
|
console.error('Or rerun: idlewatch quickstart')
|
|
396
400
|
process.exit(onceRun.status ?? 1)
|
|
397
401
|
} catch (err) {
|
|
398
|
-
|
|
402
|
+
if (String(err?.message || '') === 'setup_cancelled') {
|
|
403
|
+
console.error('Enrollment cancelled before saving config.')
|
|
404
|
+
} else if (String(err?.message || '').startsWith('setup_did_not_write_env_file:')) {
|
|
405
|
+
console.error(`Enrollment failed: setup did not save idlewatch.env (${String(err.message).split(':').slice(1).join(':')}).`)
|
|
406
|
+
} else {
|
|
407
|
+
console.error(`Enrollment failed: ${err.message}`)
|
|
408
|
+
}
|
|
399
409
|
process.exit(1)
|
|
400
410
|
}
|
|
401
411
|
}
|
|
@@ -624,9 +634,13 @@ if (firebaseConfigError) {
|
|
|
624
634
|
process.exit(1)
|
|
625
635
|
}
|
|
626
636
|
|
|
627
|
-
|
|
637
|
+
const hasAnyFirebaseConfig = Boolean(PROJECT_ID || CREDS_FILE || CREDS_JSON || CREDS_B64 || FIRESTORE_EMULATOR_HOST)
|
|
638
|
+
const hasCloudConfig = Boolean(CLOUD_INGEST_URL && CLOUD_API_KEY)
|
|
639
|
+
const shouldWarnAboutMissingPublishConfig = !appReady && !hasCloudConfig && !DRY_RUN && !hasAnyFirebaseConfig
|
|
640
|
+
|
|
641
|
+
if (shouldWarnAboutMissingPublishConfig) {
|
|
628
642
|
console.error(
|
|
629
|
-
'
|
|
643
|
+
'No publish target is configured yet. Running in local-only mode. Run idlewatch quickstart to link cloud ingest, or configure Firebase/emulator mode if you need that path.'
|
|
630
644
|
)
|
|
631
645
|
}
|
|
632
646
|
|
package/package.json
CHANGED
package/src/enrollment.js
CHANGED
|
@@ -10,6 +10,24 @@ function defaultConfigDir() {
|
|
|
10
10
|
return path.join(os.homedir(), '.idlewatch')
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function machineName() {
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
const macName = spawnSync('scutil', ['--get', 'ComputerName'], { encoding: 'utf8' })
|
|
16
|
+
if (macName.status === 0) {
|
|
17
|
+
const value = String(macName.stdout || '').trim()
|
|
18
|
+
if (value) return value
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const hostName = String(os.hostname() || '').trim()
|
|
23
|
+
if (hostName) return hostName
|
|
24
|
+
|
|
25
|
+
const envHost = String(process.env.HOSTNAME || '').trim()
|
|
26
|
+
if (envHost) return envHost
|
|
27
|
+
|
|
28
|
+
return 'IdleWatch Device'
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
function ensureDir(dirPath) {
|
|
14
32
|
fs.mkdirSync(dirPath, { recursive: true })
|
|
15
33
|
}
|
|
@@ -80,12 +98,12 @@ function looksLikeCloudApiKey(value) {
|
|
|
80
98
|
return /^iwk_[A-Za-z0-9_-]{20,}$/.test(String(value || '').trim())
|
|
81
99
|
}
|
|
82
100
|
|
|
83
|
-
function normalizeDeviceName(raw, fallback =
|
|
101
|
+
function normalizeDeviceName(raw, fallback = machineName()) {
|
|
84
102
|
const value = String(raw || '').trim().replace(/\s+/g, ' ')
|
|
85
103
|
return value || fallback
|
|
86
104
|
}
|
|
87
105
|
|
|
88
|
-
function sanitizeDeviceId(raw, fallback =
|
|
106
|
+
function sanitizeDeviceId(raw, fallback = machineName()) {
|
|
89
107
|
const base = normalizeDeviceName(raw, fallback).toLowerCase()
|
|
90
108
|
const sanitized = base
|
|
91
109
|
.replace(/[^a-z0-9._-]+/g, '-')
|
|
@@ -174,7 +192,7 @@ export async function runEnrollmentWizard(options = {}) {
|
|
|
174
192
|
let mode = options.mode || process.env.IDLEWATCH_ENROLL_MODE || null
|
|
175
193
|
let cloudApiKey = normalizeCloudApiKey(options.cloudApiKey || process.env.IDLEWATCH_CLOUD_API_KEY || null)
|
|
176
194
|
let cloudIngestUrl = options.cloudIngestUrl || process.env.IDLEWATCH_CLOUD_INGEST_URL || 'https://api.idlewatch.com/api/ingest'
|
|
177
|
-
let deviceName = normalizeDeviceName(options.deviceName || process.env.IDLEWATCH_DEVICE_NAME ||
|
|
195
|
+
let deviceName = normalizeDeviceName(options.deviceName || process.env.IDLEWATCH_DEVICE_NAME || machineName())
|
|
178
196
|
|
|
179
197
|
const availableMonitorTargets = detectAvailableMonitorTargets()
|
|
180
198
|
let monitorTargets = normalizeMonitorTargets(
|
|
@@ -229,7 +247,7 @@ export async function runEnrollmentWizard(options = {}) {
|
|
|
229
247
|
monitorTargets = normalizeMonitorTargets(monitorInput || suggested, availableMonitorTargets)
|
|
230
248
|
}
|
|
231
249
|
|
|
232
|
-
const safeDeviceId = sanitizeDeviceId(options.deviceId || process.env.IDLEWATCH_DEVICE_ID || deviceName,
|
|
250
|
+
const safeDeviceId = sanitizeDeviceId(options.deviceId || process.env.IDLEWATCH_DEVICE_ID || deviceName, machineName())
|
|
233
251
|
const localLogPath = path.join(configDir, 'logs', `${safeDeviceId}-metrics.ndjson`)
|
|
234
252
|
const localCachePath = path.join(configDir, 'cache', `${safeDeviceId}-openclaw-last-good.json`)
|
|
235
253
|
|
|
Binary file
|
package/tui/src/main.rs
CHANGED
|
@@ -70,6 +70,34 @@ fn command_exists(cmd: &str, args: &[&str]) -> bool {
|
|
|
70
70
|
.unwrap_or(false)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
fn machine_name() -> String {
|
|
74
|
+
if cfg!(target_os = "macos") {
|
|
75
|
+
if let Ok(output) = Command::new("scutil").args(["--get", "ComputerName"]).output() {
|
|
76
|
+
if output.status.success() {
|
|
77
|
+
let name = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
78
|
+
if !name.is_empty() {
|
|
79
|
+
return name;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if let Ok(output) = Command::new("hostname").output() {
|
|
86
|
+
if output.status.success() {
|
|
87
|
+
let name = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
88
|
+
if !name.is_empty() {
|
|
89
|
+
return name;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
std::env::var("HOSTNAME")
|
|
95
|
+
.ok()
|
|
96
|
+
.map(|value| value.trim().to_string())
|
|
97
|
+
.filter(|value| !value.is_empty())
|
|
98
|
+
.unwrap_or_else(|| "IdleWatch Device".to_string())
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
fn parse_env_file(path: &Path) -> ExistingConfig {
|
|
74
102
|
let mut config = ExistingConfig::default();
|
|
75
103
|
let Ok(raw) = fs::read_to_string(path) else {
|
|
@@ -443,7 +471,7 @@ fn main() -> Result<()> {
|
|
|
443
471
|
let env_file = std::env::var("IDLEWATCH_ENROLL_OUTPUT_ENV_FILE")
|
|
444
472
|
.map(PathBuf::from)
|
|
445
473
|
.unwrap_or_else(|_| config_dir.join("idlewatch.env"));
|
|
446
|
-
let host = sanitize_host(&
|
|
474
|
+
let host = sanitize_host(&machine_name());
|
|
447
475
|
let existing = parse_env_file(&env_file);
|
|
448
476
|
|
|
449
477
|
enable_raw_mode()?;
|
|
@@ -464,7 +492,7 @@ fn main() -> Result<()> {
|
|
|
464
492
|
KeyCode::Char('q') | KeyCode::Esc => {
|
|
465
493
|
disable_raw_mode()?;
|
|
466
494
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
|
467
|
-
return
|
|
495
|
+
return Err(anyhow!("setup_cancelled"));
|
|
468
496
|
}
|
|
469
497
|
_ => {}
|
|
470
498
|
}
|
|
@@ -503,7 +531,7 @@ fn main() -> Result<()> {
|
|
|
503
531
|
KeyCode::Char('q') | KeyCode::Esc => {
|
|
504
532
|
disable_raw_mode()?;
|
|
505
533
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
|
506
|
-
return
|
|
534
|
+
return Err(anyhow!("setup_cancelled"));
|
|
507
535
|
}
|
|
508
536
|
_ => {}
|
|
509
537
|
}
|
|
@@ -545,7 +573,7 @@ fn main() -> Result<()> {
|
|
|
545
573
|
KeyCode::Char('q') | KeyCode::Esc => {
|
|
546
574
|
disable_raw_mode()?;
|
|
547
575
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
|
548
|
-
return
|
|
576
|
+
return Err(anyhow!("setup_cancelled"));
|
|
549
577
|
}
|
|
550
578
|
KeyCode::Char(c) => {
|
|
551
579
|
device_name_input.push(c);
|
|
@@ -592,7 +620,7 @@ fn main() -> Result<()> {
|
|
|
592
620
|
KeyCode::Char('q') | KeyCode::Esc => {
|
|
593
621
|
disable_raw_mode()?;
|
|
594
622
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
|
595
|
-
return
|
|
623
|
+
return Err(anyhow!("setup_cancelled"));
|
|
596
624
|
}
|
|
597
625
|
KeyCode::Char(c) => {
|
|
598
626
|
cloud_api_key_input.push(c);
|