@corelayer-ai/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/groups.js +4 -5
- package/dist/commands/install-skill.js +45 -0
- package/dist/commands/integrations.js +4 -4
- package/dist/commands/issues.js +4 -4
- package/dist/commands/login.js +27 -0
- package/dist/index.js +23 -0
- package/dist/lib/auth.js +7 -1
- package/dist/lib/config.js +8 -1
- package/package.json +3 -2
- package/skill/SKILL.md +150 -0
package/dist/commands/groups.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { CorelayerClient } from "../lib/api-client.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveToken, resolveApiUrl } from "../lib/config.js";
|
|
3
3
|
import { fail, formatDate, printJson, printTable } from "../lib/output.js";
|
|
4
4
|
export async function runGroups(args, ctx) {
|
|
5
5
|
const sub = args[0];
|
|
6
6
|
if (sub !== "list") {
|
|
7
7
|
fail("Usage: corelayer groups list");
|
|
8
8
|
}
|
|
9
|
-
const
|
|
10
|
-
const token = config.token;
|
|
9
|
+
const token = resolveToken();
|
|
11
10
|
if (!token) {
|
|
12
|
-
fail("Not
|
|
11
|
+
fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
|
|
13
12
|
}
|
|
14
|
-
const apiUrl = ctx.apiUrlOverride
|
|
13
|
+
const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
|
|
15
14
|
const client = new CorelayerClient(apiUrl, token);
|
|
16
15
|
const result = await client.listGroups();
|
|
17
16
|
if (ctx.json) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, unlinkSync, rmdirSync, } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fail } from "../lib/output.js";
|
|
6
|
+
const SKILL_NAME = "corelayer";
|
|
7
|
+
function getSkillTargetDir() {
|
|
8
|
+
return join(homedir(), ".claude", "skills", SKILL_NAME);
|
|
9
|
+
}
|
|
10
|
+
function getSkillSourcePath() {
|
|
11
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
12
|
+
const distDir = dirname(dirname(thisFile));
|
|
13
|
+
const packageDir = dirname(distDir);
|
|
14
|
+
return join(packageDir, "skill", "SKILL.md");
|
|
15
|
+
}
|
|
16
|
+
export async function runInstallSkill(_args, ctx) {
|
|
17
|
+
const source = getSkillSourcePath();
|
|
18
|
+
if (!existsSync(source)) {
|
|
19
|
+
fail("Skill file not found. The CLI package may be incomplete.");
|
|
20
|
+
}
|
|
21
|
+
const targetDir = getSkillTargetDir();
|
|
22
|
+
const targetFile = join(targetDir, "SKILL.md");
|
|
23
|
+
mkdirSync(targetDir, { recursive: true });
|
|
24
|
+
copyFileSync(source, targetFile);
|
|
25
|
+
if (!ctx.quiet) {
|
|
26
|
+
process.stdout.write(`Corelayer skill installed to ${targetFile}\n`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function runUninstallSkill(_args, ctx) {
|
|
30
|
+
const targetDir = getSkillTargetDir();
|
|
31
|
+
const targetFile = join(targetDir, "SKILL.md");
|
|
32
|
+
if (!existsSync(targetFile)) {
|
|
33
|
+
fail("Corelayer skill is not installed.");
|
|
34
|
+
}
|
|
35
|
+
unlinkSync(targetFile);
|
|
36
|
+
try {
|
|
37
|
+
rmdirSync(targetDir);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// directory not empty or already removed
|
|
41
|
+
}
|
|
42
|
+
if (!ctx.quiet) {
|
|
43
|
+
process.stdout.write("Corelayer skill uninstalled.\n");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CorelayerClient } from "../lib/api-client.js";
|
|
2
|
-
import { readConfig } from "../lib/config.js";
|
|
2
|
+
import { readConfig, resolveToken, resolveApiUrl } from "../lib/config.js";
|
|
3
3
|
import { fail, printJson, printTable } from "../lib/output.js";
|
|
4
4
|
function readFlag(args, flag) {
|
|
5
5
|
const index = args.indexOf(flag);
|
|
@@ -13,12 +13,12 @@ export async function runIntegrations(args, ctx) {
|
|
|
13
13
|
fail("Usage: corelayer integrations list [--group <groupId>]");
|
|
14
14
|
}
|
|
15
15
|
const config = readConfig();
|
|
16
|
-
const token =
|
|
16
|
+
const token = resolveToken();
|
|
17
17
|
if (!token) {
|
|
18
|
-
fail("Not
|
|
18
|
+
fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
|
|
19
19
|
}
|
|
20
20
|
const groupId = readFlag(args, "--group") || config.defaults?.group;
|
|
21
|
-
const apiUrl = ctx.apiUrlOverride
|
|
21
|
+
const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
|
|
22
22
|
const client = new CorelayerClient(apiUrl, token);
|
|
23
23
|
const result = await client.listIntegrations(groupId);
|
|
24
24
|
if (ctx.json) {
|
package/dist/commands/issues.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
3
|
import { CorelayerClient } from "../lib/api-client.js";
|
|
4
|
-
import { readConfig } from "../lib/config.js";
|
|
4
|
+
import { readConfig, resolveToken, resolveApiUrl } from "../lib/config.js";
|
|
5
5
|
import { fail, formatDate, printJson, printTable } from "../lib/output.js";
|
|
6
6
|
function readFlag(args, flag) {
|
|
7
7
|
const index = args.indexOf(flag);
|
|
@@ -48,11 +48,11 @@ async function confirmDelete() {
|
|
|
48
48
|
export async function runIssues(args, ctx) {
|
|
49
49
|
const sub = args[0];
|
|
50
50
|
const config = readConfig();
|
|
51
|
-
const token =
|
|
51
|
+
const token = resolveToken();
|
|
52
52
|
if (!token) {
|
|
53
|
-
fail("Not
|
|
53
|
+
fail("Not authenticated. Set CORELAYER_API_KEY or run: corelayer login");
|
|
54
54
|
}
|
|
55
|
-
const apiUrl = ctx.apiUrlOverride
|
|
55
|
+
const apiUrl = resolveApiUrl(ctx.apiUrlOverride);
|
|
56
56
|
const client = new CorelayerClient(apiUrl, token);
|
|
57
57
|
const commandArgs = removeFlags(args.slice(1));
|
|
58
58
|
if (sub === "list") {
|
package/dist/commands/login.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loginWithBrowser, loginWithCodeDirect } from "../lib/auth.js";
|
|
2
|
+
import { updateConfig } from "../lib/config.js";
|
|
2
3
|
import { fail } from "../lib/output.js";
|
|
3
4
|
function readFlag(args, flag) {
|
|
4
5
|
const index = args.indexOf(flag);
|
|
@@ -6,8 +7,34 @@ function readFlag(args, flag) {
|
|
|
6
7
|
return undefined;
|
|
7
8
|
return args[index + 1];
|
|
8
9
|
}
|
|
10
|
+
function hasFlag(args, flag) {
|
|
11
|
+
return args.includes(flag);
|
|
12
|
+
}
|
|
13
|
+
async function readStdin() {
|
|
14
|
+
if (process.stdin.isTTY) {
|
|
15
|
+
fail("No token provided on stdin. Usage: echo $CORELAYER_API_KEY | corelayer login --with-token");
|
|
16
|
+
}
|
|
17
|
+
const chunks = [];
|
|
18
|
+
for await (const chunk of process.stdin) {
|
|
19
|
+
chunks.push(chunk);
|
|
20
|
+
}
|
|
21
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
22
|
+
}
|
|
9
23
|
export async function runLogin(args, ctx) {
|
|
10
24
|
const code = readFlag(args, "--code");
|
|
25
|
+
const withToken = hasFlag(args, "--with-token");
|
|
26
|
+
if (withToken) {
|
|
27
|
+
const token = await readStdin();
|
|
28
|
+
if (!token) {
|
|
29
|
+
fail("Empty token. Usage: echo $CORELAYER_API_KEY | corelayer login --with-token");
|
|
30
|
+
}
|
|
31
|
+
const apiUrl = ctx.apiUrlOverride || "https://api.corelayer.com";
|
|
32
|
+
updateConfig({ token, apiUrl });
|
|
33
|
+
if (!ctx.quiet) {
|
|
34
|
+
process.stdout.write(`Token saved. API URL: ${apiUrl}\n`);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
11
38
|
let email;
|
|
12
39
|
let orgName;
|
|
13
40
|
if (code) {
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { runConfig } from "./commands/config.js";
|
|
|
4
4
|
import { runGroups } from "./commands/groups.js";
|
|
5
5
|
import { runIntegrations } from "./commands/integrations.js";
|
|
6
6
|
import { runIssues } from "./commands/issues.js";
|
|
7
|
+
import { runInstallSkill, runUninstallSkill, } from "./commands/install-skill.js";
|
|
7
8
|
import { runLogin } from "./commands/login.js";
|
|
8
9
|
import { runLogout } from "./commands/logout.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
@@ -14,17 +15,33 @@ function printHelp() {
|
|
|
14
15
|
Usage:
|
|
15
16
|
corelayer login (opens browser)
|
|
16
17
|
corelayer login --code <CODE> --api-url <url> (manual)
|
|
18
|
+
corelayer login --with-token --api-url <url> (pipe token from stdin)
|
|
17
19
|
corelayer logout
|
|
18
20
|
corelayer issues <list|get|close|reopen|delete|summary> ...
|
|
19
21
|
corelayer groups list
|
|
20
22
|
corelayer integrations list [--group <groupId>]
|
|
21
23
|
corelayer config <get|set> ...
|
|
24
|
+
corelayer install-skill (install Corelayer skill)
|
|
25
|
+
corelayer uninstall-skill (remove Corelayer skill)
|
|
22
26
|
|
|
23
27
|
Global flags:
|
|
24
28
|
--json
|
|
25
29
|
--quiet, -q
|
|
26
30
|
--api-url <url>
|
|
27
31
|
--no-color
|
|
32
|
+
|
|
33
|
+
Environment variables:
|
|
34
|
+
CORELAYER_API_KEY API key for non-interactive auth (skips login)
|
|
35
|
+
CORELAYER_API_URL Server URL (default: https://api.corelayer.com)
|
|
36
|
+
CORELAYER_AUTH_URL Auth server URL (default: https://app.corelayer.com)
|
|
37
|
+
|
|
38
|
+
Non-interactive auth (CI/CD, agents):
|
|
39
|
+
export CORELAYER_API_KEY=cl_live_...
|
|
40
|
+
export CORELAYER_API_URL=https://api.corelayer.com
|
|
41
|
+
corelayer issues list --group <groupId>
|
|
42
|
+
|
|
43
|
+
Save a token to config:
|
|
44
|
+
echo $CORELAYER_API_KEY | corelayer login --with-token --api-url <url>
|
|
28
45
|
`);
|
|
29
46
|
}
|
|
30
47
|
function parseGlobalFlags(argv) {
|
|
@@ -102,6 +119,12 @@ async function main() {
|
|
|
102
119
|
case "config":
|
|
103
120
|
await runConfig(rest, ctx);
|
|
104
121
|
return;
|
|
122
|
+
case "install-skill":
|
|
123
|
+
await runInstallSkill(rest, ctx);
|
|
124
|
+
return;
|
|
125
|
+
case "uninstall-skill":
|
|
126
|
+
await runUninstallSkill(rest, ctx);
|
|
127
|
+
return;
|
|
105
128
|
default:
|
|
106
129
|
process.stderr.write(`Unknown command: ${command}\n`);
|
|
107
130
|
printHelp();
|
package/dist/lib/auth.js
CHANGED
|
@@ -151,7 +151,13 @@ export async function loginWithBrowser() {
|
|
|
151
151
|
return result;
|
|
152
152
|
}
|
|
153
153
|
function validateServerUrl(url) {
|
|
154
|
-
|
|
154
|
+
let parsed;
|
|
155
|
+
try {
|
|
156
|
+
parsed = new URL(url);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw new Error("Invalid server URL. Use a full URL such as https://app.corelayer.com or http://localhost:3000.");
|
|
160
|
+
}
|
|
155
161
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
156
162
|
throw new Error(`Unsupported protocol "${parsed.protocol}" — only http: and https: are allowed.`);
|
|
157
163
|
}
|
package/dist/lib/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
const CONFIG_DIR = path.join(os.homedir(), ".corelayer");
|
|
5
5
|
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
6
6
|
function defaultApiUrl() {
|
|
7
|
-
return process.env.CORELAYER_API_URL || "
|
|
7
|
+
return process.env.CORELAYER_API_URL || "https://api.corelayer.com";
|
|
8
8
|
}
|
|
9
9
|
export function getConfigPath() {
|
|
10
10
|
return CONFIG_PATH;
|
|
@@ -37,6 +37,7 @@ export function writeConfig(config) {
|
|
|
37
37
|
encoding: "utf8",
|
|
38
38
|
mode: 0o600,
|
|
39
39
|
});
|
|
40
|
+
fs.chmodSync(CONFIG_PATH, 0o600);
|
|
40
41
|
}
|
|
41
42
|
export function updateConfig(partial) {
|
|
42
43
|
const current = readConfig();
|
|
@@ -51,6 +52,12 @@ export function updateConfig(partial) {
|
|
|
51
52
|
writeConfig(merged);
|
|
52
53
|
return merged;
|
|
53
54
|
}
|
|
55
|
+
export function resolveToken() {
|
|
56
|
+
return process.env.CORELAYER_API_KEY || readConfig().token;
|
|
57
|
+
}
|
|
58
|
+
export function resolveApiUrl(override) {
|
|
59
|
+
return override || readConfig().apiUrl;
|
|
60
|
+
}
|
|
54
61
|
export function clearToken() {
|
|
55
62
|
const current = readConfig();
|
|
56
63
|
delete current.token;
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corelayer-ai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Corelayer CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"corelayer": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"skill"
|
|
11
12
|
],
|
|
12
13
|
"engines": {
|
|
13
14
|
"node": ">=18.0.0"
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: corelayer
|
|
3
|
+
description: >
|
|
4
|
+
Use when the user mentions production issues, errors, monitoring, incidents,
|
|
5
|
+
error analysis, root cause analysis, issue triage, Corelayer, or wants to
|
|
6
|
+
check/manage/close/reopen production issues. Also use when the user says
|
|
7
|
+
"check prod", "what's broken", "any errors", "issue summary", or references
|
|
8
|
+
the corelayer CLI.
|
|
9
|
+
user-invocable: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Corelayer CLI
|
|
13
|
+
|
|
14
|
+
Corelayer is an AI-powered error analysis and monitoring platform. The `corelayer` CLI lets you manage production issues, view root cause analyses, and triage errors directly from the terminal.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
The CLI must be installed and authenticated. Config lives at `~/.corelayer/config.json`.
|
|
19
|
+
|
|
20
|
+
If not logged in, run:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
corelayer login
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick reference
|
|
27
|
+
|
|
28
|
+
### Check production health
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
corelayer issues summary --group <groupId>
|
|
32
|
+
corelayer issues list --group <groupId>
|
|
33
|
+
corelayer issues list --severity critical
|
|
34
|
+
corelayer issues list --status open
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Inspect a specific issue
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
corelayer issues get <issueId>
|
|
41
|
+
corelayer issues get <issueId> --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Returns: title, status, severity, event count, what happened, root cause, and next steps.
|
|
45
|
+
|
|
46
|
+
### Triage issues
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
corelayer issues close <issueId> --feedback "Fixed in PR #123"
|
|
50
|
+
corelayer issues reopen <issueId>
|
|
51
|
+
corelayer issues delete <issueId> --yes
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### List groups and integrations
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
corelayer groups list
|
|
58
|
+
corelayer integrations list --group <groupId>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Configuration
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
corelayer config set default-group <groupId>
|
|
65
|
+
corelayer config get
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Setting a default group avoids repeating `--group` on every command.
|
|
69
|
+
|
|
70
|
+
## Global flags
|
|
71
|
+
|
|
72
|
+
| Flag | Purpose |
|
|
73
|
+
| ----------------- | -------------------------------------------------- |
|
|
74
|
+
| `--json` | Output as JSON (useful for piping/parsing) |
|
|
75
|
+
| `--quiet` / `-q` | Suppress non-error output |
|
|
76
|
+
| `--no-color` | Disable colored output |
|
|
77
|
+
| `--api-url <url>` | Override the configured API server for one command |
|
|
78
|
+
|
|
79
|
+
## Command reference
|
|
80
|
+
|
|
81
|
+
### `corelayer issues list [--group <id>] [--status <s>] [--severity <s>] [--limit <n>] [--page <n>]`
|
|
82
|
+
|
|
83
|
+
List issues for a group. Requires `--group` or a configured default group.
|
|
84
|
+
Filterable by status (`open`, `closed`) and severity (`critical`, `high`, `medium`, `low`).
|
|
85
|
+
|
|
86
|
+
### `corelayer issues get <issueId>`
|
|
87
|
+
|
|
88
|
+
Get full details for an issue including AI-generated analysis: what happened, root cause, and next steps.
|
|
89
|
+
|
|
90
|
+
### `corelayer issues summary [--group <id>]`
|
|
91
|
+
|
|
92
|
+
Returns a JSON severity breakdown for the group.
|
|
93
|
+
|
|
94
|
+
### `corelayer issues close <issueId> [--feedback "..."]`
|
|
95
|
+
|
|
96
|
+
Close an issue. Optionally provide feedback (e.g., link to the fix).
|
|
97
|
+
|
|
98
|
+
### `corelayer issues reopen <issueId>`
|
|
99
|
+
|
|
100
|
+
Reopen a previously closed issue.
|
|
101
|
+
|
|
102
|
+
### `corelayer issues delete <issueId> [--yes]`
|
|
103
|
+
|
|
104
|
+
Soft-delete an issue. Prompts for confirmation unless `--yes` is passed.
|
|
105
|
+
|
|
106
|
+
### `corelayer groups list`
|
|
107
|
+
|
|
108
|
+
List all groups in the organization.
|
|
109
|
+
|
|
110
|
+
### `corelayer integrations list [--group <id>]`
|
|
111
|
+
|
|
112
|
+
List connected integration accounts and their resource counts.
|
|
113
|
+
|
|
114
|
+
### `corelayer config get [key]`
|
|
115
|
+
|
|
116
|
+
Get config values. Keys: `api-url`, `default-group`. No key returns the full config.
|
|
117
|
+
|
|
118
|
+
### `corelayer config set <key> <value>`
|
|
119
|
+
|
|
120
|
+
Set config values. Keys: `api-url`, `default-group`.
|
|
121
|
+
|
|
122
|
+
### `corelayer login`
|
|
123
|
+
|
|
124
|
+
Authenticate via browser. Or use `corelayer login --code <CODE> --api-url <url>` for manual auth.
|
|
125
|
+
|
|
126
|
+
### `corelayer logout`
|
|
127
|
+
|
|
128
|
+
Clear stored credentials.
|
|
129
|
+
|
|
130
|
+
## Workflows
|
|
131
|
+
|
|
132
|
+
### "What's happening in production?"
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
corelayer issues summary
|
|
136
|
+
corelayer issues list --severity critical
|
|
137
|
+
corelayer issues get <id> --json
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Scripting and automation
|
|
141
|
+
|
|
142
|
+
Use `--json` and `--quiet` flags for machine-readable output:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
corelayer issues list --json --group <id> | jq '.issues[] | select(.severity == "critical")'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Background agent usage
|
|
149
|
+
|
|
150
|
+
When running as a background agent, always use `--json` for structured output and `--quiet` to suppress interactive messages. Set `default-group` in config so group IDs don't need to be passed every time.
|