gemkit-cli 0.2.3 → 0.3.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 +141 -7
- package/dist/commands/agent/index.d.ts +9 -0
- package/dist/commands/agent/index.js +1329 -0
- package/dist/commands/cache/index.d.ts +5 -0
- package/dist/commands/cache/index.js +43 -0
- package/dist/commands/catalog/index.d.ts +2 -0
- package/dist/commands/catalog/index.js +57 -0
- package/dist/commands/config/index.d.ts +7 -0
- package/dist/commands/config/index.js +122 -0
- package/dist/commands/convert/index.d.ts +8 -0
- package/dist/commands/convert/index.js +391 -0
- package/dist/commands/doctor/index.d.ts +2 -0
- package/dist/commands/doctor/index.js +243 -0
- package/dist/commands/extension/index.d.ts +5 -0
- package/dist/commands/extension/index.js +52 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +37 -0
- package/dist/commands/init/index.d.ts +6 -0
- package/dist/commands/init/index.js +345 -0
- package/dist/commands/new/index.d.ts +5 -0
- package/dist/commands/new/index.js +49 -0
- package/dist/commands/office/index.d.ts +5 -0
- package/dist/commands/office/index.js +283 -0
- package/dist/commands/paste/index.d.ts +10 -0
- package/dist/commands/paste/index.js +533 -0
- package/dist/commands/plan/index.d.ts +8 -0
- package/dist/commands/plan/index.js +247 -0
- package/dist/commands/session/index.d.ts +8 -0
- package/dist/commands/session/index.js +289 -0
- package/dist/commands/tokens/index.d.ts +6 -0
- package/dist/commands/tokens/index.js +148 -0
- package/dist/commands/update/index.d.ts +26 -0
- package/dist/commands/update/index.js +199 -0
- package/dist/commands/versions/index.d.ts +5 -0
- package/dist/commands/versions/index.js +39 -0
- package/dist/domains/agent/index.d.ts +8 -0
- package/dist/domains/agent/index.js +8 -0
- package/dist/domains/agent/mappings.d.ts +32 -0
- package/dist/domains/agent/mappings.js +164 -0
- package/dist/domains/agent/profile.d.ts +26 -0
- package/dist/domains/agent/profile.js +225 -0
- package/dist/domains/agent/pty-context.d.ts +11 -0
- package/dist/domains/agent/pty-context.js +83 -0
- package/dist/domains/agent/pty-providers.d.ts +18 -0
- package/dist/domains/agent/pty-providers.js +66 -0
- package/dist/domains/agent/pty-session.d.ts +33 -0
- package/dist/domains/agent/pty-session.js +82 -0
- package/dist/domains/agent/pty-types.d.ts +127 -0
- package/dist/domains/agent/pty-types.js +4 -0
- package/dist/domains/agent/search.d.ts +45 -0
- package/dist/domains/agent/search.js +614 -0
- package/dist/domains/agent/types.d.ts +78 -0
- package/dist/domains/agent/types.js +5 -0
- package/dist/domains/agent-office/documents-scanner.d.ts +9 -0
- package/dist/domains/agent-office/documents-scanner.js +143 -0
- package/dist/domains/agent-office/event-emitter.d.ts +43 -0
- package/dist/domains/agent-office/event-emitter.js +86 -0
- package/dist/domains/agent-office/file-watcher.d.ts +40 -0
- package/dist/domains/agent-office/file-watcher.js +173 -0
- package/dist/domains/agent-office/icons.d.ts +11 -0
- package/dist/domains/agent-office/icons.js +36 -0
- package/dist/domains/agent-office/index.d.ts +12 -0
- package/dist/domains/agent-office/index.js +20 -0
- package/dist/domains/agent-office/renderer/web/assets.d.ts +11 -0
- package/dist/domains/agent-office/renderer/web/assets.js +3419 -0
- package/dist/domains/agent-office/renderer/web/server.d.ts +42 -0
- package/dist/domains/agent-office/renderer/web/server.js +228 -0
- package/dist/domains/agent-office/renderer/web.d.ts +30 -0
- package/dist/domains/agent-office/renderer/web.js +111 -0
- package/dist/domains/agent-office/session-bridge.d.ts +23 -0
- package/dist/domains/agent-office/session-bridge.js +171 -0
- package/dist/domains/agent-office/state-machine.d.ts +5 -0
- package/dist/domains/agent-office/state-machine.js +82 -0
- package/dist/domains/agent-office/types.d.ts +91 -0
- package/dist/domains/agent-office/types.js +4 -0
- package/dist/domains/cache/index.d.ts +1 -0
- package/dist/domains/cache/index.js +1 -0
- package/dist/domains/cache/manager.d.ts +22 -0
- package/dist/domains/cache/manager.js +84 -0
- package/dist/domains/config/index.d.ts +5 -0
- package/dist/domains/config/index.js +5 -0
- package/dist/domains/config/manager.d.ts +24 -0
- package/dist/domains/config/manager.js +85 -0
- package/dist/domains/config/schema.d.ts +17 -0
- package/dist/domains/config/schema.js +96 -0
- package/dist/domains/convert/converter.d.ts +78 -0
- package/dist/domains/convert/converter.js +471 -0
- package/dist/domains/convert/index.d.ts +5 -0
- package/dist/domains/convert/index.js +5 -0
- package/dist/domains/convert/types.d.ts +88 -0
- package/dist/domains/convert/types.js +18 -0
- package/dist/domains/github/download.d.ts +12 -0
- package/dist/domains/github/download.js +51 -0
- package/dist/domains/github/index.d.ts +2 -0
- package/dist/domains/github/index.js +2 -0
- package/dist/domains/github/releases.d.ts +16 -0
- package/dist/domains/github/releases.js +68 -0
- package/dist/domains/installation/conflict.d.ts +13 -0
- package/dist/domains/installation/conflict.js +38 -0
- package/dist/domains/installation/file-sync.d.ts +16 -0
- package/dist/domains/installation/file-sync.js +77 -0
- package/dist/domains/installation/index.d.ts +3 -0
- package/dist/domains/installation/index.js +3 -0
- package/dist/domains/installation/metadata.d.ts +20 -0
- package/dist/domains/installation/metadata.js +52 -0
- package/dist/domains/plan/index.d.ts +2 -0
- package/dist/domains/plan/index.js +2 -0
- package/dist/domains/plan/resolver.d.ts +24 -0
- package/dist/domains/plan/resolver.js +164 -0
- package/dist/domains/plan/types.d.ts +13 -0
- package/dist/domains/plan/types.js +4 -0
- package/dist/domains/session/env.d.ts +51 -0
- package/dist/domains/session/env.js +118 -0
- package/dist/domains/session/index.d.ts +8 -0
- package/dist/domains/session/index.js +8 -0
- package/dist/domains/session/manager.d.ts +56 -0
- package/dist/domains/session/manager.js +205 -0
- package/dist/domains/session/paths.d.ts +6 -0
- package/dist/domains/session/paths.js +6 -0
- package/dist/domains/session/types.d.ts +121 -0
- package/dist/domains/session/types.js +5 -0
- package/dist/domains/session/writer.d.ts +82 -0
- package/dist/domains/session/writer.js +431 -0
- package/dist/domains/tokens/index.d.ts +5 -0
- package/dist/domains/tokens/index.js +5 -0
- package/dist/domains/tokens/pricing.d.ts +38 -0
- package/dist/domains/tokens/pricing.js +129 -0
- package/dist/domains/tokens/scanner.d.ts +42 -0
- package/dist/domains/tokens/scanner.js +168 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +87 -58
- package/dist/services/aipty.d.ts +76 -0
- package/dist/services/aipty.js +276 -0
- package/dist/services/archive.d.ts +22 -0
- package/dist/services/archive.js +53 -0
- package/dist/services/auto-update.d.ts +26 -0
- package/dist/services/auto-update.js +117 -0
- package/dist/services/hash.d.ts +36 -0
- package/dist/services/hash.js +63 -0
- package/dist/services/logger.d.ts +28 -0
- package/dist/services/logger.js +102 -0
- package/dist/services/music.d.ts +67 -0
- package/dist/services/music.js +290 -0
- package/dist/services/npm.d.ts +22 -0
- package/dist/services/npm.js +65 -0
- package/dist/services/pty-client.d.ts +66 -0
- package/dist/services/pty-client.js +154 -0
- package/dist/services/pty-server.d.ts +102 -0
- package/dist/services/pty-server.js +613 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/colors.d.ts +43 -0
- package/dist/utils/colors.js +98 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.js +56 -0
- package/dist/utils/paths.d.ts +46 -0
- package/dist/utils/paths.js +89 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +31 -0
- package/package.json +55 -54
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging service for GemKit CLI
|
|
3
|
+
*/
|
|
4
|
+
import { brand } from '../utils/colors.js';
|
|
5
|
+
const LOG_LEVELS = {
|
|
6
|
+
debug: 0,
|
|
7
|
+
info: 1,
|
|
8
|
+
warn: 2,
|
|
9
|
+
error: 3,
|
|
10
|
+
silent: 4,
|
|
11
|
+
};
|
|
12
|
+
let config = {
|
|
13
|
+
level: 'info',
|
|
14
|
+
verbose: false,
|
|
15
|
+
json: false,
|
|
16
|
+
};
|
|
17
|
+
export function configureLogger(options) {
|
|
18
|
+
config = { ...config, ...options };
|
|
19
|
+
}
|
|
20
|
+
function shouldLog(level) {
|
|
21
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[config.level];
|
|
22
|
+
}
|
|
23
|
+
export function debug(message, data) {
|
|
24
|
+
if (!shouldLog('debug'))
|
|
25
|
+
return;
|
|
26
|
+
if (config.json) {
|
|
27
|
+
console.log(JSON.stringify({ level: 'debug', message, data }));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(brand.dim(`[DEBUG] ${message}`));
|
|
31
|
+
if (data && config.verbose) {
|
|
32
|
+
console.log(brand.dim(JSON.stringify(data, null, 2)));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function info(message, data) {
|
|
37
|
+
if (!shouldLog('info'))
|
|
38
|
+
return;
|
|
39
|
+
if (config.json) {
|
|
40
|
+
console.log(JSON.stringify({ level: 'info', message, data }));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log(`${brand.info('ℹ')} ${message}`);
|
|
44
|
+
if (data && config.verbose) {
|
|
45
|
+
console.log(JSON.stringify(data, null, 2));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function success(message, data) {
|
|
50
|
+
if (!shouldLog('info'))
|
|
51
|
+
return;
|
|
52
|
+
if (config.json) {
|
|
53
|
+
console.log(JSON.stringify({ level: 'success', message, data }));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(`${brand.success('✓')} ${message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function warn(message, data) {
|
|
60
|
+
if (!shouldLog('warn'))
|
|
61
|
+
return;
|
|
62
|
+
if (config.json) {
|
|
63
|
+
console.log(JSON.stringify({ level: 'warn', message, data }));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(`${brand.warn('⚠')} ${message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function error(message, data) {
|
|
70
|
+
if (!shouldLog('error'))
|
|
71
|
+
return;
|
|
72
|
+
if (config.json) {
|
|
73
|
+
console.error(JSON.stringify({ level: 'error', message, data }));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.error(`${brand.error('✗')} ${message}`);
|
|
77
|
+
if (data && config.verbose) {
|
|
78
|
+
console.error(JSON.stringify(data, null, 2));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function table(data) {
|
|
83
|
+
if (config.json) {
|
|
84
|
+
console.log(JSON.stringify(data));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.table(data);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function json(data) {
|
|
91
|
+
console.log(JSON.stringify(data, null, 2));
|
|
92
|
+
}
|
|
93
|
+
export const logger = {
|
|
94
|
+
debug,
|
|
95
|
+
info,
|
|
96
|
+
success,
|
|
97
|
+
warn,
|
|
98
|
+
error,
|
|
99
|
+
table,
|
|
100
|
+
json,
|
|
101
|
+
configure: configureLogger,
|
|
102
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elevator Music Player - Cross-platform background audio for long-running processes
|
|
3
|
+
* Supports: Windows (PowerShell), Mac (afplay), Linux (aplay/paplay)
|
|
4
|
+
*/
|
|
5
|
+
export interface ElevatorMusicOptions {
|
|
6
|
+
audioFile?: string;
|
|
7
|
+
loop?: boolean;
|
|
8
|
+
volume?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ElevatorMusic {
|
|
11
|
+
private audioFile;
|
|
12
|
+
private loop;
|
|
13
|
+
private volume;
|
|
14
|
+
private process;
|
|
15
|
+
private isPlaying;
|
|
16
|
+
private loopInterval;
|
|
17
|
+
private hasLock;
|
|
18
|
+
private lockRefreshInterval;
|
|
19
|
+
private triedFallback;
|
|
20
|
+
constructor(options?: ElevatorMusicOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Try to acquire the global music lock
|
|
23
|
+
*/
|
|
24
|
+
private tryAcquireLock;
|
|
25
|
+
/**
|
|
26
|
+
* Try to take over a potentially stale lock
|
|
27
|
+
*/
|
|
28
|
+
private tryTakeStaleLock;
|
|
29
|
+
/**
|
|
30
|
+
* Refresh the lock timestamp
|
|
31
|
+
*/
|
|
32
|
+
private refreshLock;
|
|
33
|
+
/**
|
|
34
|
+
* Release the global music lock
|
|
35
|
+
*/
|
|
36
|
+
private releaseLock;
|
|
37
|
+
/**
|
|
38
|
+
* Get default audio file path
|
|
39
|
+
*/
|
|
40
|
+
private getDefaultAudioFile;
|
|
41
|
+
/**
|
|
42
|
+
* Get platform-specific play command
|
|
43
|
+
*/
|
|
44
|
+
private getPlayCommand;
|
|
45
|
+
/**
|
|
46
|
+
* Start playing music
|
|
47
|
+
*/
|
|
48
|
+
start(options?: {
|
|
49
|
+
force?: boolean;
|
|
50
|
+
}): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Internal loop handler
|
|
53
|
+
*/
|
|
54
|
+
private playLoop;
|
|
55
|
+
/**
|
|
56
|
+
* Stop playing music
|
|
57
|
+
*/
|
|
58
|
+
stop(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Check if currently playing
|
|
61
|
+
*/
|
|
62
|
+
get playing(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Static method to check if any instance is currently playing
|
|
65
|
+
*/
|
|
66
|
+
static isAnyPlaying(): boolean;
|
|
67
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elevator Music Player - Cross-platform background audio for long-running processes
|
|
3
|
+
* Supports: Windows (PowerShell), Mac (afplay), Linux (aplay/paplay)
|
|
4
|
+
*/
|
|
5
|
+
import { spawn, exec } from 'child_process';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { tmpdir, platform } from 'os';
|
|
9
|
+
// Global lock file path for cross-process coordination
|
|
10
|
+
const LOCK_FILE = join(tmpdir(), 'elevator-music.lock');
|
|
11
|
+
const LOCK_STALE_MS = 30000; // Consider lock stale after 30 seconds
|
|
12
|
+
export class ElevatorMusic {
|
|
13
|
+
audioFile;
|
|
14
|
+
loop;
|
|
15
|
+
volume;
|
|
16
|
+
process = null;
|
|
17
|
+
isPlaying = false;
|
|
18
|
+
loopInterval = null;
|
|
19
|
+
hasLock = false;
|
|
20
|
+
lockRefreshInterval = null;
|
|
21
|
+
triedFallback = false;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.audioFile = options.audioFile || this.getDefaultAudioFile();
|
|
24
|
+
this.loop = options.loop !== false; // Default to looping
|
|
25
|
+
this.volume = options.volume || 0.5; // 0.0 to 1.0
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Try to acquire the global music lock
|
|
29
|
+
*/
|
|
30
|
+
tryAcquireLock() {
|
|
31
|
+
const lockData = {
|
|
32
|
+
pid: process.pid,
|
|
33
|
+
timestamp: Date.now()
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
writeFileSync(LOCK_FILE, JSON.stringify(lockData), { flag: 'wx' });
|
|
37
|
+
this.hasLock = true;
|
|
38
|
+
this.lockRefreshInterval = setInterval(() => {
|
|
39
|
+
this.refreshLock();
|
|
40
|
+
}, LOCK_STALE_MS / 3);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err.code === 'EEXIST') {
|
|
45
|
+
return this.tryTakeStaleLock(lockData);
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Try to take over a potentially stale lock
|
|
52
|
+
*/
|
|
53
|
+
tryTakeStaleLock(newLockData) {
|
|
54
|
+
try {
|
|
55
|
+
const existingLock = JSON.parse(readFileSync(LOCK_FILE, 'utf8'));
|
|
56
|
+
const lockAge = Date.now() - existingLock.timestamp;
|
|
57
|
+
if (lockAge < LOCK_STALE_MS && existingLock.pid !== process.pid) {
|
|
58
|
+
try {
|
|
59
|
+
process.kill(existingLock.pid, 0);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Process doesn't exist, lock is stale
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
unlinkSync(LOCK_FILE);
|
|
67
|
+
try {
|
|
68
|
+
writeFileSync(LOCK_FILE, JSON.stringify(newLockData), { flag: 'wx' });
|
|
69
|
+
this.hasLock = true;
|
|
70
|
+
this.lockRefreshInterval = setInterval(() => {
|
|
71
|
+
this.refreshLock();
|
|
72
|
+
}, LOCK_STALE_MS / 3);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Refresh the lock timestamp
|
|
85
|
+
*/
|
|
86
|
+
refreshLock() {
|
|
87
|
+
if (!this.hasLock)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
const lockData = {
|
|
91
|
+
pid: process.pid,
|
|
92
|
+
timestamp: Date.now()
|
|
93
|
+
};
|
|
94
|
+
writeFileSync(LOCK_FILE, JSON.stringify(lockData), { flag: 'w' });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Ignore refresh errors
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Release the global music lock
|
|
102
|
+
*/
|
|
103
|
+
releaseLock() {
|
|
104
|
+
if (this.lockRefreshInterval) {
|
|
105
|
+
clearInterval(this.lockRefreshInterval);
|
|
106
|
+
this.lockRefreshInterval = null;
|
|
107
|
+
}
|
|
108
|
+
if (!this.hasLock)
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
if (existsSync(LOCK_FILE)) {
|
|
112
|
+
const lockData = JSON.parse(readFileSync(LOCK_FILE, 'utf8'));
|
|
113
|
+
if (lockData.pid === process.pid) {
|
|
114
|
+
unlinkSync(LOCK_FILE);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Ignore cleanup errors
|
|
120
|
+
}
|
|
121
|
+
this.hasLock = false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get default audio file path
|
|
125
|
+
*/
|
|
126
|
+
getDefaultAudioFile() {
|
|
127
|
+
// Check common locations for audio file
|
|
128
|
+
const searchPaths = [
|
|
129
|
+
join(process.cwd(), '.gemini', 'extensions', 'spawn-agent', 'assets', 'golden-coast-melody.wav'),
|
|
130
|
+
join(process.cwd(), '.gemini', 'assets', 'elevator_music.wav'),
|
|
131
|
+
join(process.cwd(), 'assets', 'elevator_music.wav'),
|
|
132
|
+
];
|
|
133
|
+
for (const p of searchPaths) {
|
|
134
|
+
if (existsSync(p)) {
|
|
135
|
+
return p;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get platform-specific play command
|
|
142
|
+
*/
|
|
143
|
+
getPlayCommand() {
|
|
144
|
+
if (!this.audioFile)
|
|
145
|
+
return null;
|
|
146
|
+
const plat = platform();
|
|
147
|
+
switch (plat) {
|
|
148
|
+
case 'win32':
|
|
149
|
+
return {
|
|
150
|
+
command: 'powershell',
|
|
151
|
+
args: [
|
|
152
|
+
'-NoProfile',
|
|
153
|
+
'-Command',
|
|
154
|
+
`$player = New-Object System.Media.SoundPlayer '${this.audioFile}'; $player.PlaySync()`
|
|
155
|
+
],
|
|
156
|
+
shell: false
|
|
157
|
+
};
|
|
158
|
+
case 'darwin':
|
|
159
|
+
return {
|
|
160
|
+
command: 'afplay',
|
|
161
|
+
args: ['-v', String(this.volume), this.audioFile],
|
|
162
|
+
shell: false
|
|
163
|
+
};
|
|
164
|
+
case 'linux':
|
|
165
|
+
return {
|
|
166
|
+
command: 'paplay',
|
|
167
|
+
args: ['--volume', String(Math.round(this.volume * 65536)), this.audioFile],
|
|
168
|
+
shell: false,
|
|
169
|
+
fallback: {
|
|
170
|
+
command: 'aplay',
|
|
171
|
+
args: ['-q', this.audioFile],
|
|
172
|
+
shell: false
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
default:
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Start playing music
|
|
181
|
+
*/
|
|
182
|
+
start(options = {}) {
|
|
183
|
+
if (!this.audioFile) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if (!existsSync(this.audioFile)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (this.isPlaying) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
if (!options.force && !this.tryAcquireLock()) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
const playConfig = this.getPlayCommand();
|
|
196
|
+
if (!playConfig) {
|
|
197
|
+
this.releaseLock();
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
this.isPlaying = true;
|
|
201
|
+
this.playLoop(playConfig);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Internal loop handler
|
|
206
|
+
*/
|
|
207
|
+
playLoop(playConfig) {
|
|
208
|
+
if (!this.isPlaying)
|
|
209
|
+
return;
|
|
210
|
+
const startPlay = (config) => {
|
|
211
|
+
this.process = spawn(config.command, config.args, {
|
|
212
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
213
|
+
windowsHide: true,
|
|
214
|
+
shell: config.shell
|
|
215
|
+
});
|
|
216
|
+
this.process.stderr?.on('data', () => {
|
|
217
|
+
if (config.fallback && !this.triedFallback) {
|
|
218
|
+
this.triedFallback = true;
|
|
219
|
+
this.process?.kill();
|
|
220
|
+
startPlay(config.fallback);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
this.process.on('close', () => {
|
|
224
|
+
if (this.isPlaying && this.loop) {
|
|
225
|
+
this.loopInterval = setTimeout(() => {
|
|
226
|
+
this.playLoop(playConfig);
|
|
227
|
+
}, 100);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this.process.on('error', () => {
|
|
231
|
+
if (config.fallback && !this.triedFallback) {
|
|
232
|
+
this.triedFallback = true;
|
|
233
|
+
startPlay(config.fallback);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
startPlay(playConfig);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Stop playing music
|
|
241
|
+
*/
|
|
242
|
+
stop() {
|
|
243
|
+
this.isPlaying = false;
|
|
244
|
+
if (this.loopInterval) {
|
|
245
|
+
clearTimeout(this.loopInterval);
|
|
246
|
+
this.loopInterval = null;
|
|
247
|
+
}
|
|
248
|
+
if (this.process) {
|
|
249
|
+
if (platform() === 'win32') {
|
|
250
|
+
exec(`taskkill /pid ${this.process.pid} /T /F`, { windowsHide: true });
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
this.process.kill('SIGTERM');
|
|
254
|
+
}
|
|
255
|
+
this.process = null;
|
|
256
|
+
}
|
|
257
|
+
this.releaseLock();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if currently playing
|
|
261
|
+
*/
|
|
262
|
+
get playing() {
|
|
263
|
+
return this.isPlaying;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Static method to check if any instance is currently playing
|
|
267
|
+
*/
|
|
268
|
+
static isAnyPlaying() {
|
|
269
|
+
try {
|
|
270
|
+
if (!existsSync(LOCK_FILE)) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
const lockData = JSON.parse(readFileSync(LOCK_FILE, 'utf8'));
|
|
274
|
+
const lockAge = Date.now() - lockData.timestamp;
|
|
275
|
+
if (lockAge >= LOCK_STALE_MS) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
process.kill(lockData.pid, 0);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NPM Registry service - Check package versions
|
|
3
|
+
*/
|
|
4
|
+
export interface NpmPackageInfo {
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
publishedAt?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get latest version of a package from npm registry
|
|
12
|
+
*/
|
|
13
|
+
export declare function getLatestNpmVersion(packageName: string): Promise<NpmPackageInfo | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Compare two semver versions
|
|
16
|
+
* Returns: 1 if a > b, -1 if a < b, 0 if equal
|
|
17
|
+
*/
|
|
18
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
19
|
+
/**
|
|
20
|
+
* Check if update is available
|
|
21
|
+
*/
|
|
22
|
+
export declare function isUpdateAvailable(current: string, latest: string): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NPM Registry service - Check package versions
|
|
3
|
+
*/
|
|
4
|
+
import { getCache, setCache } from '../domains/cache/manager.js';
|
|
5
|
+
const NPM_REGISTRY = 'https://registry.npmjs.org';
|
|
6
|
+
const CACHE_KEY = 'npm-version';
|
|
7
|
+
const CACHE_TTL = 3600; // 1 hour
|
|
8
|
+
/**
|
|
9
|
+
* Get latest version of a package from npm registry
|
|
10
|
+
*/
|
|
11
|
+
export async function getLatestNpmVersion(packageName) {
|
|
12
|
+
const cacheKey = `${CACHE_KEY}-${packageName}`;
|
|
13
|
+
// Check cache first
|
|
14
|
+
const cached = getCache(cacheKey);
|
|
15
|
+
if (cached) {
|
|
16
|
+
return cached;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`${NPM_REGISTRY}/${packageName}/latest`, {
|
|
20
|
+
headers: {
|
|
21
|
+
'Accept': 'application/json',
|
|
22
|
+
'User-Agent': 'gemkit-cli',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
const info = {
|
|
30
|
+
name: data.name,
|
|
31
|
+
version: data.version,
|
|
32
|
+
description: data.description,
|
|
33
|
+
publishedAt: data.time?.[data.version],
|
|
34
|
+
};
|
|
35
|
+
// Cache result
|
|
36
|
+
setCache(cacheKey, info, CACHE_TTL);
|
|
37
|
+
return info;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Compare two semver versions
|
|
45
|
+
* Returns: 1 if a > b, -1 if a < b, 0 if equal
|
|
46
|
+
*/
|
|
47
|
+
export function compareVersions(a, b) {
|
|
48
|
+
const partsA = a.replace(/^v/, '').split('.').map(Number);
|
|
49
|
+
const partsB = b.replace(/^v/, '').split('.').map(Number);
|
|
50
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
51
|
+
const numA = partsA[i] || 0;
|
|
52
|
+
const numB = partsB[i] || 0;
|
|
53
|
+
if (numA > numB)
|
|
54
|
+
return 1;
|
|
55
|
+
if (numA < numB)
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if update is available
|
|
62
|
+
*/
|
|
63
|
+
export function isUpdateAvailable(current, latest) {
|
|
64
|
+
return compareVersions(latest, current) > 0;
|
|
65
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PTY Client - Sends commands to the PTY server
|
|
3
|
+
* Ported from claude-pty-wrapper/client.js
|
|
4
|
+
*/
|
|
5
|
+
import type { PtyExchange, PtyServerStatus, PtySendResponse, PtyCompleteResponse, PtyPendingResponse } from '../domains/agent/pty-types.js';
|
|
6
|
+
export declare class PtyClient {
|
|
7
|
+
private port;
|
|
8
|
+
private host;
|
|
9
|
+
constructor(port?: number, host?: string);
|
|
10
|
+
/**
|
|
11
|
+
* Send command to server and get response
|
|
12
|
+
*/
|
|
13
|
+
sendCommand(command: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if server is running
|
|
16
|
+
*/
|
|
17
|
+
isServerRunning(): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Get server status
|
|
20
|
+
*/
|
|
21
|
+
status(): Promise<PtyServerStatus>;
|
|
22
|
+
/**
|
|
23
|
+
* Send prompt to session
|
|
24
|
+
*/
|
|
25
|
+
send(prompt: string): Promise<PtySendResponse>;
|
|
26
|
+
/**
|
|
27
|
+
* Wait for completion
|
|
28
|
+
*/
|
|
29
|
+
waitForCompletion(timeout?: number): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Check if response is complete
|
|
32
|
+
*/
|
|
33
|
+
complete(): Promise<PtyCompleteResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Get structured exchange output
|
|
36
|
+
*/
|
|
37
|
+
exchange(): Promise<PtyExchange>;
|
|
38
|
+
/**
|
|
39
|
+
* Check for pending tool confirmations
|
|
40
|
+
*/
|
|
41
|
+
pending(): Promise<PtyPendingResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Read raw output
|
|
44
|
+
*/
|
|
45
|
+
read(lines?: number): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Get exchange history
|
|
48
|
+
*/
|
|
49
|
+
history(): Promise<{
|
|
50
|
+
provider: string;
|
|
51
|
+
count: number;
|
|
52
|
+
exchanges: PtyExchange[];
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Clear exchange history
|
|
56
|
+
*/
|
|
57
|
+
clearHistory(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Stop the server
|
|
60
|
+
*/
|
|
61
|
+
stop(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Sleep helper
|
|
64
|
+
*/
|
|
65
|
+
private sleep;
|
|
66
|
+
}
|