workspacecord 1.0.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.
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SENSITIVE_KEYS,
4
+ VALID_KEYS,
5
+ deleteConfigValue,
6
+ getAllConfig,
7
+ getConfigPath,
8
+ getConfigValue,
9
+ maskSensitive,
10
+ setConfigValue,
11
+ validateConfigValue
12
+ } from "./chunk-OKI4UVGY.js";
13
+ import "./chunk-K3NQKI34.js";
14
+
15
+ // src/config-cli.ts
16
+ function printHelp() {
17
+ console.log(`
18
+ \x1B[1mworkspacecord config\x1B[0m \u2014 manage global configuration
19
+
20
+ \x1B[1mUsage:\x1B[0m
21
+ workspacecord config setup Interactive configuration wizard
22
+ workspacecord config get <key> Read a configuration value
23
+ workspacecord config set <key> <val> Write a configuration value
24
+ workspacecord config unset <key> Remove a configuration value
25
+ workspacecord config list List all configuration values
26
+ workspacecord config path Show the config file path
27
+
28
+ \x1B[1mValid keys:\x1B[0m
29
+ ${Array.from(VALID_KEYS).join(", ")}
30
+ `);
31
+ }
32
+ async function handleConfig(args) {
33
+ const [subcommand, ...rest] = args;
34
+ switch (subcommand) {
35
+ case "setup": {
36
+ const { runSetup } = await import("./setup-TKOVXSME.js");
37
+ await runSetup();
38
+ break;
39
+ }
40
+ case "get": {
41
+ const key = rest[0];
42
+ if (!key) {
43
+ console.error("Usage: workspacecord config get <key>");
44
+ process.exit(1);
45
+ }
46
+ if (!VALID_KEYS.has(key)) {
47
+ console.error(`Unknown config key: ${key}`);
48
+ console.error(`Valid keys: ${Array.from(VALID_KEYS).join(", ")}`);
49
+ process.exit(1);
50
+ }
51
+ const value = getConfigValue(key);
52
+ if (value === void 0) {
53
+ console.log(`(not set)`);
54
+ } else {
55
+ console.log(maskSensitive(key, value));
56
+ }
57
+ break;
58
+ }
59
+ case "set": {
60
+ const [key, value] = rest;
61
+ if (!key || value === void 0) {
62
+ console.error("Usage: workspacecord config set <key> <value>");
63
+ process.exit(1);
64
+ }
65
+ const err = validateConfigValue(key, value);
66
+ if (err) {
67
+ console.error(err);
68
+ process.exit(1);
69
+ }
70
+ setConfigValue(key, value);
71
+ const display = SENSITIVE_KEYS.has(key) ? maskSensitive(key, value) : value;
72
+ console.log(`\x1B[32m\u2713\x1B[0m ${key} = ${display}`);
73
+ break;
74
+ }
75
+ case "unset": {
76
+ const key = rest[0];
77
+ if (!key) {
78
+ console.error("Usage: workspacecord config unset <key>");
79
+ process.exit(1);
80
+ }
81
+ if (!VALID_KEYS.has(key)) {
82
+ console.error(`Unknown config key: ${key}`);
83
+ process.exit(1);
84
+ }
85
+ deleteConfigValue(key);
86
+ console.log(`\x1B[32m\u2713\x1B[0m ${key} removed`);
87
+ break;
88
+ }
89
+ case "list": {
90
+ const all = getAllConfig();
91
+ const keys = Object.keys(all);
92
+ if (keys.length === 0) {
93
+ console.log("(no configuration set \u2014 run \x1B[36mworkspacecord config setup\x1B[0m)");
94
+ } else {
95
+ for (const key of keys) {
96
+ const value = all[key];
97
+ console.log(`${key}=${maskSensitive(key, value)}`);
98
+ }
99
+ }
100
+ break;
101
+ }
102
+ case "path": {
103
+ console.log(getConfigPath());
104
+ break;
105
+ }
106
+ case void 0:
107
+ case "help":
108
+ case "--help":
109
+ case "-h":
110
+ printHelp();
111
+ break;
112
+ default:
113
+ console.error(`Unknown config subcommand: ${subcommand}`);
114
+ printHelp();
115
+ process.exit(1);
116
+ }
117
+ }
118
+ export {
119
+ handleConfig
120
+ };
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-K3NQKI34.js";
3
+
4
+ // src/daemon.ts
5
+ import * as p from "@clack/prompts";
6
+ import { existsSync, writeFileSync, unlinkSync, readFileSync } from "fs";
7
+ import { resolve, join } from "path";
8
+ import { execSync } from "child_process";
9
+ import { homedir, platform } from "os";
10
+ var LABEL = "com.workspacecord";
11
+ var SERVICE_NAME = "workspacecord";
12
+ function getMacPlistPath() {
13
+ return join(homedir(), "Library", "LaunchAgents", `${LABEL}.plist`);
14
+ }
15
+ function getLinuxServicePath() {
16
+ return join(homedir(), ".config", "systemd", "user", `${SERVICE_NAME}.service`);
17
+ }
18
+ function getNodePath() {
19
+ return process.execPath;
20
+ }
21
+ function getCliPath() {
22
+ try {
23
+ const result = execSync("which workspacecord", { encoding: "utf-8" }).trim();
24
+ if (result) {
25
+ try {
26
+ const wrapper = readFileSync(result, "utf-8");
27
+ const match = wrapper.match(
28
+ /exec\s+(?:node|"\$basedir\/node")\s+"?\$basedir\/([^"$\s]+cli\.js)"?/
29
+ );
30
+ if (match) {
31
+ const wrapperDir = execSync(`dirname "${result}"`, { encoding: "utf-8" }).trim();
32
+ const resolved = resolve(wrapperDir, match[1]);
33
+ if (existsSync(resolved)) return resolved;
34
+ }
35
+ } catch {
36
+ }
37
+ const realPath = execSync(
38
+ `readlink -f "${result}" 2>/dev/null || realpath "${result}" 2>/dev/null || echo "${result}"`,
39
+ { encoding: "utf-8" }
40
+ ).trim();
41
+ if (realPath.endsWith(".js") && existsSync(realPath)) return realPath;
42
+ }
43
+ } catch {
44
+ }
45
+ return resolve(import.meta.dirname, "..", "dist", "cli.js");
46
+ }
47
+ function generateMacPlist(workDir, logDir) {
48
+ const nodePath = getNodePath();
49
+ const cliPath = getCliPath();
50
+ return `<?xml version="1.0" encoding="UTF-8"?>
51
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
52
+ <plist version="1.0">
53
+ <dict>
54
+ <key>Label</key>
55
+ <string>${LABEL}</string>
56
+ <key>ProgramArguments</key>
57
+ <array>
58
+ <string>${nodePath}</string>
59
+ <string>--watch</string>
60
+ <string>${cliPath}</string>
61
+ </array>
62
+ <key>WorkingDirectory</key>
63
+ <string>${workDir}</string>
64
+ <key>RunAtLoad</key>
65
+ <true/>
66
+ <key>KeepAlive</key>
67
+ <dict>
68
+ <key>Crashed</key>
69
+ <true/>
70
+ </dict>
71
+ <key>ThrottleInterval</key>
72
+ <integer>10</integer>
73
+ <key>StandardOutPath</key>
74
+ <string>${join(logDir, "workspacecord.log")}</string>
75
+ <key>StandardErrorPath</key>
76
+ <string>${join(logDir, "workspacecord.error.log")}</string>
77
+ <key>EnvironmentVariables</key>
78
+ <dict>
79
+ <key>PATH</key>
80
+ <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
81
+ </dict>
82
+ </dict>
83
+ </plist>`;
84
+ }
85
+ function generateSystemdUnit(workDir) {
86
+ const nodePath = getNodePath();
87
+ const cliPath = getCliPath();
88
+ return `[Unit]
89
+ Description=workspacecord - Discord AI agent bot
90
+ After=network.target
91
+
92
+ [Service]
93
+ Type=simple
94
+ ExecStart=${nodePath} --watch ${cliPath}
95
+ WorkingDirectory=${workDir}
96
+ Restart=on-abnormal
97
+ RestartSec=10
98
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin
99
+
100
+ [Install]
101
+ WantedBy=default.target
102
+ `;
103
+ }
104
+ async function install() {
105
+ const workDir = join(homedir(), ".workspacecord");
106
+ const isMac = platform() === "darwin";
107
+ if (isMac) {
108
+ const plistPath = getMacPlistPath();
109
+ const logDir = workDir;
110
+ if (existsSync(plistPath)) {
111
+ try {
112
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null`);
113
+ } catch {
114
+ }
115
+ }
116
+ const plist = generateMacPlist(workDir, logDir);
117
+ writeFileSync(plistPath, plist);
118
+ execSync(`launchctl load "${plistPath}"`);
119
+ p.log.success("LaunchAgent installed and started.");
120
+ p.log.info(`Plist: ${plistPath}`);
121
+ p.log.info(`Logs: ${join(logDir, "workspacecord.log")}`);
122
+ p.log.info(`Errors: ${join(logDir, "workspacecord.error.log")}`);
123
+ } else {
124
+ const servicePath = getLinuxServicePath();
125
+ const serviceDir = resolve(servicePath, "..");
126
+ if (!existsSync(serviceDir)) {
127
+ execSync(`mkdir -p "${serviceDir}"`);
128
+ }
129
+ const unit = generateSystemdUnit(workDir);
130
+ writeFileSync(servicePath, unit);
131
+ execSync("systemctl --user daemon-reload");
132
+ execSync(`systemctl --user enable ${SERVICE_NAME}`);
133
+ execSync(`systemctl --user start ${SERVICE_NAME}`);
134
+ p.log.success("systemd user service installed and started.");
135
+ p.log.info(`Unit: ${servicePath}`);
136
+ p.log.info(`Logs: journalctl --user -u ${SERVICE_NAME} -f`);
137
+ }
138
+ }
139
+ async function uninstall() {
140
+ const isMac = platform() === "darwin";
141
+ if (isMac) {
142
+ const plistPath = getMacPlistPath();
143
+ if (!existsSync(plistPath)) {
144
+ p.log.warn("No LaunchAgent found. Nothing to uninstall.");
145
+ return;
146
+ }
147
+ try {
148
+ execSync(`launchctl unload "${plistPath}"`);
149
+ } catch {
150
+ }
151
+ unlinkSync(plistPath);
152
+ p.log.success("LaunchAgent uninstalled.");
153
+ } else {
154
+ const servicePath = getLinuxServicePath();
155
+ if (!existsSync(servicePath)) {
156
+ p.log.warn("No systemd service found. Nothing to uninstall.");
157
+ return;
158
+ }
159
+ try {
160
+ execSync(`systemctl --user stop ${SERVICE_NAME}`);
161
+ execSync(`systemctl --user disable ${SERVICE_NAME}`);
162
+ } catch {
163
+ }
164
+ unlinkSync(servicePath);
165
+ execSync("systemctl --user daemon-reload");
166
+ p.log.success("systemd user service uninstalled.");
167
+ }
168
+ }
169
+ async function status() {
170
+ const isMac = platform() === "darwin";
171
+ if (isMac) {
172
+ const plistPath = getMacPlistPath();
173
+ if (!existsSync(plistPath)) {
174
+ p.log.warn("No LaunchAgent installed.");
175
+ return;
176
+ }
177
+ try {
178
+ const output = execSync(`launchctl list | grep ${LABEL}`, { encoding: "utf-8" }).trim();
179
+ if (output) {
180
+ const parts = output.split(" ");
181
+ const pid = parts[0];
182
+ const exitCode = parts[1];
183
+ if (pid && pid !== "-") {
184
+ p.log.success(`Running (PID ${pid})`);
185
+ } else {
186
+ p.log.warn(`Not running (last exit code: ${exitCode})`);
187
+ }
188
+ } else {
189
+ p.log.warn("Installed but not loaded.");
190
+ }
191
+ } catch {
192
+ p.log.warn("Installed but not loaded.");
193
+ }
194
+ try {
195
+ const plist = readFileSync(plistPath, "utf-8");
196
+ const match = plist.match(/<key>WorkingDirectory<\/key>\s*<string>(.+?)<\/string>/);
197
+ if (match) p.log.info(`Directory: ${match[1]}`);
198
+ } catch {
199
+ }
200
+ } else {
201
+ try {
202
+ const output = execSync(`systemctl --user is-active ${SERVICE_NAME}`, {
203
+ encoding: "utf-8"
204
+ }).trim();
205
+ if (output === "active") {
206
+ p.log.success("Running");
207
+ } else {
208
+ p.log.warn(`Status: ${output}`);
209
+ }
210
+ const statusOutput = execSync(`systemctl --user status ${SERVICE_NAME} --no-pager -l`, {
211
+ encoding: "utf-8"
212
+ });
213
+ console.log(statusOutput);
214
+ } catch (err) {
215
+ const servicePath = getLinuxServicePath();
216
+ if (existsSync(servicePath)) {
217
+ p.log.warn("Installed but not running.");
218
+ } else {
219
+ p.log.warn("No systemd service installed.");
220
+ }
221
+ }
222
+ }
223
+ }
224
+ async function handleDaemon(subcommand) {
225
+ switch (subcommand) {
226
+ case "install":
227
+ await install();
228
+ break;
229
+ case "uninstall":
230
+ case "remove":
231
+ await uninstall();
232
+ break;
233
+ case "status":
234
+ await status();
235
+ break;
236
+ default:
237
+ console.log(`
238
+ \x1B[1mworkspacecord daemon\x1B[0m \u2014 manage background service
239
+
240
+ \x1B[1mUsage:\x1B[0m
241
+ workspacecord daemon install Install and start as background service
242
+ workspacecord daemon uninstall Stop and remove the background service
243
+ workspacecord daemon status Check if the service is running
244
+
245
+ The service auto-starts on boot and restarts on crash.
246
+ Run \x1B[36mworkspacecord config setup\x1B[0m first to configure the bot.
247
+ `);
248
+ }
249
+ }
250
+ export {
251
+ handleDaemon
252
+ };
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getAllRegisteredProjects,
4
+ getProjectByPath,
5
+ loadRegistry,
6
+ registerProject,
7
+ removeProject,
8
+ renameProject,
9
+ unbindProjectCategory
10
+ } from "./chunk-NIXZJTOZ.js";
11
+ import "./chunk-K3NQKI34.js";
12
+
13
+ // src/project-cli.ts
14
+ import { basename, resolve } from "path";
15
+ function parseNameArg(args) {
16
+ const index = args.indexOf("--name");
17
+ if (index >= 0 && args[index + 1]) return args[index + 1];
18
+ return void 0;
19
+ }
20
+ function printHelp() {
21
+ console.log(`
22
+ workspacecord project \u2014 manage mounted projects
23
+
24
+ Usage:
25
+ workspacecord project init [--name <name>]
26
+ workspacecord project list
27
+ workspacecord project info
28
+ workspacecord project rename <new-name>
29
+ workspacecord project remove
30
+ `);
31
+ }
32
+ async function handleProject(args) {
33
+ await loadRegistry();
34
+ const [subcommand, ...rest] = args;
35
+ switch (subcommand) {
36
+ case "init": {
37
+ const cwd = resolve(process.cwd());
38
+ const name = parseNameArg(rest) || basename(cwd);
39
+ const project = await registerProject(name, cwd);
40
+ console.log(`\u2713 Project mounted: ${project.name}`);
41
+ console.log(` Path: ${project.path}`);
42
+ if (project.discordCategoryId) {
43
+ console.log(
44
+ ` Bound Discord category: ${project.discordCategoryName ?? project.discordCategoryId}`
45
+ );
46
+ } else {
47
+ console.log(" Discord binding: pending (`/project setup` in Discord)");
48
+ }
49
+ return;
50
+ }
51
+ case "list": {
52
+ const projects = getAllRegisteredProjects();
53
+ if (projects.length === 0) {
54
+ console.log("No projects mounted. Run `workspacecord project init` in a repository.");
55
+ return;
56
+ }
57
+ for (const project of projects) {
58
+ const status = project.discordCategoryId ? `discord:${project.discordCategoryName ?? project.discordCategoryId}` : "discord:pending";
59
+ console.log(`${project.name} ${project.path} [${status}]`);
60
+ }
61
+ return;
62
+ }
63
+ case "info": {
64
+ const cwd = resolve(process.cwd());
65
+ const project = getProjectByPath(cwd);
66
+ if (!project) {
67
+ console.log("Current directory is not mounted as a workspacecord project.");
68
+ process.exit(1);
69
+ }
70
+ console.log(`name: ${project.name}`);
71
+ console.log(`path: ${project.path}`);
72
+ console.log(`discordCategoryId: ${project.discordCategoryId ?? "(pending)"}`);
73
+ console.log(`discordCategoryName: ${project.discordCategoryName ?? "(pending)"}`);
74
+ console.log(`historyChannelId: ${project.historyChannelId ?? "(pending)"}`);
75
+ return;
76
+ }
77
+ case "rename": {
78
+ const newName = rest[0];
79
+ if (!newName) {
80
+ console.error("Usage: workspacecord project rename <new-name>");
81
+ process.exit(1);
82
+ }
83
+ const cwd = resolve(process.cwd());
84
+ const project = getProjectByPath(cwd);
85
+ if (!project) {
86
+ console.error("Current directory is not mounted.");
87
+ process.exit(1);
88
+ }
89
+ await renameProject(project.name, newName);
90
+ console.log(`\u2713 Project renamed: ${project.name} -> ${newName}`);
91
+ return;
92
+ }
93
+ case "remove": {
94
+ const cwd = resolve(process.cwd());
95
+ const project = getProjectByPath(cwd);
96
+ if (!project) {
97
+ console.error("Current directory is not mounted.");
98
+ process.exit(1);
99
+ }
100
+ if (project.discordCategoryId) {
101
+ await unbindProjectCategory(project.name);
102
+ }
103
+ await removeProject(project.name);
104
+ console.log(`\u2713 Project removed: ${project.name}`);
105
+ return;
106
+ }
107
+ case void 0:
108
+ case "help":
109
+ case "--help":
110
+ case "-h":
111
+ printHelp();
112
+ return;
113
+ default:
114
+ console.error(`Unknown project subcommand: ${subcommand}`);
115
+ printHelp();
116
+ process.exit(1);
117
+ }
118
+ }
119
+ export {
120
+ handleProject
121
+ };
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ bindProjectCategory,
4
+ getAllRegisteredProjects,
5
+ getProjectByCategoryId,
6
+ getProjectByName,
7
+ getProjectByPath,
8
+ loadRegistry,
9
+ registerProject,
10
+ removeProject,
11
+ renameProject,
12
+ setProjectControlChannel,
13
+ setProjectHistoryChannel,
14
+ unbindProjectCategory,
15
+ updateProject
16
+ } from "./chunk-NIXZJTOZ.js";
17
+ import "./chunk-K3NQKI34.js";
18
+ export {
19
+ bindProjectCategory,
20
+ getAllRegisteredProjects,
21
+ getProjectByCategoryId,
22
+ getProjectByName,
23
+ getProjectByPath,
24
+ loadRegistry,
25
+ registerProject,
26
+ removeProject,
27
+ renameProject,
28
+ setProjectControlChannel,
29
+ setProjectHistoryChannel,
30
+ unbindProjectCategory,
31
+ updateProject
32
+ };