@ts47andres/exeggutor 1.1.3 → 1.1.5

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.
@@ -1,138 +1,138 @@
1
- import { execSync } from 'child_process';
2
- import { existsSync } from 'fs';
3
- import * as path from 'path';
4
- import * as os from 'os';
5
-
6
- // Describes the parsed result of `tailscale status --json` for the local node.
7
- export interface TailscaleInfo {
8
- ip: string; // Tailscale IPv4 address of the local machine.
9
- dnsName: string; // MagicDNS hostname (e.g. "hostname.tailnet.ts.net").
10
- online: boolean; // Whether this node is currently connected to the tailnet.
11
- tailnetName: string; // Human-readable tailnet name from status.
12
- }
13
-
14
- // Resolved path to the tailscale binary, or null if not found.
15
- let _tailscalePath: string | null | undefined = undefined;
16
-
17
- // Common installation paths for the tailscale CLI across platforms.
18
- function getCommonTailscalePaths(): string[] {
19
- const platform = os.platform(); // Operating system identifier.
20
- const paths: string[] = [];
21
- if (platform === 'win32') {
22
- paths.push('C:\\Program Files\\Tailscale\\tailscale.exe');
23
- const programFilesX86 = process.env['ProgramFiles(x86)'];
24
- if (programFilesX86) {
25
- paths.push(path.join(programFilesX86, 'Tailscale', 'tailscale.exe'));
26
- }
27
- } else if (platform === 'darwin') {
28
- paths.push('/Applications/Tailscale.app/Contents/MacOS/Tailscale');
29
- paths.push('/usr/local/bin/tailscale');
30
- } else {
31
- paths.push('/usr/bin/tailscale');
32
- paths.push('/usr/local/bin/tailscale');
33
- paths.push('/opt/tailscale/tailscale');
34
- }
35
- return paths;
36
- }
37
-
38
- // Locates the tailscale binary by checking common install paths in addition to PATH.
39
- function findTailscaleBinary(): string | null {
40
- if (_tailscalePath !== undefined) {
41
- return _tailscalePath;
42
- }
43
- try {
44
- execSync('tailscale version', { stdio: 'ignore', timeout: 3000 });
45
- _tailscalePath = 'tailscale';
46
- return _tailscalePath;
47
- } catch {
48
- // Not in PATH, check common install locations.
49
- }
50
- for (const candidate of getCommonTailscalePaths()) {
51
- if (existsSync(candidate)) {
52
- _tailscalePath = candidate;
53
- return _tailscalePath;
54
- }
55
- }
56
- _tailscalePath = null;
57
- return null;
58
- }
59
-
60
- // Returns true if the `tailscale` CLI binary is found on the system.
61
- export function isTailscaleInstalled(): boolean {
62
- return findTailscaleBinary() !== null;
63
- }
64
-
65
- // Resolves the tailscale CLI binary path for executing commands.
66
- function tailscaleBin(): string {
67
- const bin = findTailscaleBinary(); // Located tailscale binary path.
68
- if (!bin) {
69
- throw new Error('Tailscale CLI not found');
70
- }
71
- return bin;
72
- }
73
-
74
- // Parses the local node state from `tailscale status --json`.
75
- // Returns null if Tailscale is not running or the binary is unavailable.
76
- export function getTailscaleInfo(): TailscaleInfo | null {
77
- if (!isTailscaleInstalled()) {
78
- return null;
79
- }
80
- try {
81
- const raw = execSync(`"${tailscaleBin()}" status --json`, {
82
- encoding: 'utf8',
83
- timeout: 5000,
84
- }); // Raw JSON output from the tailscale status command.
85
-
86
- const parsed = JSON.parse(raw); // Parsed status payload from the tailscale daemon.
87
-
88
- const self = parsed.Self; // The local node descriptor from the status object.
89
- if (!self) {
90
- return null;
91
- }
92
-
93
- const ipv4 = (self.TailscaleIPs || []).find((addr: string) =>
94
- addr.startsWith('100.')
95
- ) as string | undefined; // First 100.x.x.x Tailscale IP assigned to this node.
96
-
97
- const dnsNameRaw = self.DNSName || ''; // Raw DNS name with trailing dot.
98
- const dnsName = dnsNameRaw.replace(/\.$/, ''); // Strip the trailing dot.
99
-
100
- const tailnetName = dnsName.split('.').slice(1).join('.') || 'unknown'; // Tailnet identifier extracted from the DNS name.
101
-
102
- return {
103
- ip: ipv4 || 'unknown',
104
- dnsName: dnsName || 'unknown',
105
- online: self.Online !== false,
106
- tailnetName,
107
- };
108
- } catch {
109
- return null;
110
- }
111
- }
112
-
113
- // Returns the Tailscale IPv4 address of this node, or null if unavailable.
114
- export function getTailscaleIP(): string | null {
115
- const info = getTailscaleInfo(); // Parsed Tailscale status for the local node.
116
- return info ? info.ip : null;
117
- }
118
-
119
- // Returns the MagicDNS hostname (e.g. "mybox.tailnet.ts.net") or null.
120
- export function getMagicDNSName(): string | null {
121
- const info = getTailscaleInfo(); // Parsed Tailscale status for the local node.
122
- return info ? info.dnsName : null;
123
- }
124
-
125
- // Returns the full URL to access Exeggutor via Tailscale, or null.
126
- export function getTailscaleURL(port: number): string | null {
127
- const dnsName = getMagicDNSName(); // MagicDNS hostname of the local node.
128
- if (dnsName) {
129
- return `https://${dnsName}:${port}`;
130
- }
131
- const ip = getTailscaleIP(); // Tailscale IP of the local node.
132
- if (ip) {
133
- return `http://${ip}:${port}`;
134
- }
135
- return null;
136
- }
137
-
138
-
1
+ import { execSync } from 'child_process';
2
+ import { existsSync } from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+
6
+ // Describes the parsed result of `tailscale status --json` for the local node.
7
+ export interface TailscaleInfo {
8
+ ip: string; // Tailscale IPv4 address of the local machine.
9
+ dnsName: string; // MagicDNS hostname (e.g. "hostname.tailnet.ts.net").
10
+ online: boolean; // Whether this node is currently connected to the tailnet.
11
+ tailnetName: string; // Human-readable tailnet name from status.
12
+ }
13
+
14
+ // Resolved path to the tailscale binary, or null if not found.
15
+ let _tailscalePath: string | null | undefined = undefined;
16
+
17
+ // Common installation paths for the tailscale CLI across platforms.
18
+ function getCommonTailscalePaths(): string[] {
19
+ const platform = os.platform(); // Operating system identifier.
20
+ const paths: string[] = [];
21
+ if (platform === 'win32') {
22
+ paths.push('C:\\Program Files\\Tailscale\\tailscale.exe');
23
+ const programFilesX86 = process.env['ProgramFiles(x86)'];
24
+ if (programFilesX86) {
25
+ paths.push(path.join(programFilesX86, 'Tailscale', 'tailscale.exe'));
26
+ }
27
+ } else if (platform === 'darwin') {
28
+ paths.push('/Applications/Tailscale.app/Contents/MacOS/Tailscale');
29
+ paths.push('/usr/local/bin/tailscale');
30
+ } else {
31
+ paths.push('/usr/bin/tailscale');
32
+ paths.push('/usr/local/bin/tailscale');
33
+ paths.push('/opt/tailscale/tailscale');
34
+ }
35
+ return paths;
36
+ }
37
+
38
+ // Locates the tailscale binary by checking common install paths in addition to PATH.
39
+ function findTailscaleBinary(): string | null {
40
+ if (_tailscalePath !== undefined) {
41
+ return _tailscalePath;
42
+ }
43
+ try {
44
+ execSync('tailscale version', { stdio: 'ignore', timeout: 3000 });
45
+ _tailscalePath = 'tailscale';
46
+ return _tailscalePath;
47
+ } catch {
48
+ // Not in PATH, check common install locations.
49
+ }
50
+ for (const candidate of getCommonTailscalePaths()) {
51
+ if (existsSync(candidate)) {
52
+ _tailscalePath = candidate;
53
+ return _tailscalePath;
54
+ }
55
+ }
56
+ _tailscalePath = null;
57
+ return null;
58
+ }
59
+
60
+ // Returns true if the `tailscale` CLI binary is found on the system.
61
+ export function isTailscaleInstalled(): boolean {
62
+ return findTailscaleBinary() !== null;
63
+ }
64
+
65
+ // Resolves the tailscale CLI binary path for executing commands.
66
+ function tailscaleBin(): string {
67
+ const bin = findTailscaleBinary(); // Located tailscale binary path.
68
+ if (!bin) {
69
+ throw new Error('Tailscale CLI not found');
70
+ }
71
+ return bin;
72
+ }
73
+
74
+ // Parses the local node state from `tailscale status --json`.
75
+ // Returns null if Tailscale is not running or the binary is unavailable.
76
+ export function getTailscaleInfo(): TailscaleInfo | null {
77
+ if (!isTailscaleInstalled()) {
78
+ return null;
79
+ }
80
+ try {
81
+ const raw = execSync(`"${tailscaleBin()}" status --json`, {
82
+ encoding: 'utf8',
83
+ timeout: 5000,
84
+ }); // Raw JSON output from the tailscale status command.
85
+
86
+ const parsed = JSON.parse(raw); // Parsed status payload from the tailscale daemon.
87
+
88
+ const self = parsed.Self; // The local node descriptor from the status object.
89
+ if (!self) {
90
+ return null;
91
+ }
92
+
93
+ const ipv4 = (self.TailscaleIPs || []).find((addr: string) =>
94
+ addr.startsWith('100.')
95
+ ) as string | undefined; // First 100.x.x.x Tailscale IP assigned to this node.
96
+
97
+ const dnsNameRaw = self.DNSName || ''; // Raw DNS name with trailing dot.
98
+ const dnsName = dnsNameRaw.replace(/\.$/, ''); // Strip the trailing dot.
99
+
100
+ const tailnetName = dnsName.split('.').slice(1).join('.') || 'unknown'; // Tailnet identifier extracted from the DNS name.
101
+
102
+ return {
103
+ ip: ipv4 || 'unknown',
104
+ dnsName: dnsName || 'unknown',
105
+ online: self.Online !== false,
106
+ tailnetName,
107
+ };
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ // Returns the Tailscale IPv4 address of this node, or null if unavailable.
114
+ export function getTailscaleIP(): string | null {
115
+ const info = getTailscaleInfo(); // Parsed Tailscale status for the local node.
116
+ return info ? info.ip : null;
117
+ }
118
+
119
+ // Returns the MagicDNS hostname (e.g. "mybox.tailnet.ts.net") or null.
120
+ export function getMagicDNSName(): string | null {
121
+ const info = getTailscaleInfo(); // Parsed Tailscale status for the local node.
122
+ return info ? info.dnsName : null;
123
+ }
124
+
125
+ // Returns the full URL to access Exeggutor via Tailscale, or null.
126
+ export function getTailscaleURL(port: number): string | null {
127
+ const dnsName = getMagicDNSName(); // MagicDNS hostname of the local node.
128
+ if (dnsName) {
129
+ return `https://${dnsName}:${port}`;
130
+ }
131
+ const ip = getTailscaleIP(); // Tailscale IP of the local node.
132
+ if (ip) {
133
+ return `http://${ip}:${port}`;
134
+ }
135
+ return null;
136
+ }
137
+
138
+
@@ -1,151 +1,151 @@
1
- import * as fs from 'fs';
2
- import * as os from 'os';
3
- import * as path from 'path';
4
-
5
- export interface TerminalTab {
6
- id: string; // Unique identifier for the terminal tab.
7
- name: string; // User-facing name of the terminal tab.
8
- cwd: string; // Current working directory for this terminal session.
9
- shell?: string; // Optional shell path override for the terminal.
10
- branch?: string; // Target Git branch assigned to this individual terminal tab.
11
- worktreePath?: string; // Path to the generated git worktree for this tab if isolated.
12
- pid?: number; // The process ID of the active shell process.
13
- }
14
-
15
- export interface Workspace {
16
- id: string; // Unique identifier for the workspace.
17
- name: string; // User-facing name of the workspace.
18
- path: string; // Core absolute path to the workspace code directory.
19
- layout?: any; // The react-mosaic-component layout state for this workspace.
20
- tabs: TerminalTab[]; // List of terminal tabs owned by this workspace.
21
- }
22
-
23
- export interface SessionDb {
24
- workspaces: Workspace[]; // Array of workspaces managed in this system session.
25
- activeWorkspaceId?: string; // ID of the currently selected active workspace.
26
- }
27
-
28
- const dbPath = path.join(os.homedir(), '.exeggutor-sessions.json'); // Absolute system path pointing to the sessions flat-file JSON database.
29
-
30
- // Reads and parses the sessions database from the local file system.
31
- export function readDatabase(): SessionDb {
32
- if (!fs.existsSync(dbPath)) {
33
- const initialDb: SessionDb = { workspaces: [] }; // The initial structural database template to write when the file does not exist.
34
- fs.writeFileSync(dbPath, JSON.stringify(initialDb, null, 2), 'utf8');
35
- return initialDb;
36
- }
37
- const rawData = fs.readFileSync(dbPath, 'utf8'); // Loaded string content from the session JSON file.
38
- const parsed = JSON.parse(rawData) as SessionDb; // Parsed sessions database schema object.
39
- return parsed;
40
- }
41
-
42
- // Serializes and saves the database state back to the local file system.
43
- export function writeDatabase(db: SessionDb): void {
44
- const serialized = JSON.stringify(db, null, 2); // Serialized JSON string of the session database structure.
45
- const tempPath = dbPath + '.tmp'; // Temporary file path to write.
46
- fs.writeFileSync(tempPath, serialized, 'utf8');
47
- fs.renameSync(tempPath, dbPath);
48
- }
49
-
50
- // Retrieves all workspaces registered in the database.
51
- export function getWorkspaces(): Workspace[] {
52
- const db = readDatabase(); // The current session database instance loaded from disk.
53
- const list = db.workspaces; // The list of workspaces extracted from the loaded database.
54
- return list;
55
- }
56
-
57
- // Creates a new workspace and initializes it in the persistent database.
58
- export function createWorkspace(name: string, folderPath: string): Workspace {
59
- const db = readDatabase(); // The active database object loaded from persistent storage.
60
- const newWorkspace: Workspace = {
61
- id: 'ws_' + Math.random().toString(36).substring(2, 9), // Dynamically generated unique workspace ID string.
62
- name: name, // The workspace name passed as a parameter.
63
- path: path.resolve(folderPath), // Resolved absolute path string.
64
- tabs: [], // Initialized empty list of terminal tabs.
65
- }; // The new workspace structure to append.
66
- db.workspaces.push(newWorkspace);
67
- if (!db.activeWorkspaceId) {
68
- db.activeWorkspaceId = newWorkspace.id; // Sets the first workspace as the default active workspace.
69
- }
70
- writeDatabase(db);
71
- const result = newWorkspace; // The created workspace returned to caller.
72
- return result;
73
- }
74
-
75
- // Deletes a workspace by its ID from the persistent database.
76
- export function deleteWorkspace(id: string): void {
77
- const db = readDatabase(); // The current database structure loaded from disk.
78
- const filtered = db.workspaces.filter(ws => ws.id !== id); // Filtered array of workspaces excluding the target workspace ID.
79
- db.workspaces = filtered;
80
- if (db.activeWorkspaceId === id) {
81
- const fallbackId = db.workspaces.length > 0 ? db.workspaces[0].id : undefined; // Fallback workspace ID if the deleted one was active.
82
- db.activeWorkspaceId = fallbackId;
83
- }
84
- writeDatabase(db);
85
- }
86
-
87
- // Updates details of an existing workspace in the database.
88
- export function updateWorkspace(id: string, updates: Partial<Omit<Workspace, 'id' | 'tabs'>>): Workspace | null {
89
- const db = readDatabase(); // The loaded session database object.
90
- const wsIndex = db.workspaces.findIndex(ws => ws.id === id); // Index of the target workspace in the array.
91
- if (wsIndex === -1) {
92
- const nullResult = null; // Represents a missing workspace lookup outcome.
93
- return nullResult;
94
- }
95
- const updatedWs = { ...db.workspaces[wsIndex], ...updates }; // Blended workspace object with updated values.
96
- db.workspaces[wsIndex] = updatedWs;
97
- writeDatabase(db);
98
- const result = updatedWs; // Returns the updated workspace structure.
99
- return result;
100
- }
101
-
102
- // Creates a new terminal tab under a specific workspace in the database.
103
- export function createTerminalTab(workspaceId: string, name: string, cwd: string, shell?: string): TerminalTab | null {
104
- const db = readDatabase(); // The database session object retrieved from file system.
105
- const ws = db.workspaces.find(w => w.id === workspaceId); // Workspace object referenced by workspaceId.
106
- if (!ws) {
107
- const errorResult = null; // Represents a failure to find the workspace.
108
- return errorResult;
109
- }
110
- const newTab: TerminalTab = {
111
- id: 'tab_' + Math.random().toString(36).substring(2, 9), // Dynamically generated unique tab ID string.
112
- name: name, // User assigned name of the terminal tab.
113
- cwd: path.resolve(cwd), // Resolved absolute current working directory.
114
- shell: shell, // The optional custom shell path override.
115
- }; // The new terminal tab object to be created.
116
- ws.tabs.push(newTab);
117
- writeDatabase(db);
118
- const result = newTab; // Returns the newly created terminal tab.
119
- return result;
120
- }
121
-
122
- // Deletes a terminal tab from a workspace in the database.
123
- export function deleteTerminalTab(workspaceId: string, tabId: string): void {
124
- const db = readDatabase(); // The database state loaded from sessions.json.
125
- const ws = db.workspaces.find(w => w.id === workspaceId); // Target workspace object.
126
- if (ws) {
127
- const filteredTabs = ws.tabs.filter(t => t.id !== tabId); // Filtered terminal tabs excluding the target tab ID.
128
- ws.tabs = filteredTabs;
129
- writeDatabase(db);
130
- }
131
- }
132
-
133
- // Updates details of an existing terminal tab in a workspace.
134
- export function updateTerminalTab(workspaceId: string, tabId: string, updates: Partial<Omit<TerminalTab, 'id'>>): TerminalTab | null {
135
- const db = readDatabase(); // The loaded session database object.
136
- const ws = db.workspaces.find(w => w.id === workspaceId); // Target workspace instance.
137
- if (!ws) {
138
- const errorResult = null; // Target workspace not found.
139
- return errorResult;
140
- }
141
- const tabIndex = ws.tabs.findIndex(t => t.id === tabId); // Index of the target tab.
142
- if (tabIndex === -1) {
143
- const errorResult = null; // Target tab not found.
144
- return errorResult;
145
- }
146
- const updatedTab = { ...ws.tabs[tabIndex], ...updates }; // Blended terminal tab configuration.
147
- ws.tabs[tabIndex] = updatedTab;
148
- writeDatabase(db);
149
- const result = updatedTab; // Returns the updated tab structure.
150
- return result;
151
- }
1
+ import * as fs from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+
5
+ export interface TerminalTab {
6
+ id: string; // Unique identifier for the terminal tab.
7
+ name: string; // User-facing name of the terminal tab.
8
+ cwd: string; // Current working directory for this terminal session.
9
+ shell?: string; // Optional shell path override for the terminal.
10
+ branch?: string; // Target Git branch assigned to this individual terminal tab.
11
+ worktreePath?: string; // Path to the generated git worktree for this tab if isolated.
12
+ pid?: number; // The process ID of the active shell process.
13
+ }
14
+
15
+ export interface Workspace {
16
+ id: string; // Unique identifier for the workspace.
17
+ name: string; // User-facing name of the workspace.
18
+ path: string; // Core absolute path to the workspace code directory.
19
+ layout?: any; // The react-mosaic-component layout state for this workspace.
20
+ tabs: TerminalTab[]; // List of terminal tabs owned by this workspace.
21
+ }
22
+
23
+ export interface SessionDb {
24
+ workspaces: Workspace[]; // Array of workspaces managed in this system session.
25
+ activeWorkspaceId?: string; // ID of the currently selected active workspace.
26
+ }
27
+
28
+ const dbPath = path.join(os.homedir(), '.exeggutor-sessions.json'); // Absolute system path pointing to the sessions flat-file JSON database.
29
+
30
+ // Reads and parses the sessions database from the local file system.
31
+ export function readDatabase(): SessionDb {
32
+ if (!fs.existsSync(dbPath)) {
33
+ const initialDb: SessionDb = { workspaces: [] }; // The initial structural database template to write when the file does not exist.
34
+ fs.writeFileSync(dbPath, JSON.stringify(initialDb, null, 2), 'utf8');
35
+ return initialDb;
36
+ }
37
+ const rawData = fs.readFileSync(dbPath, 'utf8'); // Loaded string content from the session JSON file.
38
+ const parsed = JSON.parse(rawData) as SessionDb; // Parsed sessions database schema object.
39
+ return parsed;
40
+ }
41
+
42
+ // Serializes and saves the database state back to the local file system.
43
+ export function writeDatabase(db: SessionDb): void {
44
+ const serialized = JSON.stringify(db, null, 2); // Serialized JSON string of the session database structure.
45
+ const tempPath = dbPath + '.tmp'; // Temporary file path to write.
46
+ fs.writeFileSync(tempPath, serialized, 'utf8');
47
+ fs.renameSync(tempPath, dbPath);
48
+ }
49
+
50
+ // Retrieves all workspaces registered in the database.
51
+ export function getWorkspaces(): Workspace[] {
52
+ const db = readDatabase(); // The current session database instance loaded from disk.
53
+ const list = db.workspaces; // The list of workspaces extracted from the loaded database.
54
+ return list;
55
+ }
56
+
57
+ // Creates a new workspace and initializes it in the persistent database.
58
+ export function createWorkspace(name: string, folderPath: string): Workspace {
59
+ const db = readDatabase(); // The active database object loaded from persistent storage.
60
+ const newWorkspace: Workspace = {
61
+ id: 'ws_' + Math.random().toString(36).substring(2, 9), // Dynamically generated unique workspace ID string.
62
+ name: name, // The workspace name passed as a parameter.
63
+ path: path.resolve(folderPath), // Resolved absolute path string.
64
+ tabs: [], // Initialized empty list of terminal tabs.
65
+ }; // The new workspace structure to append.
66
+ db.workspaces.push(newWorkspace);
67
+ if (!db.activeWorkspaceId) {
68
+ db.activeWorkspaceId = newWorkspace.id; // Sets the first workspace as the default active workspace.
69
+ }
70
+ writeDatabase(db);
71
+ const result = newWorkspace; // The created workspace returned to caller.
72
+ return result;
73
+ }
74
+
75
+ // Deletes a workspace by its ID from the persistent database.
76
+ export function deleteWorkspace(id: string): void {
77
+ const db = readDatabase(); // The current database structure loaded from disk.
78
+ const filtered = db.workspaces.filter(ws => ws.id !== id); // Filtered array of workspaces excluding the target workspace ID.
79
+ db.workspaces = filtered;
80
+ if (db.activeWorkspaceId === id) {
81
+ const fallbackId = db.workspaces.length > 0 ? db.workspaces[0].id : undefined; // Fallback workspace ID if the deleted one was active.
82
+ db.activeWorkspaceId = fallbackId;
83
+ }
84
+ writeDatabase(db);
85
+ }
86
+
87
+ // Updates details of an existing workspace in the database.
88
+ export function updateWorkspace(id: string, updates: Partial<Omit<Workspace, 'id' | 'tabs'>>): Workspace | null {
89
+ const db = readDatabase(); // The loaded session database object.
90
+ const wsIndex = db.workspaces.findIndex(ws => ws.id === id); // Index of the target workspace in the array.
91
+ if (wsIndex === -1) {
92
+ const nullResult = null; // Represents a missing workspace lookup outcome.
93
+ return nullResult;
94
+ }
95
+ const updatedWs = { ...db.workspaces[wsIndex], ...updates }; // Blended workspace object with updated values.
96
+ db.workspaces[wsIndex] = updatedWs;
97
+ writeDatabase(db);
98
+ const result = updatedWs; // Returns the updated workspace structure.
99
+ return result;
100
+ }
101
+
102
+ // Creates a new terminal tab under a specific workspace in the database.
103
+ export function createTerminalTab(workspaceId: string, name: string, cwd: string, shell?: string): TerminalTab | null {
104
+ const db = readDatabase(); // The database session object retrieved from file system.
105
+ const ws = db.workspaces.find(w => w.id === workspaceId); // Workspace object referenced by workspaceId.
106
+ if (!ws) {
107
+ const errorResult = null; // Represents a failure to find the workspace.
108
+ return errorResult;
109
+ }
110
+ const newTab: TerminalTab = {
111
+ id: 'tab_' + Math.random().toString(36).substring(2, 9), // Dynamically generated unique tab ID string.
112
+ name: name, // User assigned name of the terminal tab.
113
+ cwd: path.resolve(cwd), // Resolved absolute current working directory.
114
+ shell: shell, // The optional custom shell path override.
115
+ }; // The new terminal tab object to be created.
116
+ ws.tabs.push(newTab);
117
+ writeDatabase(db);
118
+ const result = newTab; // Returns the newly created terminal tab.
119
+ return result;
120
+ }
121
+
122
+ // Deletes a terminal tab from a workspace in the database.
123
+ export function deleteTerminalTab(workspaceId: string, tabId: string): void {
124
+ const db = readDatabase(); // The database state loaded from sessions.json.
125
+ const ws = db.workspaces.find(w => w.id === workspaceId); // Target workspace object.
126
+ if (ws) {
127
+ const filteredTabs = ws.tabs.filter(t => t.id !== tabId); // Filtered terminal tabs excluding the target tab ID.
128
+ ws.tabs = filteredTabs;
129
+ writeDatabase(db);
130
+ }
131
+ }
132
+
133
+ // Updates details of an existing terminal tab in a workspace.
134
+ export function updateTerminalTab(workspaceId: string, tabId: string, updates: Partial<Omit<TerminalTab, 'id'>>): TerminalTab | null {
135
+ const db = readDatabase(); // The loaded session database object.
136
+ const ws = db.workspaces.find(w => w.id === workspaceId); // Target workspace instance.
137
+ if (!ws) {
138
+ const errorResult = null; // Target workspace not found.
139
+ return errorResult;
140
+ }
141
+ const tabIndex = ws.tabs.findIndex(t => t.id === tabId); // Index of the target tab.
142
+ if (tabIndex === -1) {
143
+ const errorResult = null; // Target tab not found.
144
+ return errorResult;
145
+ }
146
+ const updatedTab = { ...ws.tabs[tabIndex], ...updates }; // Blended terminal tab configuration.
147
+ ws.tabs[tabIndex] = updatedTab;
148
+ writeDatabase(db);
149
+ const result = updatedTab; // Returns the updated tab structure.
150
+ return result;
151
+ }
@@ -1,17 +1,17 @@
1
- <!doctype html>
2
- <html lang="en" class="h-full bg-dark-900 text-slate-100">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='20' fill='%23000000'/%3E%3Ctext x='20' y='70' fill='white' font-family='monospace' font-size='60' font-weight='bold'%3E%26gt;_%3C/text%3E%3C/svg%3E" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Exeggutor</title>
8
- <link rel="preconnect" href="https://fonts.googleapis.com">
9
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
1
+ <!doctype html>
2
+ <html lang="en" class="h-full bg-dark-900 text-slate-100">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='20' fill='%23000000'/%3E%3Ctext x='20' y='70' fill='white' font-family='monospace' font-size='60' font-weight='bold'%3E%26gt;_%3C/text%3E%3C/svg%3E" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Exeggutor</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
11
11
  <script type="module" crossorigin src="/assets/index-DfUyE-fY.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-B3TWNlss.css">
13
- </head>
14
- <body class="h-full antialiased selection:bg-white/30 selection:text-white overflow-hidden">
15
- <div id="root" class="h-full"></div>
16
- </body>
17
- </html>
13
+ </head>
14
+ <body class="h-full antialiased selection:bg-white/30 selection:text-white overflow-hidden">
15
+ <div id="root" class="h-full"></div>
16
+ </body>
17
+ </html>