@jive-ai/cli 0.0.32 → 0.0.33

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 CHANGED
@@ -13,7 +13,7 @@ npm install -g @jive-ai/cli
13
13
  ### Docker
14
14
 
15
15
  ```bash
16
- docker pull jive-ai/task:latest
16
+ docker pull jiveai/task:latest
17
17
  ```
18
18
 
19
19
  ## Configuration
@@ -44,7 +44,7 @@ as long as the same entrypoint script is provided. The task runner relies on it.
44
44
  You can extend the Jive task runner image to add custom tools or dependencies:
45
45
 
46
46
  ```dockerfile
47
- FROM jive-ai/task:latest
47
+ FROM jiveai/task:latest
48
48
 
49
49
  # Add your custom dependencies
50
50
  RUN apk add --no-cache python3 py3-pip
@@ -0,0 +1,3 @@
1
+ import { A as getProjectConfig, D as clearCredentials, F as saveCredentials, I as saveProjectConfig, L as updateCredentials, M as isProjectInitialized, N as requireAuth, O as getActiveTeamId, P as requireProjectConfig, R as updateProjectConfig, j as getTasksConfigSync, k as getCredentials } from "./index.mjs";
2
+
3
+ export { getCredentials };
@@ -0,0 +1,3 @@
1
+ import { C as queries, S as mutations, b as cliMutations, x as cliQueries, y as getGraphQLClient } from "./index.mjs";
2
+
3
+ export { getGraphQLClient };
package/dist/index.mjs CHANGED
@@ -3407,7 +3407,7 @@ function getEffectiveResourceLimits(config$2, projectLimits) {
3407
3407
  docker: {
3408
3408
  cpus: projectLimits?.dockerCpus?.toString() || process.env.JIVE_DOCKER_CPUS || config$2.resourceLimits?.docker?.cpus || "2",
3409
3409
  memory: projectLimits?.dockerMemory || process.env.JIVE_DOCKER_MEMORY || config$2.resourceLimits?.docker?.memory || "2g",
3410
- image: projectLimits?.dockerImage || process.env.JIVE_DOCKER_IMAGE || config$2.resourceLimits?.docker?.image || "jive-ai/task:latest"
3410
+ image: projectLimits?.dockerImage || process.env.JIVE_DOCKER_IMAGE || config$2.resourceLimits?.docker?.image || "jiveai/task:latest"
3411
3411
  }
3412
3412
  };
3413
3413
  }
@@ -3484,7 +3484,7 @@ var TaskRunner = class {
3484
3484
  }
3485
3485
  async fetchTaskContext(taskId) {
3486
3486
  try {
3487
- const { getGraphQLClient: getGraphQLClient$1 } = await import("./graphql-client-DtLFKd4m.mjs");
3487
+ const { getGraphQLClient: getGraphQLClient$1 } = await import("./graphql-client-DtN1tbTC.mjs");
3488
3488
  const client = await getGraphQLClient$1();
3489
3489
  const query$1 = graphql(`
3490
3490
  query TaskContext($taskId: ID!) {
@@ -5219,6 +5219,207 @@ async function logsCommand() {
5219
5219
  async function startTaskCommand(taskCtx) {
5220
5220
  new Task(JSON.parse(decode(taskCtx))).start();
5221
5221
  }
5222
+ /**
5223
+ * Install the task runner as a system service
5224
+ */
5225
+ async function installServiceCommand() {
5226
+ console.log(chalk.bold("\nJive Task Runner Service Installation"));
5227
+ console.log(chalk.dim("=========================================\n"));
5228
+ try {
5229
+ const { getServiceManager, validateServiceInstallation, detectPlatform } = await import("./service-DTf-SkmQ.mjs");
5230
+ if (detectPlatform() === "unsupported") {
5231
+ console.error(chalk.red("Service installation is not supported on this platform."));
5232
+ console.log(chalk.dim("\nCurrently supported platforms:"));
5233
+ console.log(chalk.dim(" - Linux (systemd)"));
5234
+ console.log(chalk.dim("\nMacOS and Windows support coming soon!"));
5235
+ process.exit(1);
5236
+ }
5237
+ const spinner = ora("Validating service installation requirements...").start();
5238
+ const validation = await validateServiceInstallation();
5239
+ spinner.stop();
5240
+ console.log(chalk.bold("\nValidation Results:\n"));
5241
+ for (const check of validation.checks) {
5242
+ let icon = "✓";
5243
+ let color = chalk.green;
5244
+ if (check.status === "warning") {
5245
+ icon = "⚠";
5246
+ color = chalk.yellow;
5247
+ } else if (check.status === "error") {
5248
+ icon = "✗";
5249
+ color = chalk.red;
5250
+ }
5251
+ console.log(color(`${icon} ${check.name.padEnd(20)} ${check.message}`));
5252
+ if (check.detail) console.log(chalk.dim(` ${check.detail}`));
5253
+ }
5254
+ if (!validation.canInstall) {
5255
+ console.log(chalk.red("\n✗ Cannot install service due to validation errors."));
5256
+ console.log(chalk.dim("Please fix the errors above and try again."));
5257
+ process.exit(1);
5258
+ }
5259
+ if (validation.hasWarnings) console.log(chalk.yellow("\n⚠ Installation can proceed, but there are warnings."));
5260
+ console.log(chalk.dim(`\nPlatform: Linux (systemd)`));
5261
+ console.log(chalk.dim(`Service file: ~/.config/systemd/user/jive-task-runner.service\n`));
5262
+ const { confirm } = await prompts({
5263
+ type: "confirm",
5264
+ name: "confirm",
5265
+ message: "Continue with installation?",
5266
+ initial: true
5267
+ });
5268
+ if (!confirm) {
5269
+ console.log(chalk.yellow("Installation cancelled"));
5270
+ return;
5271
+ }
5272
+ spinner.start("Creating service file...");
5273
+ const manager = getServiceManager();
5274
+ await manager.install();
5275
+ spinner.succeed("Service installed successfully!");
5276
+ const { startNow } = await prompts({
5277
+ type: "confirm",
5278
+ name: "startNow",
5279
+ message: "Start service now?",
5280
+ initial: true
5281
+ });
5282
+ if (startNow) {
5283
+ spinner.start("Starting service...");
5284
+ await manager.start();
5285
+ spinner.succeed("Service started");
5286
+ const status = await manager.status();
5287
+ console.log(chalk.green(`\nService Status: ● Running`));
5288
+ if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
5289
+ }
5290
+ console.log(chalk.bold("\nNext steps:"));
5291
+ console.log(chalk.dim(" • View logs: ") + chalk.cyan("jive task-runner service-logs"));
5292
+ console.log(chalk.dim(" • Check status: ") + chalk.cyan("jive task-runner service-status"));
5293
+ console.log(chalk.dim(" • Restart: ") + chalk.cyan("jive task-runner service-restart"));
5294
+ } catch (error$1) {
5295
+ console.error(chalk.red(`\n✗ Installation failed: ${error$1.message}`));
5296
+ process.exit(1);
5297
+ }
5298
+ }
5299
+ /**
5300
+ * Uninstall the task runner service
5301
+ */
5302
+ async function uninstallServiceCommand() {
5303
+ try {
5304
+ const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
5305
+ const manager = getServiceManager();
5306
+ if (!await manager.isInstalled()) {
5307
+ console.log(chalk.yellow("Service is not installed."));
5308
+ return;
5309
+ }
5310
+ const { confirm } = await prompts({
5311
+ type: "confirm",
5312
+ name: "confirm",
5313
+ message: chalk.yellow("⚠ Remove the task runner service?"),
5314
+ initial: false
5315
+ });
5316
+ if (!confirm) {
5317
+ console.log(chalk.yellow("Cancelled"));
5318
+ return;
5319
+ }
5320
+ const spinner = ora("Uninstalling service...").start();
5321
+ await manager.uninstall();
5322
+ spinner.succeed("Service uninstalled successfully");
5323
+ console.log(chalk.dim("\nTo reinstall, run: ") + chalk.cyan("jive task-runner install-service"));
5324
+ } catch (error$1) {
5325
+ console.error(chalk.red(`\n✗ Uninstall failed: ${error$1.message}`));
5326
+ process.exit(1);
5327
+ }
5328
+ }
5329
+ /**
5330
+ * Show service status
5331
+ */
5332
+ async function serviceStatusCommand() {
5333
+ try {
5334
+ const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
5335
+ const manager = getServiceManager();
5336
+ if (!await manager.isInstalled()) {
5337
+ console.log(chalk.dim("Service is not installed."));
5338
+ console.log(chalk.dim("\nTo install, run: ") + chalk.cyan("jive task-runner install-service"));
5339
+ return;
5340
+ }
5341
+ const spinner = ora("Checking service status...").start();
5342
+ const status = await manager.status();
5343
+ spinner.stop();
5344
+ console.log(chalk.bold("\nJive Task Runner Service"));
5345
+ console.log(chalk.dim("═".repeat(60)));
5346
+ const statusIcon = status.running ? "●" : "○";
5347
+ const statusColor = status.running ? chalk.green : chalk.dim;
5348
+ const statusText = status.running ? "Running" : "Stopped";
5349
+ console.log(`\nStatus: ${statusColor(statusIcon)} ${statusColor(statusText)}`);
5350
+ if (status.pid) console.log(chalk.dim(`PID: ${status.pid}`));
5351
+ if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
5352
+ const autoStartIcon = status.enabled ? "✓" : "✗";
5353
+ const autoStartColor = status.enabled ? chalk.green : chalk.dim;
5354
+ console.log(`Auto-start: ${autoStartColor(autoStartIcon)} ${status.enabled ? "Enabled" : "Disabled"}`);
5355
+ if (status.running) {
5356
+ console.log(chalk.bold("\nRecent Logs:"));
5357
+ console.log(chalk.dim("─".repeat(60)));
5358
+ try {
5359
+ await manager.logs({ lines: 20 });
5360
+ } catch {
5361
+ console.log(chalk.dim("No logs available"));
5362
+ }
5363
+ }
5364
+ console.log(chalk.bold("\nCommands:"));
5365
+ console.log(chalk.dim("─".repeat(60)));
5366
+ console.log(chalk.dim(" Logs: ") + chalk.cyan("jive task-runner service-logs [-f]"));
5367
+ console.log(chalk.dim(" Restart: ") + chalk.cyan("jive task-runner service-restart"));
5368
+ if (status.running) console.log(chalk.dim(" Stop: ") + chalk.cyan("systemctl --user stop jive-task-runner"));
5369
+ else console.log(chalk.dim(" Start: ") + chalk.cyan("systemctl --user start jive-task-runner"));
5370
+ console.log(chalk.dim(" Uninstall: ") + chalk.cyan("jive task-runner uninstall-service"));
5371
+ console.log();
5372
+ } catch (error$1) {
5373
+ console.error(chalk.red(`\n✗ Failed to get status: ${error$1.message}`));
5374
+ process.exit(1);
5375
+ }
5376
+ }
5377
+ /**
5378
+ * View service logs
5379
+ */
5380
+ async function serviceLogsCommand(options) {
5381
+ try {
5382
+ const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
5383
+ const manager = getServiceManager();
5384
+ if (!await manager.isInstalled()) {
5385
+ console.log(chalk.dim("Service is not installed."));
5386
+ return;
5387
+ }
5388
+ const lines = options.lines ? parseInt(options.lines, 10) : 50;
5389
+ await manager.logs({
5390
+ follow: options.follow,
5391
+ lines
5392
+ });
5393
+ } catch (error$1) {
5394
+ if (error$1.message.includes("SIGTERM")) return;
5395
+ console.error(chalk.red(`\n✗ Failed to view logs: ${error$1.message}`));
5396
+ process.exit(1);
5397
+ }
5398
+ }
5399
+ /**
5400
+ * Restart the service
5401
+ */
5402
+ async function serviceRestartCommand() {
5403
+ try {
5404
+ const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
5405
+ const manager = getServiceManager();
5406
+ if (!await manager.isInstalled()) {
5407
+ console.log(chalk.dim("Service is not installed."));
5408
+ return;
5409
+ }
5410
+ const spinner = ora("Restarting service...").start();
5411
+ await manager.restart();
5412
+ spinner.succeed("Service restarted");
5413
+ const status = await manager.status();
5414
+ if (status.running) {
5415
+ console.log(chalk.green(`\n● Service is running`));
5416
+ if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
5417
+ }
5418
+ } catch (error$1) {
5419
+ console.error(chalk.red(`\n✗ Failed to restart: ${error$1.message}`));
5420
+ process.exit(1);
5421
+ }
5422
+ }
5222
5423
  const taskRunnerCommands = {
5223
5424
  init: initCommand$1,
5224
5425
  setup: setupRunnerCommand,
@@ -5227,7 +5428,12 @@ const taskRunnerCommands = {
5227
5428
  stop: stopRunnerCommand,
5228
5429
  remove: removeRunnerCommand,
5229
5430
  logs: logsCommand,
5230
- startTask: startTaskCommand
5431
+ startTask: startTaskCommand,
5432
+ installService: installServiceCommand,
5433
+ uninstallService: uninstallServiceCommand,
5434
+ serviceStatus: serviceStatusCommand,
5435
+ serviceLogs: serviceLogsCommand,
5436
+ serviceRestart: serviceRestartCommand
5231
5437
  };
5232
5438
 
5233
5439
  //#endregion
@@ -5447,7 +5653,7 @@ const taskCommands = { resume: resumeCommand };
5447
5653
 
5448
5654
  //#endregion
5449
5655
  //#region package.json
5450
- var version = "0.0.32";
5656
+ var version = "0.0.33";
5451
5657
 
5452
5658
  //#endregion
5453
5659
  //#region src/index.ts
@@ -5468,8 +5674,13 @@ taskRunner.command("stop").description("Stop the task runner").action(taskRunner
5468
5674
  taskRunner.command("remove").description("Remove the task runner configuration").action(taskRunnerCommands.remove);
5469
5675
  taskRunner.command("logs").description("View logs from the task runner").action(taskRunnerCommands.logs);
5470
5676
  taskRunner.command("start-task").description("Start a task").argument("<task-ctx>", "JSON Context of the task to start").action(taskRunnerCommands.startTask);
5677
+ taskRunner.command("install-service").description("Install task runner as a system service").action(taskRunnerCommands.installService);
5678
+ taskRunner.command("uninstall-service").description("Uninstall task runner service").action(taskRunnerCommands.uninstallService);
5679
+ taskRunner.command("service-status").description("Show service status").action(taskRunnerCommands.serviceStatus);
5680
+ taskRunner.command("service-logs").description("View service logs").option("-f, --follow", "Follow log output").option("-n, --lines <number>", "Number of lines to show", "50").action(taskRunnerCommands.serviceLogs);
5681
+ taskRunner.command("service-restart").description("Restart the service").action(taskRunnerCommands.serviceRestart);
5471
5682
  program.command("task").description("Manage tasks").command("resume").description("Resume a remote task session locally using Claude Code").argument("<taskId>", "ID of the task to resume").action(taskCommands.resume);
5472
5683
  program.parse();
5473
5684
 
5474
5685
  //#endregion
5475
- export { queries as a, mutations as i, cliMutations as n, cliQueries as r, getGraphQLClient as t };
5686
+ export { getProjectConfig as A, queries as C, clearCredentials as D, WS_URL as E, saveCredentials as F, saveProjectConfig as I, updateCredentials as L, isProjectInitialized as M, requireAuth as N, getActiveTeamId as O, requireProjectConfig as P, updateProjectConfig as R, mutations as S, GRAPHQL_API_URL as T, taskRunnerCommands as _, installServiceCommand as a, cliMutations as b, runnerInfoCommand as c, serviceRestartCommand as d, serviceStatusCommand as f, stopRunnerCommand as g, startTaskCommand as h, initCommand$1 as i, getTasksConfigSync as j, getCredentials as k, saveRunnerConfig as l, startRunnerCommand as m, ensureTasksConfig as n, logsCommand as o, setupRunnerCommand as p, getRunnerConfig as r, removeRunnerCommand as s, createTasksConfig as t, serviceLogsCommand as u, uninstallServiceCommand as v, API_URL as w, cliQueries as x, getGraphQLClient as y };
@@ -0,0 +1,347 @@
1
+ import { E as WS_URL, T as GRAPHQL_API_URL, w as API_URL } from "./index.mjs";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import os from "os";
5
+ import { exec, spawn } from "child_process";
6
+ import { promisify } from "util";
7
+
8
+ //#region src/lib/service/systemd.ts
9
+ const execAsync$1 = promisify(exec);
10
+ const SERVICE_TEMPLATE = `[Unit]
11
+ Description=Jive Task Runner
12
+ Documentation=https://getjive.app/docs
13
+ After=network-online.target
14
+ Wants=network-online.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ ExecStart={{JIVE_BINARY_PATH}} task-runner start
19
+ Restart=on-failure
20
+ RestartSec=30
21
+ TimeoutStartSec=90
22
+ TimeoutStopSec=30
23
+ StartLimitBurst=5
24
+ StartLimitIntervalSec=10m
25
+ KillMode=mixed
26
+ Environment="PATH={{NODE_BIN_PATH}}:/usr/local/bin:/usr/bin:/bin"
27
+ Environment="JIVE_API_KEY={{JIVE_API_KEY}}"
28
+ Environment="ANTHROPIC_API_KEY={{ANTHROPIC_API_KEY}}"
29
+ Environment="JIVE_TEAM_ID={{JIVE_TEAM_ID}}"
30
+ Environment="JIVE_RUNNER_ID={{JIVE_RUNNER_ID}}"
31
+ Environment="JIVE_API_URL={{JIVE_API_URL}}"
32
+ Environment="JIVE_WS_URL={{JIVE_WS_URL}}"
33
+ Environment="JIVE_GRAPHQL_API_URL={{JIVE_GRAPHQL_API_URL}}"
34
+
35
+ StandardOutput=journal
36
+ StandardError=journal
37
+ SyslogIdentifier=jive-task-runner
38
+
39
+ # Security hardening (safe for Docker runners)
40
+ NoNewPrivileges=true
41
+ PrivateTmp=true
42
+ ProtectKernelTunables=true
43
+ RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
44
+
45
+ [Install]
46
+ WantedBy=default.target
47
+ `;
48
+ var SystemdServiceManager = class {
49
+ servicePath;
50
+ serviceName = "jive-task-runner";
51
+ constructor() {
52
+ const homeDir = os.homedir();
53
+ this.servicePath = path.join(homeDir, ".config", "systemd", "user", `${this.serviceName}.service`);
54
+ }
55
+ async install() {
56
+ const { getRunnerConfig } = await import("./tasks-Py86q1u7.mjs");
57
+ const { getCredentials } = await import("./config-7rVDmj2u.mjs");
58
+ const runnerConfig = await getRunnerConfig();
59
+ const credentials = await getCredentials();
60
+ if (!credentials?.token) throw new Error("No API key found. Run `jive login` first.");
61
+ const { stdout: binaryPath } = await execAsync$1("which jive", { timeout: 3e4 });
62
+ const resolvedPath = binaryPath.trim();
63
+ if (!resolvedPath) throw new Error("Cannot locate jive binary in PATH");
64
+ const { stdout: nodePath } = await execAsync$1("readlink -f $(which node)", { timeout: 3e4 });
65
+ const resolvedNodePath = nodePath.trim();
66
+ const variables = {
67
+ JIVE_BINARY_PATH: resolvedPath,
68
+ NODE_BIN_PATH: path.dirname(resolvedNodePath),
69
+ JIVE_API_KEY: credentials.token,
70
+ ANTHROPIC_API_KEY: credentials.anthropicApiKey || "",
71
+ JIVE_TEAM_ID: runnerConfig.teamId,
72
+ JIVE_RUNNER_ID: runnerConfig.id.toString(),
73
+ JIVE_API_URL: process.env.JIVE_API_URL || API_URL,
74
+ JIVE_WS_URL: process.env.JIVE_WS_URL || WS_URL,
75
+ JIVE_GRAPHQL_API_URL: process.env.JIVE_GRAPHQL_API_URL || GRAPHQL_API_URL
76
+ };
77
+ let serviceContent = SERVICE_TEMPLATE;
78
+ for (const [key, value] of Object.entries(variables)) serviceContent = serviceContent.replace(new RegExp(`{{${key}}}`, "g"), value);
79
+ const serviceDir = path.dirname(this.servicePath);
80
+ await fs.mkdir(serviceDir, {
81
+ recursive: true,
82
+ mode: 493
83
+ });
84
+ const dirMode = (await fs.stat(serviceDir)).mode & 511;
85
+ if (dirMode > 493) throw new Error(`Systemd user directory has overly permissive permissions: ${dirMode.toString(8)}\nExpected 0755 or stricter. Fix with: chmod 755 ${serviceDir}`);
86
+ await fs.writeFile(this.servicePath, serviceContent, { mode: 384 });
87
+ try {
88
+ await execAsync$1("systemctl --user daemon-reload", { timeout: 3e4 });
89
+ await execAsync$1(`systemctl --user enable ${this.serviceName}`, { timeout: 3e4 });
90
+ } catch (error) {
91
+ try {
92
+ await fs.unlink(this.servicePath);
93
+ await execAsync$1("systemctl --user daemon-reload", { timeout: 3e4 });
94
+ } catch (cleanupError) {
95
+ console.error("Failed to clean up after installation failure:", cleanupError);
96
+ }
97
+ throw new Error(`Service installation failed: ${error.message}\nPartial installation has been rolled back.`);
98
+ }
99
+ }
100
+ async uninstall() {
101
+ try {
102
+ await this.stop();
103
+ } catch (error) {
104
+ if (error.message?.includes("inactive") || error.message?.includes("not be found")) {} else if (error.code === "EACCES") console.warn("Warning: Permission denied when stopping service");
105
+ else console.warn(`Warning: Failed to stop service: ${error.message}`);
106
+ }
107
+ try {
108
+ await execAsync$1(`systemctl --user disable ${this.serviceName}`, { timeout: 3e4 });
109
+ } catch (error) {
110
+ if (error.stderr?.includes("No such file") || error.message?.includes("not be found")) {} else if (error.code === "EACCES") console.warn("Warning: Permission denied when disabling service");
111
+ else console.warn(`Warning: Failed to disable service: ${error.message}`);
112
+ }
113
+ try {
114
+ await fs.unlink(this.servicePath);
115
+ } catch (error) {
116
+ if (error.code !== "ENOENT") throw new Error(`Failed to remove service file: ${error.message}`);
117
+ }
118
+ await execAsync$1("systemctl --user daemon-reload", { timeout: 3e4 });
119
+ }
120
+ async start() {
121
+ await execAsync$1(`systemctl --user start ${this.serviceName}`, { timeout: 3e4 });
122
+ }
123
+ async stop() {
124
+ await execAsync$1(`systemctl --user stop ${this.serviceName}`, { timeout: 3e4 });
125
+ }
126
+ async restart() {
127
+ await execAsync$1(`systemctl --user restart ${this.serviceName}`, { timeout: 3e4 });
128
+ }
129
+ async status() {
130
+ if (!await this.isInstalled()) return {
131
+ installed: false,
132
+ running: false,
133
+ enabled: false
134
+ };
135
+ try {
136
+ const { stdout } = await execAsync$1(`systemctl --user status ${this.serviceName} --no-pager`, { timeout: 3e4 });
137
+ const running = stdout.includes("Active: active (running)");
138
+ const pid = this.extractPid(stdout);
139
+ const uptime = this.extractUptime(stdout);
140
+ const { stdout: isEnabledOutput } = await execAsync$1(`systemctl --user is-enabled ${this.serviceName}`, { timeout: 3e4 });
141
+ return {
142
+ installed: true,
143
+ running,
144
+ enabled: isEnabledOutput.trim() === "enabled",
145
+ uptime,
146
+ pid
147
+ };
148
+ } catch (error) {
149
+ const { stdout: isEnabledOutput } = await execAsync$1(`systemctl --user is-enabled ${this.serviceName}`, { timeout: 3e4 }).catch(() => ({ stdout: "disabled" }));
150
+ return {
151
+ installed: true,
152
+ running: false,
153
+ enabled: isEnabledOutput.trim() === "enabled"
154
+ };
155
+ }
156
+ }
157
+ async logs(options) {
158
+ const args = [
159
+ "--user",
160
+ "-u",
161
+ this.serviceName
162
+ ];
163
+ if (options?.follow) args.push("-f");
164
+ if (options?.lines) args.push("-n", options.lines.toString());
165
+ const logsProcess = spawn("journalctl", args, { stdio: "inherit" });
166
+ return new Promise((resolve, reject) => {
167
+ logsProcess.on("exit", (code) => {
168
+ if (code === 0) resolve();
169
+ else reject(/* @__PURE__ */ new Error(`journalctl exited with code ${code}`));
170
+ });
171
+ logsProcess.on("error", (error) => {
172
+ reject(error);
173
+ });
174
+ if (options?.follow) process.on("SIGINT", () => {
175
+ logsProcess.kill("SIGTERM");
176
+ resolve();
177
+ });
178
+ });
179
+ }
180
+ async isInstalled() {
181
+ try {
182
+ await fs.access(this.servicePath);
183
+ return true;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+ extractPid(statusOutput) {
189
+ const pidMatch = statusOutput.match(/Main PID: (\d+)/);
190
+ return pidMatch ? parseInt(pidMatch[1], 10) : void 0;
191
+ }
192
+ extractUptime(statusOutput) {
193
+ const uptimeMatch = statusOutput.match(/Active: active \(running\) since (.+?);/);
194
+ if (!uptimeMatch) return void 0;
195
+ const sinceStr = uptimeMatch[1];
196
+ const sinceDate = new Date(sinceStr);
197
+ const diffMs = (/* @__PURE__ */ new Date()).getTime() - sinceDate.getTime();
198
+ const seconds = Math.floor(diffMs / 1e3);
199
+ const minutes = Math.floor(seconds / 60);
200
+ const hours = Math.floor(minutes / 60);
201
+ const days = Math.floor(hours / 24);
202
+ if (days > 0) return `${days}d ${hours % 24}h`;
203
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
204
+ if (minutes > 0) return `${minutes}m`;
205
+ return "Just now";
206
+ }
207
+ };
208
+
209
+ //#endregion
210
+ //#region src/lib/service/index.ts
211
+ const execAsync = promisify(exec);
212
+ /**
213
+ * Detect the current platform and service management system
214
+ */
215
+ function detectPlatform() {
216
+ if (os.platform() === "linux") return "linux-systemd";
217
+ return "unsupported";
218
+ }
219
+ /**
220
+ * Get a service manager instance for the current platform
221
+ */
222
+ function getServiceManager() {
223
+ if (detectPlatform() === "unsupported") throw new Error(`Service installation is not supported on ${os.platform()}.\nCurrently supported platforms:
224
+ - Linux (systemd)`);
225
+ return new SystemdServiceManager();
226
+ }
227
+ /**
228
+ * Validate that the system is ready for service installation
229
+ */
230
+ async function validateServiceInstallation() {
231
+ const checks = [];
232
+ let canInstall = true;
233
+ let hasWarnings = false;
234
+ try {
235
+ const { getRunnerConfig } = await import("./tasks-Py86q1u7.mjs");
236
+ const runnerConfig = await getRunnerConfig();
237
+ checks.push({
238
+ name: "Runner config",
239
+ status: "success",
240
+ message: `${runnerConfig.name} (${runnerConfig.type})`
241
+ });
242
+ } catch (error) {
243
+ checks.push({
244
+ name: "Runner config",
245
+ status: "error",
246
+ message: "Not configured",
247
+ detail: "Run `jive task-runner setup` first"
248
+ });
249
+ canInstall = false;
250
+ }
251
+ try {
252
+ const { getCredentials } = await import("./config-7rVDmj2u.mjs");
253
+ const credentials = await getCredentials();
254
+ if (!credentials?.token) throw new Error("No API key found");
255
+ if (!credentials?.anthropicApiKey) {
256
+ checks.push({
257
+ name: "Credentials",
258
+ status: "warning",
259
+ message: "Anthropic API key not found",
260
+ detail: "Runner may fail to start without it"
261
+ });
262
+ hasWarnings = true;
263
+ } else checks.push({
264
+ name: "Credentials",
265
+ status: "success",
266
+ message: "API keys configured"
267
+ });
268
+ } catch (error) {
269
+ checks.push({
270
+ name: "Credentials",
271
+ status: "error",
272
+ message: "Not configured",
273
+ detail: "Run `jive login` first"
274
+ });
275
+ canInstall = false;
276
+ }
277
+ try {
278
+ const { getRunnerConfig } = await import("./tasks-Py86q1u7.mjs");
279
+ if ((await getRunnerConfig()).type === "docker") try {
280
+ await execAsync("docker ps", { timeout: 1e4 });
281
+ checks.push({
282
+ name: "Docker",
283
+ status: "success",
284
+ message: "Docker daemon running"
285
+ });
286
+ } catch {
287
+ checks.push({
288
+ name: "Docker",
289
+ status: "error",
290
+ message: "Docker daemon not running",
291
+ detail: "Start Docker daemon or install from https://docs.docker.com/get-docker/"
292
+ });
293
+ canInstall = false;
294
+ }
295
+ } catch {}
296
+ try {
297
+ if (await getServiceManager().isInstalled()) {
298
+ checks.push({
299
+ name: "Service status",
300
+ status: "error",
301
+ message: "Service already installed",
302
+ detail: "Run `jive task-runner uninstall-service` first"
303
+ });
304
+ canInstall = false;
305
+ } else checks.push({
306
+ name: "Service status",
307
+ status: "success",
308
+ message: "Not installed"
309
+ });
310
+ } catch (error) {
311
+ if (error.message.includes("not supported")) {
312
+ checks.push({
313
+ name: "Service status",
314
+ status: "error",
315
+ message: "Platform not supported",
316
+ detail: error.message
317
+ });
318
+ canInstall = false;
319
+ }
320
+ }
321
+ try {
322
+ const { stdout } = await execAsync("which jive", { timeout: 3e4 });
323
+ const binaryPath = stdout.trim();
324
+ if (!binaryPath) throw new Error("Binary not in PATH");
325
+ checks.push({
326
+ name: "Binary path",
327
+ status: "success",
328
+ message: binaryPath
329
+ });
330
+ } catch (error) {
331
+ checks.push({
332
+ name: "Binary path",
333
+ status: "error",
334
+ message: "Cannot locate jive binary",
335
+ detail: "Ensure jive is installed and in PATH"
336
+ });
337
+ canInstall = false;
338
+ }
339
+ return {
340
+ checks,
341
+ canInstall,
342
+ hasWarnings
343
+ };
344
+ }
345
+
346
+ //#endregion
347
+ export { detectPlatform, getServiceManager, validateServiceInstallation };
@@ -0,0 +1,3 @@
1
+ import { _ as taskRunnerCommands, a as installServiceCommand, c as runnerInfoCommand, d as serviceRestartCommand, f as serviceStatusCommand, g as stopRunnerCommand, h as startTaskCommand, i as initCommand, l as saveRunnerConfig, m as startRunnerCommand, n as ensureTasksConfig, o as logsCommand, p as setupRunnerCommand, r as getRunnerConfig, s as removeRunnerCommand, t as createTasksConfig, u as serviceLogsCommand, v as uninstallServiceCommand } from "./index.mjs";
2
+
3
+ export { getRunnerConfig };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@jive-ai/cli",
4
- "version": "0.0.32",
4
+ "version": "0.0.33",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "dist",
@@ -15,11 +15,11 @@
15
15
  "test": "echo \"Error: no test specified\" && exit 1",
16
16
  "typecheck": "tsc --noEmit",
17
17
  "build": "tsdown && npm pack && npm install -g jive-ai-cli-*.tgz",
18
- "docker:build": "touch jive-ai-cli-0.0.0.tgz && docker build -t jive-ai/task:latest . && rm -f jive-ai-cli-*.tgz",
19
- "docker:build:local": "bun run build && docker build --build-arg USE_LOCAL=true -t jive-ai/task:latest .",
20
- "docker:build:public": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64,linux/arm64 -t jive-ai/task:latest -t jive-ai/task:$npm_package_version . && rm -f jive-ai-cli-*.tgz",
21
- "docker:push": "docker buildx build --platform linux/amd64,linux/arm64 --push -t jive-ai/task:latest -t jive-ai/task:$npm_package_version .",
22
- "docker:publish": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64,linux/arm64 --push -t jive-ai/task:latest -t jive-ai/task:$npm_package_version . && rm -f jive-ai-cli-*.tgz",
18
+ "docker:build": "touch jive-ai-cli-0.0.0.tgz && docker build -t jiveai/task:latest . && rm -f jive-ai-cli-*.tgz",
19
+ "docker:build:local": "bun run build && docker build --build-arg USE_LOCAL=true -t jiveai/task:latest .",
20
+ "docker:build:public": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64 -t jiveai/task:latest -t jiveai/task:$npm_package_version . && rm -f jive-ai-cli-*.tgz",
21
+ "docker:push": "docker buildx build --platform linux/amd64 --push -t jiveai/task:latest -t jiveai/task:$npm_package_version .",
22
+ "docker:publish": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64 --push -t jiveai/task:latest -t jiveai/task:$npm_package_version . && rm -f jive-ai-cli-*.tgz",
23
23
  "prepublishOnly": "npm run typecheck && npm run build"
24
24
  },
25
25
  "author": "",
@@ -1,3 +0,0 @@
1
- import { a as queries, i as mutations, n as cliMutations, r as cliQueries, t as getGraphQLClient } from "./index.mjs";
2
-
3
- export { getGraphQLClient };