@jive-ai/cli 0.0.32 ā 0.0.35
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 +2 -2
- package/dist/config-7rVDmj2u.mjs +3 -0
- package/dist/graphql-client-DtN1tbTC.mjs +3 -0
- package/dist/index.mjs +306 -43
- package/dist/service-DTf-SkmQ.mjs +347 -0
- package/dist/tasks-Py86q1u7.mjs +3 -0
- package/package.json +6 -6
- package/dist/graphql-client-DtLFKd4m.mjs +0 -3
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
|
|
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
|
|
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -185,9 +185,9 @@ async function openBrowser(url) {
|
|
|
185
185
|
async function loginCommand() {
|
|
186
186
|
console.log(chalk.bold("\nš Login to Jive\n"));
|
|
187
187
|
console.log(chalk.white("Opening browser for authentication...\n"));
|
|
188
|
-
await openBrowser(`${API_URL}/cli-auth`);
|
|
188
|
+
await openBrowser(`${API_URL}/app/cli-auth`);
|
|
189
189
|
console.log(chalk.gray("If the browser did not open, visit:"));
|
|
190
|
-
console.log(chalk.cyan(`${API_URL}/cli-auth\n`));
|
|
190
|
+
console.log(chalk.cyan(`${API_URL}/app/cli-auth\n`));
|
|
191
191
|
const response = await prompts({
|
|
192
192
|
type: "password",
|
|
193
193
|
name: "apiKey",
|
|
@@ -2613,6 +2613,18 @@ const cliQueries = {
|
|
|
2613
2613
|
gitDefaultBranch
|
|
2614
2614
|
team {
|
|
2615
2615
|
id
|
|
2616
|
+
settings {
|
|
2617
|
+
claude {
|
|
2618
|
+
defaultModel
|
|
2619
|
+
environmentVariables
|
|
2620
|
+
betaFlags
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
settings {
|
|
2625
|
+
defaultModel
|
|
2626
|
+
environmentVariables
|
|
2627
|
+
betaFlags
|
|
2616
2628
|
}
|
|
2617
2629
|
createdAt
|
|
2618
2630
|
updatedAt
|
|
@@ -2951,16 +2963,24 @@ var ApiClient = class {
|
|
|
2951
2963
|
};
|
|
2952
2964
|
}
|
|
2953
2965
|
async getSessionLines(taskId, sessionId, excludePending = false) {
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2966
|
+
const client = await getGraphQLClient();
|
|
2967
|
+
const allLines = [];
|
|
2968
|
+
let hasNextPage = true;
|
|
2969
|
+
let after = void 0;
|
|
2970
|
+
while (hasNextPage) {
|
|
2971
|
+
const response = await client.request(queries.GetSessionLines, {
|
|
2972
|
+
taskId: String(taskId),
|
|
2973
|
+
sessionId: String(sessionId),
|
|
2974
|
+
excludePending,
|
|
2975
|
+
first: 100,
|
|
2976
|
+
after
|
|
2977
|
+
});
|
|
2978
|
+
const pageLines = response.sessionLines.edges.map((edge) => edge.node);
|
|
2979
|
+
allLines.push(...pageLines);
|
|
2980
|
+
hasNextPage = response.sessionLines.pageInfo.hasNextPage;
|
|
2981
|
+
after = response.sessionLines.pageInfo.endCursor ?? void 0;
|
|
2982
|
+
}
|
|
2983
|
+
return allLines;
|
|
2964
2984
|
}
|
|
2965
2985
|
async getTaskSessions(taskId) {
|
|
2966
2986
|
return (await (await getGraphQLClient()).request(queries.GetSessions, { taskId: String(taskId) })).sessions.map((s) => ({
|
|
@@ -3337,7 +3357,10 @@ async function spawnTaskDocker(ctx, config$2, opts) {
|
|
|
3337
3357
|
console.log(chalk.yellow("Warning: Using Docker socket mount (no isolation). Set JIVE_USE_SYSBOX=true on Linux for secure mode."));
|
|
3338
3358
|
}
|
|
3339
3359
|
dockerArgs.push("--network", "bridge");
|
|
3340
|
-
if (isDevMode)
|
|
3360
|
+
if (isDevMode) {
|
|
3361
|
+
dockerArgs.push("--add-host", "host.docker.internal:host-gateway");
|
|
3362
|
+
dockerArgs.push("--pull", "never");
|
|
3363
|
+
}
|
|
3341
3364
|
dockerArgs.push("--cpus", limits.docker.cpus, "--memory", limits.docker.memory, "-v", `${ctx.directory}:/workspace`, "-w", "/workspace", ...envVars, limits.docker.image, encode(JSON.stringify(ctx)));
|
|
3342
3365
|
console.log(chalk.dim(`[Task ${ctx.taskId}] Spawning Docker container with args:`, JSON.stringify(dockerArgs, null, 2)));
|
|
3343
3366
|
const taskProcess = spawn("docker", dockerArgs, { cwd: ctx.directory });
|
|
@@ -3396,6 +3419,10 @@ async function createGraphQLClient() {
|
|
|
3396
3419
|
};
|
|
3397
3420
|
}
|
|
3398
3421
|
|
|
3422
|
+
//#endregion
|
|
3423
|
+
//#region package.json
|
|
3424
|
+
var version = "0.0.35";
|
|
3425
|
+
|
|
3399
3426
|
//#endregion
|
|
3400
3427
|
//#region src/runner/index.ts
|
|
3401
3428
|
const execAsync$3 = promisify(exec);
|
|
@@ -3407,7 +3434,7 @@ function getEffectiveResourceLimits(config$2, projectLimits) {
|
|
|
3407
3434
|
docker: {
|
|
3408
3435
|
cpus: projectLimits?.dockerCpus?.toString() || process.env.JIVE_DOCKER_CPUS || config$2.resourceLimits?.docker?.cpus || "2",
|
|
3409
3436
|
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 ||
|
|
3437
|
+
image: projectLimits?.dockerImage || process.env.JIVE_DOCKER_IMAGE || config$2.resourceLimits?.docker?.image || `jiveai/task:${version}`
|
|
3411
3438
|
}
|
|
3412
3439
|
};
|
|
3413
3440
|
}
|
|
@@ -3484,7 +3511,7 @@ var TaskRunner = class {
|
|
|
3484
3511
|
}
|
|
3485
3512
|
async fetchTaskContext(taskId) {
|
|
3486
3513
|
try {
|
|
3487
|
-
const { getGraphQLClient: getGraphQLClient$1 } = await import("./graphql-client-
|
|
3514
|
+
const { getGraphQLClient: getGraphQLClient$1 } = await import("./graphql-client-DtN1tbTC.mjs");
|
|
3488
3515
|
const client = await getGraphQLClient$1();
|
|
3489
3516
|
const query$1 = graphql(`
|
|
3490
3517
|
query TaskContext($taskId: ID!) {
|
|
@@ -3496,6 +3523,7 @@ var TaskRunner = class {
|
|
|
3496
3523
|
directory
|
|
3497
3524
|
repository
|
|
3498
3525
|
branch
|
|
3526
|
+
defaultBranch
|
|
3499
3527
|
gitAuth {
|
|
3500
3528
|
privateKey
|
|
3501
3529
|
repositoryUrl
|
|
@@ -3528,6 +3556,7 @@ var TaskRunner = class {
|
|
|
3528
3556
|
directory: data.taskContext.directory,
|
|
3529
3557
|
repository: data.taskContext.repository,
|
|
3530
3558
|
branch: data.taskContext.branch,
|
|
3559
|
+
defaultBranch: data.taskContext.defaultBranch,
|
|
3531
3560
|
gitAuth: auth || void 0,
|
|
3532
3561
|
permissionMode: data.taskContext.permissionMode
|
|
3533
3562
|
};
|
|
@@ -3792,27 +3821,11 @@ async function queryClaude(prompt, mcpServer, opts) {
|
|
|
3792
3821
|
const credentials = await getCredentials();
|
|
3793
3822
|
if (!credentials?.anthropicApiKey) throw new Error("Anthropic API key not found in credentials JSON");
|
|
3794
3823
|
process.env.ANTHROPIC_API_KEY = credentials.anthropicApiKey;
|
|
3795
|
-
|
|
3796
|
-
try {
|
|
3797
|
-
const client = await getGraphQLClient();
|
|
3798
|
-
const projectData = await client.request(cliQueries.Project, { id: task.projectId.toString() });
|
|
3799
|
-
if (projectData.project?.team) {
|
|
3800
|
-
const claude = (await client.request(cliQueries.Team, { id: projectData.project.team.id })).team?.settings?.claude;
|
|
3801
|
-
if (claude) teamSettings = {
|
|
3802
|
-
defaultModel: claude.defaultModel,
|
|
3803
|
-
environmentVariables: claude.environmentVariables,
|
|
3804
|
-
betaFlags: claude.betaFlags
|
|
3805
|
-
};
|
|
3806
|
-
}
|
|
3807
|
-
} catch (error$1) {
|
|
3808
|
-
console.error("Failed to fetch team settings, using defaults:", error$1);
|
|
3809
|
-
}
|
|
3810
|
-
const defaultModel = teamSettings?.defaultModel || "sonnet";
|
|
3811
|
-
const environmentVariables = teamSettings?.environmentVariables || {};
|
|
3812
|
-
const betaFlags = teamSettings?.betaFlags || [];
|
|
3824
|
+
const { defaultModel, environmentVariables, betaFlags } = await getTaskClaudeSettings({ projectId: task.projectId.toString() });
|
|
3813
3825
|
for (const [key, value$1] of Object.entries(environmentVariables)) process.env[key] = value$1;
|
|
3814
3826
|
debugLog(`[Task ${task.id}] Querying Claude with permission mode: ${permissionMode}`);
|
|
3815
3827
|
debugLog(`[Task ${task.id}] Using model: ${defaultModel}`);
|
|
3828
|
+
if (Object.keys(environmentVariables).length > 0) debugLog(`[Task ${task.id}] Environment variables: ${Object.keys(environmentVariables).join(", ")}`);
|
|
3816
3829
|
if (betaFlags.length > 0) debugLog(`[Task ${task.id}] Beta flags: ${betaFlags.join(", ")}`);
|
|
3817
3830
|
return query({
|
|
3818
3831
|
prompt,
|
|
@@ -3851,6 +3864,49 @@ async function queryClaude(prompt, mcpServer, opts) {
|
|
|
3851
3864
|
}
|
|
3852
3865
|
});
|
|
3853
3866
|
}
|
|
3867
|
+
async function getTaskClaudeSettings(opts) {
|
|
3868
|
+
const { projectId } = opts;
|
|
3869
|
+
let teamSettings;
|
|
3870
|
+
let projectSettings;
|
|
3871
|
+
try {
|
|
3872
|
+
const client = await getGraphQLClient();
|
|
3873
|
+
const projectData = await client.request(cliQueries.Project, { id: projectId });
|
|
3874
|
+
if (projectData.project) {
|
|
3875
|
+
const projectSettingsData = projectData.project?.settings;
|
|
3876
|
+
if (projectSettingsData) projectSettings = {
|
|
3877
|
+
defaultModel: projectSettingsData.defaultModel,
|
|
3878
|
+
environmentVariables: projectSettingsData.environmentVariables,
|
|
3879
|
+
betaFlags: projectSettingsData.betaFlags
|
|
3880
|
+
};
|
|
3881
|
+
if (projectData.project.team) {
|
|
3882
|
+
const teamClaude = (await client.request(cliQueries.Team, { id: projectData.project.team.id })).team?.settings?.claude;
|
|
3883
|
+
if (teamClaude) teamSettings = {
|
|
3884
|
+
defaultModel: teamClaude.defaultModel,
|
|
3885
|
+
environmentVariables: teamClaude.environmentVariables,
|
|
3886
|
+
betaFlags: teamClaude.betaFlags
|
|
3887
|
+
};
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
} catch (error$1) {
|
|
3891
|
+
debugLog(`[Task] Failed to fetch settings, using defaults: ${error$1}`);
|
|
3892
|
+
}
|
|
3893
|
+
const defaultModel = projectSettings?.defaultModel || teamSettings?.defaultModel || "sonnet";
|
|
3894
|
+
const teamEnvVars = teamSettings?.environmentVariables || {};
|
|
3895
|
+
const projectEnvVars = projectSettings?.environmentVariables || {};
|
|
3896
|
+
const environmentVariables = {
|
|
3897
|
+
...teamEnvVars,
|
|
3898
|
+
...projectEnvVars
|
|
3899
|
+
};
|
|
3900
|
+
const betaFlags = [...teamSettings?.betaFlags || [], ...projectSettings?.betaFlags || []];
|
|
3901
|
+
debugLog(`[Task] Default model: ${defaultModel}`);
|
|
3902
|
+
debugLog(`[Task] Environment variables: ${Object.keys(environmentVariables).join(", ")}`);
|
|
3903
|
+
debugLog(`[Task] Beta flags: ${betaFlags.join(", ")}`);
|
|
3904
|
+
return {
|
|
3905
|
+
defaultModel,
|
|
3906
|
+
environmentVariables,
|
|
3907
|
+
betaFlags
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3854
3910
|
function mapPermissionMode(permissionMode) {
|
|
3855
3911
|
switch (permissionMode) {
|
|
3856
3912
|
case "DEFAULT": return "default";
|
|
@@ -4731,8 +4787,8 @@ Host gitlab.com
|
|
|
4731
4787
|
this.debugLog("Branch does not exist, creating...");
|
|
4732
4788
|
await execAsync$1(`git checkout -b ${this.ctx.branch}`);
|
|
4733
4789
|
}
|
|
4734
|
-
this.debugLog(
|
|
4735
|
-
await execAsync$1(`git pull origin
|
|
4790
|
+
this.debugLog(`Pulling from default branch (${this.ctx.defaultBranch})...`);
|
|
4791
|
+
await execAsync$1(`git pull origin ${this.ctx.defaultBranch}`);
|
|
4736
4792
|
this.debugLog("Pulling from task branch...");
|
|
4737
4793
|
try {
|
|
4738
4794
|
await execAsync$1(`git pull origin ${this.ctx.branch}`);
|
|
@@ -4753,7 +4809,7 @@ Host gitlab.com
|
|
|
4753
4809
|
const jsonlContent = sessionLines.map((m) => JSON.stringify(m)).join("\n");
|
|
4754
4810
|
await writeFile(this.claudeSessionFilePath, jsonlContent + "\n");
|
|
4755
4811
|
this.debugLog(`ā Saved session file to ${this.claudeSessionFilePath}`);
|
|
4756
|
-
this.debugLog(`
|
|
4812
|
+
this.debugLog(`Contains ${messages.length.toLocaleString()} session lines`);
|
|
4757
4813
|
}
|
|
4758
4814
|
async readNewSessionLines() {
|
|
4759
4815
|
try {
|
|
@@ -4879,7 +4935,7 @@ Host gitlab.com
|
|
|
4879
4935
|
}
|
|
4880
4936
|
});
|
|
4881
4937
|
} finally {
|
|
4882
|
-
this.claudeAbortController
|
|
4938
|
+
this.claudeAbortController?.signal.removeEventListener("abort", abortHandler);
|
|
4883
4939
|
this.stopSessionFileWatcher();
|
|
4884
4940
|
this.claudeAbortController = null;
|
|
4885
4941
|
this.sendStatusUpdate("idle");
|
|
@@ -5219,6 +5275,207 @@ async function logsCommand() {
|
|
|
5219
5275
|
async function startTaskCommand(taskCtx) {
|
|
5220
5276
|
new Task(JSON.parse(decode(taskCtx))).start();
|
|
5221
5277
|
}
|
|
5278
|
+
/**
|
|
5279
|
+
* Install the task runner as a system service
|
|
5280
|
+
*/
|
|
5281
|
+
async function installServiceCommand() {
|
|
5282
|
+
console.log(chalk.bold("\nJive Task Runner Service Installation"));
|
|
5283
|
+
console.log(chalk.dim("=========================================\n"));
|
|
5284
|
+
try {
|
|
5285
|
+
const { getServiceManager, validateServiceInstallation, detectPlatform } = await import("./service-DTf-SkmQ.mjs");
|
|
5286
|
+
if (detectPlatform() === "unsupported") {
|
|
5287
|
+
console.error(chalk.red("Service installation is not supported on this platform."));
|
|
5288
|
+
console.log(chalk.dim("\nCurrently supported platforms:"));
|
|
5289
|
+
console.log(chalk.dim(" - Linux (systemd)"));
|
|
5290
|
+
console.log(chalk.dim("\nMacOS and Windows support coming soon!"));
|
|
5291
|
+
process.exit(1);
|
|
5292
|
+
}
|
|
5293
|
+
const spinner = ora("Validating service installation requirements...").start();
|
|
5294
|
+
const validation = await validateServiceInstallation();
|
|
5295
|
+
spinner.stop();
|
|
5296
|
+
console.log(chalk.bold("\nValidation Results:\n"));
|
|
5297
|
+
for (const check of validation.checks) {
|
|
5298
|
+
let icon = "ā";
|
|
5299
|
+
let color = chalk.green;
|
|
5300
|
+
if (check.status === "warning") {
|
|
5301
|
+
icon = "ā ";
|
|
5302
|
+
color = chalk.yellow;
|
|
5303
|
+
} else if (check.status === "error") {
|
|
5304
|
+
icon = "ā";
|
|
5305
|
+
color = chalk.red;
|
|
5306
|
+
}
|
|
5307
|
+
console.log(color(`${icon} ${check.name.padEnd(20)} ${check.message}`));
|
|
5308
|
+
if (check.detail) console.log(chalk.dim(` ${check.detail}`));
|
|
5309
|
+
}
|
|
5310
|
+
if (!validation.canInstall) {
|
|
5311
|
+
console.log(chalk.red("\nā Cannot install service due to validation errors."));
|
|
5312
|
+
console.log(chalk.dim("Please fix the errors above and try again."));
|
|
5313
|
+
process.exit(1);
|
|
5314
|
+
}
|
|
5315
|
+
if (validation.hasWarnings) console.log(chalk.yellow("\nā Installation can proceed, but there are warnings."));
|
|
5316
|
+
console.log(chalk.dim(`\nPlatform: Linux (systemd)`));
|
|
5317
|
+
console.log(chalk.dim(`Service file: ~/.config/systemd/user/jive-task-runner.service\n`));
|
|
5318
|
+
const { confirm } = await prompts({
|
|
5319
|
+
type: "confirm",
|
|
5320
|
+
name: "confirm",
|
|
5321
|
+
message: "Continue with installation?",
|
|
5322
|
+
initial: true
|
|
5323
|
+
});
|
|
5324
|
+
if (!confirm) {
|
|
5325
|
+
console.log(chalk.yellow("Installation cancelled"));
|
|
5326
|
+
return;
|
|
5327
|
+
}
|
|
5328
|
+
spinner.start("Creating service file...");
|
|
5329
|
+
const manager = getServiceManager();
|
|
5330
|
+
await manager.install();
|
|
5331
|
+
spinner.succeed("Service installed successfully!");
|
|
5332
|
+
const { startNow } = await prompts({
|
|
5333
|
+
type: "confirm",
|
|
5334
|
+
name: "startNow",
|
|
5335
|
+
message: "Start service now?",
|
|
5336
|
+
initial: true
|
|
5337
|
+
});
|
|
5338
|
+
if (startNow) {
|
|
5339
|
+
spinner.start("Starting service...");
|
|
5340
|
+
await manager.start();
|
|
5341
|
+
spinner.succeed("Service started");
|
|
5342
|
+
const status = await manager.status();
|
|
5343
|
+
console.log(chalk.green(`\nService Status: ā Running`));
|
|
5344
|
+
if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
|
|
5345
|
+
}
|
|
5346
|
+
console.log(chalk.bold("\nNext steps:"));
|
|
5347
|
+
console.log(chalk.dim(" ⢠View logs: ") + chalk.cyan("jive task-runner service-logs"));
|
|
5348
|
+
console.log(chalk.dim(" ⢠Check status: ") + chalk.cyan("jive task-runner service-status"));
|
|
5349
|
+
console.log(chalk.dim(" ⢠Restart: ") + chalk.cyan("jive task-runner service-restart"));
|
|
5350
|
+
} catch (error$1) {
|
|
5351
|
+
console.error(chalk.red(`\nā Installation failed: ${error$1.message}`));
|
|
5352
|
+
process.exit(1);
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
/**
|
|
5356
|
+
* Uninstall the task runner service
|
|
5357
|
+
*/
|
|
5358
|
+
async function uninstallServiceCommand() {
|
|
5359
|
+
try {
|
|
5360
|
+
const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
|
|
5361
|
+
const manager = getServiceManager();
|
|
5362
|
+
if (!await manager.isInstalled()) {
|
|
5363
|
+
console.log(chalk.yellow("Service is not installed."));
|
|
5364
|
+
return;
|
|
5365
|
+
}
|
|
5366
|
+
const { confirm } = await prompts({
|
|
5367
|
+
type: "confirm",
|
|
5368
|
+
name: "confirm",
|
|
5369
|
+
message: chalk.yellow("ā Remove the task runner service?"),
|
|
5370
|
+
initial: false
|
|
5371
|
+
});
|
|
5372
|
+
if (!confirm) {
|
|
5373
|
+
console.log(chalk.yellow("Cancelled"));
|
|
5374
|
+
return;
|
|
5375
|
+
}
|
|
5376
|
+
const spinner = ora("Uninstalling service...").start();
|
|
5377
|
+
await manager.uninstall();
|
|
5378
|
+
spinner.succeed("Service uninstalled successfully");
|
|
5379
|
+
console.log(chalk.dim("\nTo reinstall, run: ") + chalk.cyan("jive task-runner install-service"));
|
|
5380
|
+
} catch (error$1) {
|
|
5381
|
+
console.error(chalk.red(`\nā Uninstall failed: ${error$1.message}`));
|
|
5382
|
+
process.exit(1);
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
/**
|
|
5386
|
+
* Show service status
|
|
5387
|
+
*/
|
|
5388
|
+
async function serviceStatusCommand() {
|
|
5389
|
+
try {
|
|
5390
|
+
const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
|
|
5391
|
+
const manager = getServiceManager();
|
|
5392
|
+
if (!await manager.isInstalled()) {
|
|
5393
|
+
console.log(chalk.dim("Service is not installed."));
|
|
5394
|
+
console.log(chalk.dim("\nTo install, run: ") + chalk.cyan("jive task-runner install-service"));
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5397
|
+
const spinner = ora("Checking service status...").start();
|
|
5398
|
+
const status = await manager.status();
|
|
5399
|
+
spinner.stop();
|
|
5400
|
+
console.log(chalk.bold("\nJive Task Runner Service"));
|
|
5401
|
+
console.log(chalk.dim("ā".repeat(60)));
|
|
5402
|
+
const statusIcon = status.running ? "ā" : "ā";
|
|
5403
|
+
const statusColor = status.running ? chalk.green : chalk.dim;
|
|
5404
|
+
const statusText = status.running ? "Running" : "Stopped";
|
|
5405
|
+
console.log(`\nStatus: ${statusColor(statusIcon)} ${statusColor(statusText)}`);
|
|
5406
|
+
if (status.pid) console.log(chalk.dim(`PID: ${status.pid}`));
|
|
5407
|
+
if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
|
|
5408
|
+
const autoStartIcon = status.enabled ? "ā" : "ā";
|
|
5409
|
+
const autoStartColor = status.enabled ? chalk.green : chalk.dim;
|
|
5410
|
+
console.log(`Auto-start: ${autoStartColor(autoStartIcon)} ${status.enabled ? "Enabled" : "Disabled"}`);
|
|
5411
|
+
if (status.running) {
|
|
5412
|
+
console.log(chalk.bold("\nRecent Logs:"));
|
|
5413
|
+
console.log(chalk.dim("ā".repeat(60)));
|
|
5414
|
+
try {
|
|
5415
|
+
await manager.logs({ lines: 20 });
|
|
5416
|
+
} catch {
|
|
5417
|
+
console.log(chalk.dim("No logs available"));
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
console.log(chalk.bold("\nCommands:"));
|
|
5421
|
+
console.log(chalk.dim("ā".repeat(60)));
|
|
5422
|
+
console.log(chalk.dim(" Logs: ") + chalk.cyan("jive task-runner service-logs [-f]"));
|
|
5423
|
+
console.log(chalk.dim(" Restart: ") + chalk.cyan("jive task-runner service-restart"));
|
|
5424
|
+
if (status.running) console.log(chalk.dim(" Stop: ") + chalk.cyan("systemctl --user stop jive-task-runner"));
|
|
5425
|
+
else console.log(chalk.dim(" Start: ") + chalk.cyan("systemctl --user start jive-task-runner"));
|
|
5426
|
+
console.log(chalk.dim(" Uninstall: ") + chalk.cyan("jive task-runner uninstall-service"));
|
|
5427
|
+
console.log();
|
|
5428
|
+
} catch (error$1) {
|
|
5429
|
+
console.error(chalk.red(`\nā Failed to get status: ${error$1.message}`));
|
|
5430
|
+
process.exit(1);
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
/**
|
|
5434
|
+
* View service logs
|
|
5435
|
+
*/
|
|
5436
|
+
async function serviceLogsCommand(options) {
|
|
5437
|
+
try {
|
|
5438
|
+
const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
|
|
5439
|
+
const manager = getServiceManager();
|
|
5440
|
+
if (!await manager.isInstalled()) {
|
|
5441
|
+
console.log(chalk.dim("Service is not installed."));
|
|
5442
|
+
return;
|
|
5443
|
+
}
|
|
5444
|
+
const lines = options.lines ? parseInt(options.lines, 10) : 50;
|
|
5445
|
+
await manager.logs({
|
|
5446
|
+
follow: options.follow,
|
|
5447
|
+
lines
|
|
5448
|
+
});
|
|
5449
|
+
} catch (error$1) {
|
|
5450
|
+
if (error$1.message.includes("SIGTERM")) return;
|
|
5451
|
+
console.error(chalk.red(`\nā Failed to view logs: ${error$1.message}`));
|
|
5452
|
+
process.exit(1);
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
/**
|
|
5456
|
+
* Restart the service
|
|
5457
|
+
*/
|
|
5458
|
+
async function serviceRestartCommand() {
|
|
5459
|
+
try {
|
|
5460
|
+
const { getServiceManager } = await import("./service-DTf-SkmQ.mjs");
|
|
5461
|
+
const manager = getServiceManager();
|
|
5462
|
+
if (!await manager.isInstalled()) {
|
|
5463
|
+
console.log(chalk.dim("Service is not installed."));
|
|
5464
|
+
return;
|
|
5465
|
+
}
|
|
5466
|
+
const spinner = ora("Restarting service...").start();
|
|
5467
|
+
await manager.restart();
|
|
5468
|
+
spinner.succeed("Service restarted");
|
|
5469
|
+
const status = await manager.status();
|
|
5470
|
+
if (status.running) {
|
|
5471
|
+
console.log(chalk.green(`\nā Service is running`));
|
|
5472
|
+
if (status.uptime) console.log(chalk.dim(`Uptime: ${status.uptime}`));
|
|
5473
|
+
}
|
|
5474
|
+
} catch (error$1) {
|
|
5475
|
+
console.error(chalk.red(`\nā Failed to restart: ${error$1.message}`));
|
|
5476
|
+
process.exit(1);
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5222
5479
|
const taskRunnerCommands = {
|
|
5223
5480
|
init: initCommand$1,
|
|
5224
5481
|
setup: setupRunnerCommand,
|
|
@@ -5227,7 +5484,12 @@ const taskRunnerCommands = {
|
|
|
5227
5484
|
stop: stopRunnerCommand,
|
|
5228
5485
|
remove: removeRunnerCommand,
|
|
5229
5486
|
logs: logsCommand,
|
|
5230
|
-
startTask: startTaskCommand
|
|
5487
|
+
startTask: startTaskCommand,
|
|
5488
|
+
installService: installServiceCommand,
|
|
5489
|
+
uninstallService: uninstallServiceCommand,
|
|
5490
|
+
serviceStatus: serviceStatusCommand,
|
|
5491
|
+
serviceLogs: serviceLogsCommand,
|
|
5492
|
+
serviceRestart: serviceRestartCommand
|
|
5231
5493
|
};
|
|
5232
5494
|
|
|
5233
5495
|
//#endregion
|
|
@@ -5445,10 +5707,6 @@ async function resumeCommand(taskId) {
|
|
|
5445
5707
|
}
|
|
5446
5708
|
const taskCommands = { resume: resumeCommand };
|
|
5447
5709
|
|
|
5448
|
-
//#endregion
|
|
5449
|
-
//#region package.json
|
|
5450
|
-
var version = "0.0.32";
|
|
5451
|
-
|
|
5452
5710
|
//#endregion
|
|
5453
5711
|
//#region src/index.ts
|
|
5454
5712
|
const program = new Command();
|
|
@@ -5468,8 +5726,13 @@ taskRunner.command("stop").description("Stop the task runner").action(taskRunner
|
|
|
5468
5726
|
taskRunner.command("remove").description("Remove the task runner configuration").action(taskRunnerCommands.remove);
|
|
5469
5727
|
taskRunner.command("logs").description("View logs from the task runner").action(taskRunnerCommands.logs);
|
|
5470
5728
|
taskRunner.command("start-task").description("Start a task").argument("<task-ctx>", "JSON Context of the task to start").action(taskRunnerCommands.startTask);
|
|
5729
|
+
taskRunner.command("install-service").description("Install task runner as a system service").action(taskRunnerCommands.installService);
|
|
5730
|
+
taskRunner.command("uninstall-service").description("Uninstall task runner service").action(taskRunnerCommands.uninstallService);
|
|
5731
|
+
taskRunner.command("service-status").description("Show service status").action(taskRunnerCommands.serviceStatus);
|
|
5732
|
+
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);
|
|
5733
|
+
taskRunner.command("service-restart").description("Restart the service").action(taskRunnerCommands.serviceRestart);
|
|
5471
5734
|
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
5735
|
program.parse();
|
|
5473
5736
|
|
|
5474
5737
|
//#endregion
|
|
5475
|
-
export { queries as
|
|
5738
|
+
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.
|
|
4
|
+
"version": "0.0.35",
|
|
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
|
|
19
|
-
"docker:build:local": "bun run build && docker build --build-arg USE_LOCAL=true -t
|
|
20
|
-
"docker:build:public": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64
|
|
21
|
-
"docker:push": "docker buildx build --platform linux/amd64
|
|
22
|
-
"docker:publish": "touch jive-ai-cli-0.0.0.tgz && docker buildx build --platform linux/amd64
|
|
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": "docker rmi jiveai/task:$npm_package_version jiveai/task:latest; bun run build && docker build --build-arg USE_LOCAL=true -t jiveai/task:latest -t jiveai/task:$npm_package_version .",
|
|
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": "",
|