tide-commander 0.52.0 ā 0.53.2
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/index.html
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<meta name="apple-mobile-web-app-title" content="Tide CMD" />
|
|
20
20
|
<link rel="apple-touch-icon" href="/assets/icons/icon-192.png" />
|
|
21
21
|
<title>Tide Commander</title>
|
|
22
|
-
<script type="module" crossorigin src="/assets/main-
|
|
22
|
+
<script type="module" crossorigin src="/assets/main-rd_RRZq_.js"></script>
|
|
23
23
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-uS-d4TUT.js">
|
|
24
24
|
<link rel="modulepreload" crossorigin href="/assets/vendor-three-4iQNXcoo.js">
|
|
25
25
|
<link rel="stylesheet" crossorigin href="/assets/main-BIpLsrUu.css">
|
|
@@ -1,25 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
3
5
|
import path from 'node:path';
|
|
4
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
const PID_DIR = path.join(os.homedir(), '.local', 'share', 'tide-commander');
|
|
8
|
+
const PID_FILE = path.join(PID_DIR, 'server.pid');
|
|
9
|
+
const LOG_FILE = path.join(process.cwd(), 'logs', 'server.log');
|
|
5
10
|
function printHelp() {
|
|
6
11
|
console.log(`Tide Commander
|
|
7
12
|
|
|
8
13
|
Usage:
|
|
9
|
-
tide-commander [options]
|
|
14
|
+
tide-commander [start] [options]
|
|
15
|
+
tide-commander stop
|
|
16
|
+
tide-commander status
|
|
17
|
+
tide-commander logs [--lines <n>] [--follow]
|
|
10
18
|
|
|
11
19
|
Options:
|
|
12
20
|
-p, --port <port> Set server port (default: 5174)
|
|
13
21
|
-H, --host <host> Set server host (default: 127.0.0.1)
|
|
14
22
|
-l, --listen-all Listen on all network interfaces
|
|
15
23
|
-f, --foreground Run in foreground (default is background)
|
|
24
|
+
--lines <n> Number of log lines for logs command (default: 100)
|
|
25
|
+
--follow Follow logs stream (like tail -f)
|
|
16
26
|
-h, --help Show this help message
|
|
17
27
|
`);
|
|
18
28
|
}
|
|
19
29
|
function parseArgs(argv) {
|
|
20
|
-
const options = {};
|
|
30
|
+
const options = { command: 'start' };
|
|
31
|
+
let commandParsed = false;
|
|
21
32
|
for (let i = 0; i < argv.length; i += 1) {
|
|
22
33
|
const arg = argv[i];
|
|
34
|
+
if (!arg.startsWith('-') && !commandParsed) {
|
|
35
|
+
if (arg === 'start' || arg === 'stop' || arg === 'status' || arg === 'logs') {
|
|
36
|
+
options.command = arg;
|
|
37
|
+
commandParsed = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Unknown command: ${arg}`);
|
|
41
|
+
}
|
|
23
42
|
switch (arg) {
|
|
24
43
|
case '-p':
|
|
25
44
|
case '--port': {
|
|
@@ -47,8 +66,29 @@ function parseArgs(argv) {
|
|
|
47
66
|
break;
|
|
48
67
|
case '-f':
|
|
49
68
|
case '--foreground':
|
|
50
|
-
options.
|
|
69
|
+
if (options.command === 'logs') {
|
|
70
|
+
options.follow = true;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
options.foreground = true;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case '--follow':
|
|
77
|
+
options.follow = true;
|
|
51
78
|
break;
|
|
79
|
+
case '--lines': {
|
|
80
|
+
const value = argv[i + 1];
|
|
81
|
+
if (!value || value.startsWith('-')) {
|
|
82
|
+
throw new Error(`Missing value for ${arg}`);
|
|
83
|
+
}
|
|
84
|
+
const lines = Number(value);
|
|
85
|
+
if (!Number.isInteger(lines) || lines < 1) {
|
|
86
|
+
throw new Error(`Invalid lines value: ${value}`);
|
|
87
|
+
}
|
|
88
|
+
options.lines = lines;
|
|
89
|
+
i += 1;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
52
92
|
case '-h':
|
|
53
93
|
case '--help':
|
|
54
94
|
options.help = true;
|
|
@@ -65,12 +105,106 @@ function validatePort(value) {
|
|
|
65
105
|
throw new Error(`Invalid port: ${value}`);
|
|
66
106
|
}
|
|
67
107
|
}
|
|
108
|
+
function ensurePidDir() {
|
|
109
|
+
fs.mkdirSync(PID_DIR, { recursive: true });
|
|
110
|
+
}
|
|
111
|
+
function writePidFile(pid) {
|
|
112
|
+
ensurePidDir();
|
|
113
|
+
fs.writeFileSync(PID_FILE, `${pid}\n`, 'utf8');
|
|
114
|
+
}
|
|
115
|
+
function clearPidFile() {
|
|
116
|
+
try {
|
|
117
|
+
fs.rmSync(PID_FILE, { force: true });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// no-op
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function readPidFile() {
|
|
124
|
+
try {
|
|
125
|
+
const raw = fs.readFileSync(PID_FILE, 'utf8').trim();
|
|
126
|
+
const pid = Number(raw);
|
|
127
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function isRunning(pid) {
|
|
134
|
+
try {
|
|
135
|
+
process.kill(pid, 0);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function stopCommand() {
|
|
143
|
+
const pid = readPidFile();
|
|
144
|
+
if (!pid) {
|
|
145
|
+
console.log('Tide Commander is not running');
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
if (!isRunning(pid)) {
|
|
149
|
+
clearPidFile();
|
|
150
|
+
console.log('Removed stale PID file');
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
process.kill(pid, 'SIGTERM');
|
|
154
|
+
console.log(`Sent SIGTERM to Tide Commander (PID: ${pid})`);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
function statusCommand() {
|
|
158
|
+
const pid = readPidFile();
|
|
159
|
+
if (!pid) {
|
|
160
|
+
console.log('Tide Commander is stopped');
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
if (!isRunning(pid)) {
|
|
164
|
+
clearPidFile();
|
|
165
|
+
console.log('Tide Commander is stopped (stale PID file removed)');
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
console.log(`Tide Commander is running (PID: ${pid})`);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
async function logsCommand(options) {
|
|
172
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
173
|
+
console.error(`Log file not found: ${LOG_FILE}`);
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
const lines = options.lines ?? 100;
|
|
177
|
+
const args = ['-n', String(lines)];
|
|
178
|
+
if (options.follow) {
|
|
179
|
+
args.push('-f');
|
|
180
|
+
}
|
|
181
|
+
args.push(LOG_FILE);
|
|
182
|
+
const tail = spawn('tail', args, { stdio: 'inherit' });
|
|
183
|
+
return await new Promise((resolve) => {
|
|
184
|
+
tail.on('error', (error) => {
|
|
185
|
+
console.error(`Failed to read logs: ${error.message}`);
|
|
186
|
+
resolve(1);
|
|
187
|
+
});
|
|
188
|
+
tail.on('exit', (code) => {
|
|
189
|
+
resolve(code ?? 0);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
68
193
|
async function main() {
|
|
69
194
|
const options = parseArgs(process.argv.slice(2));
|
|
70
195
|
if (options.help) {
|
|
71
196
|
printHelp();
|
|
72
197
|
return;
|
|
73
198
|
}
|
|
199
|
+
if (options.command === 'stop') {
|
|
200
|
+
process.exit(stopCommand());
|
|
201
|
+
}
|
|
202
|
+
if (options.command === 'status') {
|
|
203
|
+
process.exit(statusCommand());
|
|
204
|
+
}
|
|
205
|
+
if (options.command === 'logs') {
|
|
206
|
+
process.exit(await logsCommand(options));
|
|
207
|
+
}
|
|
74
208
|
if (options.port) {
|
|
75
209
|
validatePort(options.port);
|
|
76
210
|
process.env.PORT = options.port;
|
|
@@ -85,6 +219,12 @@ async function main() {
|
|
|
85
219
|
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
86
220
|
const serverEntry = path.join(cliDir, 'index.js');
|
|
87
221
|
const runInForeground = options.foreground === true || process.env.TIDE_COMMANDER_FOREGROUND === '1';
|
|
222
|
+
const existingPid = readPidFile();
|
|
223
|
+
if (existingPid && isRunning(existingPid)) {
|
|
224
|
+
console.log(`Tide Commander is already running (PID: ${existingPid})`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
clearPidFile();
|
|
88
228
|
const child = spawn(process.execPath, ['--experimental-specifier-resolution=node', serverEntry], {
|
|
89
229
|
stdio: runInForeground ? 'inherit' : 'ignore',
|
|
90
230
|
detached: !runInForeground,
|
|
@@ -95,11 +235,26 @@ async function main() {
|
|
|
95
235
|
process.exit(1);
|
|
96
236
|
});
|
|
97
237
|
if (!runInForeground) {
|
|
238
|
+
if (child.pid) {
|
|
239
|
+
writePidFile(child.pid);
|
|
240
|
+
}
|
|
98
241
|
child.unref();
|
|
99
|
-
|
|
242
|
+
const port = process.env.PORT || '5174';
|
|
243
|
+
const host = process.env.HOST || 'localhost';
|
|
244
|
+
const url = `http://${host}:${port}`;
|
|
245
|
+
console.log('\nš Tide Commander');
|
|
246
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
247
|
+
console.log(`ā Started in background (PID: ${child.pid ?? 'unknown'})`);
|
|
248
|
+
console.log(`š Open: ${url}`);
|
|
249
|
+
console.log(`š Logs: tail -f logs/server.log`);
|
|
250
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
100
251
|
return;
|
|
101
252
|
}
|
|
253
|
+
if (child.pid) {
|
|
254
|
+
writePidFile(child.pid);
|
|
255
|
+
}
|
|
102
256
|
child.on('exit', (code, signal) => {
|
|
257
|
+
clearPidFile();
|
|
103
258
|
if (signal) {
|
|
104
259
|
process.kill(process.pid, signal);
|
|
105
260
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tide-commander",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.2",
|
|
4
4
|
"description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,20 +27,10 @@
|
|
|
27
27
|
"test:ci": "vitest run --exclude src/packages/client/hooks/__tests__/useSnapshots.test.ts --exclude src/packages/client/hooks/__tests__/useKeyboardShortcuts.test.ts",
|
|
28
28
|
"test:watch": "vitest",
|
|
29
29
|
"test:coverage": "vitest run --coverage",
|
|
30
|
-
"prepack": "npm run build"
|
|
31
|
-
"cap:sync": "npx cap sync android",
|
|
32
|
-
"cap:open": "npx cap open android",
|
|
33
|
-
"cap:build": "npm run build && npx cap sync android",
|
|
34
|
-
"android": "npm run build && npx cap sync android && npx cap open android"
|
|
30
|
+
"prepack": "npm run build"
|
|
35
31
|
},
|
|
36
32
|
"dependencies": {
|
|
37
33
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
38
|
-
"@capacitor/android": "^8.0.1",
|
|
39
|
-
"@capacitor/cli": "^8.0.1",
|
|
40
|
-
"@capacitor/core": "^8.0.1",
|
|
41
|
-
"@capacitor/haptics": "^8.0.0",
|
|
42
|
-
"@capacitor/local-notifications": "^8.0.0",
|
|
43
|
-
"@capawesome/capacitor-background-task": "^8.0.0",
|
|
44
34
|
"@tanstack/react-virtual": "^3.13.18",
|
|
45
35
|
"@types/oracledb": "^6.10.1",
|
|
46
36
|
"archiver": "^7.0.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{W as a,I as i,N as r}from"./main-DMTRw3br.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-4iQNXcoo.js";class l extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{l as HapticsWeb};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{W as s}from"./main-DMTRw3br.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-4iQNXcoo.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
|