@corelayer-ai/cli 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,12 @@
1
- import { execSync } from "node:child_process";
1
+ import { execFileSync } from "node:child_process";
2
2
  import { CorelayerClient } from "../lib/api-client.js";
3
- import { resolveToken, resolveApiUrl } from "../lib/config.js";
3
+ import { readFlag } from "../lib/args.js";
4
+ import { resolveApiUrl, resolveToken } from "../lib/config.js";
4
5
  import { fail, printJson } from "../lib/output.js";
5
6
  const MAX_DIFF_CHARS = 12_000;
6
7
  function printHelp() {
7
- process.stdout.write(`corelayer preflight - Check a change against org memory before you push
8
+ process.stdout
9
+ .write(`corelayer preflight - Check a change against org memory before you push
8
10
 
9
11
  Surfaces failure modes and things to double-check that your org has hit before for
10
12
  changes like this one. Run inside a git repo with no arguments and it derives the PR
@@ -28,15 +30,9 @@ EXAMPLES:
28
30
  corelayer preflight --description "Add Redis cache to the payments worker" --repo payments-api
29
31
  `);
30
32
  }
31
- function readFlag(args, flag) {
32
- const index = args.indexOf(flag);
33
- if (index === -1)
34
- return undefined;
35
- return args[index + 1];
36
- }
37
33
  function git(args) {
38
34
  try {
39
- return execSync(`git ${args}`, {
35
+ return execFileSync("git", args, {
40
36
  encoding: "utf8",
41
37
  stdio: ["ignore", "pipe", "ignore"],
42
38
  }).trim();
@@ -46,18 +42,23 @@ function git(args) {
46
42
  }
47
43
  }
48
44
  function detectRepo() {
49
- const url = git("config --get remote.origin.url");
45
+ const url = git(["config", "--get", "remote.origin.url"]);
50
46
  if (!url)
51
47
  return undefined;
52
48
  const match = url.replace(/\.git$/, "").match(/([^/:]+)$/);
53
49
  return match ? match[1] : undefined;
54
50
  }
55
51
  function detectBase() {
56
- const head = git("rev-parse --abbrev-ref origin/HEAD"); // e.g. origin/main
52
+ const head = git(["rev-parse", "--abbrev-ref", "origin/HEAD"]);
57
53
  if (head && head.startsWith("origin/"))
58
- return head.slice("origin/".length);
54
+ return head;
59
55
  for (const candidate of ["main", "master"]) {
60
- if (git(`rev-parse --verify ${candidate}`))
56
+ if (git([
57
+ "rev-parse",
58
+ "--verify",
59
+ "--end-of-options",
60
+ `${candidate}^{commit}`,
61
+ ]))
61
62
  return candidate;
62
63
  }
63
64
  return "main";
@@ -81,15 +82,28 @@ function printChecks(checks, noColor) {
81
82
  process.stdout.write(` ${severityColor(check.severity, noColor)} · "${check.title}" · ${where}${seen}\n`);
82
83
  });
83
84
  }
84
- export async function runPreflight(args, ctx) {
85
- if (args.includes("--help") || args.includes("-h")) {
86
- printHelp();
87
- return;
88
- }
85
+ function buildPreflightContext(args) {
89
86
  const base = readFlag(args, "--base") || detectBase();
90
- const commits = git(`log ${base}..HEAD --pretty=format:%s%n%b`) || "";
91
- const files = git(`diff --name-only ${base}...HEAD`) || "";
92
- const branch = git("rev-parse --abbrev-ref HEAD");
87
+ const baseCommit = git([
88
+ "rev-parse",
89
+ "--verify",
90
+ "--end-of-options",
91
+ `${base}^{commit}`,
92
+ ]);
93
+ const commits = baseCommit
94
+ ? git(["log", "--pretty=format:%s%n%b", `${baseCommit}..HEAD`, "--"]) || ""
95
+ : "";
96
+ const files = baseCommit
97
+ ? git([
98
+ "diff",
99
+ "--no-ext-diff",
100
+ "--no-textconv",
101
+ "--name-only",
102
+ `${baseCommit}...HEAD`,
103
+ "--",
104
+ ]) || ""
105
+ : "";
106
+ const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
93
107
  const repo = readFlag(args, "--repo") || detectRepo();
94
108
  const title = readFlag(args, "--title") || commits.split("\n")[0]?.trim() || branch;
95
109
  let description = readFlag(args, "--description");
@@ -101,11 +115,37 @@ export async function runPreflight(args, ctx) {
101
115
  parts.push(`Changed files:\n${files.trim()}`);
102
116
  description = parts.join("\n\n");
103
117
  }
104
- if (args.includes("--diff")) {
105
- const diff = git(`diff ${base}...HEAD`);
118
+ if (args.includes("--diff") && baseCommit) {
119
+ const diff = git([
120
+ "diff",
121
+ "--no-ext-diff",
122
+ "--no-textconv",
123
+ `${baseCommit}...HEAD`,
124
+ "--",
125
+ ]);
106
126
  if (diff)
107
127
  description += `\n\nDiff:\n${diff.slice(0, MAX_DIFF_CHARS)}`;
108
128
  }
129
+ return { base, description, repo, title };
130
+ }
131
+ function printPreflightResult(result, ctx) {
132
+ if (result.checks.length === 0) {
133
+ if (!ctx.quiet) {
134
+ process.stdout.write("✓ Preflight: nothing in org memory to double-check for this change.\n");
135
+ }
136
+ return;
137
+ }
138
+ if (!ctx.quiet) {
139
+ process.stdout.write(`Preflight — ${result.checks.length} thing(s) to double-check before you push:\n\n`);
140
+ }
141
+ printChecks(result.checks, ctx.noColor);
142
+ }
143
+ export async function runPreflight(args, ctx) {
144
+ if (args.includes("--help") || args.includes("-h")) {
145
+ printHelp();
146
+ return;
147
+ }
148
+ const { base, description, repo, title } = buildPreflightContext(args);
109
149
  if (!description.trim()) {
110
150
  fail(`No PR context found. Run inside a git repo with commits ahead of "${base}", or pass --description.`);
111
151
  }
@@ -124,14 +164,5 @@ export async function runPreflight(args, ctx) {
124
164
  printJson(result);
125
165
  return;
126
166
  }
127
- if (result.checks.length === 0) {
128
- if (!ctx.quiet) {
129
- process.stdout.write("✓ Preflight: nothing in org memory to double-check for this change.\n");
130
- }
131
- return;
132
- }
133
- if (!ctx.quiet) {
134
- process.stdout.write(`Preflight — ${result.checks.length} thing(s) to double-check before you push:\n\n`);
135
- }
136
- printChecks(result.checks, ctx.noColor);
167
+ printPreflightResult(result, ctx);
137
168
  }
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { CorelayerClient } from "../lib/api-client.js";
3
+ import { readFlag } from "../lib/args.js";
4
+ import { resolveApiUrl, resolveToken } from "../lib/config.js";
5
+ import { fail, printJson } from "../lib/output.js";
6
+ export async function runSchedules(args, ctx) {
7
+ if (args.includes("--help") || args.includes("-h")) {
8
+ process.stdout.write(`corelayer schedules - Manage native scheduled tasks\n\nUSAGE:\n corelayer schedules list --group <id>\n corelayer schedules create --group <id> --file schedule.json\n corelayer schedules update <id> --group <id> --file patch.json\n corelayer schedules pause|resume|delete|runs <id> --group <id>\n\nCREATE FILE:\n {"label":"Daily review","prompt":"Review open issues","schedule_kind":"cron","schedule_expr":"0 9 * * 1-5","timezone":"America/New_York","is_enabled":true}\n\nRun output is saved in Monitoring > Schedules. Jobs are scoped to the selected group.\n`);
9
+ return;
10
+ }
11
+ const action = args[0], group = readFlag(args, "--group"), id = args[1];
12
+ if (!group || group.startsWith("--"))
13
+ fail("Provide --group <id>.");
14
+ if (!["list", "create", "update", "pause", "resume", "delete", "runs"].includes(action))
15
+ fail("Run corelayer schedules --help for usage.");
16
+ if (!["list", "create"].includes(action) && (!id || id.startsWith("--")))
17
+ fail("Provide a schedule id.");
18
+ const token = resolveToken();
19
+ if (!token)
20
+ fail("Not authenticated. Run corelayer login.");
21
+ const client = new CorelayerClient(resolveApiUrl(ctx.apiUrlOverride), token);
22
+ const query = `?groupId=${encodeURIComponent(group)}`, target = `/${encodeURIComponent(id ?? "")}`;
23
+ const body = readScheduleBody(action, args);
24
+ const result = action === "list"
25
+ ? await client.scheduleRequest("GET", query)
26
+ : action === "runs"
27
+ ? await client.scheduleRequest("GET", `${target}/runs${query}`)
28
+ : action === "delete"
29
+ ? await client.scheduleRequest("DELETE", `${target}${query}`)
30
+ : await client.scheduleRequest(action === "create" ? "POST" : "PATCH", action === "create" ? "" : target, {
31
+ ...body,
32
+ groupId: group,
33
+ ...(action === "pause" || action === "resume"
34
+ ? { is_enabled: action === "resume" }
35
+ : {}),
36
+ });
37
+ printJson(result);
38
+ }
39
+ function readScheduleBody(action, args) {
40
+ let body = {};
41
+ if (action === "create" || action === "update") {
42
+ const file = readFlag(args, "--file");
43
+ if (!file || file.startsWith("--"))
44
+ fail("Provide --file <JSON file>.");
45
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
46
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
47
+ fail("The file must contain a JSON object.");
48
+ body = parsed;
49
+ }
50
+ return body;
51
+ }
package/dist/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { runApiKeys } from "./commands/api-keys.js";
3
2
  import { createRequire } from "node:module";
3
+ import { runAnomalies } from "./commands/anomalies.js";
4
+ import { runApiKeys } from "./commands/api-keys.js";
4
5
  import { runConfig } from "./commands/config.js";
5
6
  import { runGroups } from "./commands/groups.js";
7
+ import { runInstallSkill, runUninstallSkill, } from "./commands/install-skill.js";
6
8
  import { runIntegrations } from "./commands/integrations.js";
7
9
  import { runIssues } from "./commands/issues.js";
8
- import { runInstallSkill, runUninstallSkill, } from "./commands/install-skill.js";
9
10
  import { runLogin } from "./commands/login.js";
10
11
  import { runLogout } from "./commands/logout.js";
11
12
  import { runPreflight } from "./commands/preflight.js";
13
+ import { runSchedules } from "./commands/schedules.js";
12
14
  const require = createRequire(import.meta.url);
13
15
  const { version } = require("../package.json");
14
16
  function printHelp() {
@@ -24,6 +26,8 @@ COMMANDS:
24
26
  api-keys Create Corelayer API keys
25
27
  groups List and inspect groups
26
28
  issues List, inspect, and manage issues
29
+ schedules Create and manage native scheduled tasks
30
+ anomalies List and inspect configured anomaly monitors
27
31
  preflight Check a change against org memory before you push
28
32
  integrations List integrations for a group
29
33
  config Read and write local CLI config
@@ -112,6 +116,12 @@ async function main() {
112
116
  case "issues":
113
117
  await runIssues(rest, ctx);
114
118
  return;
119
+ case "anomalies":
120
+ await runAnomalies(rest, ctx);
121
+ return;
122
+ case "schedules":
123
+ await runSchedules(rest, ctx);
124
+ break;
115
125
  case "groups":
116
126
  await runGroups(rest, ctx);
117
127
  return;
@@ -139,7 +149,7 @@ async function main() {
139
149
  process.exit(1);
140
150
  }
141
151
  }
142
- main().catch(error => {
152
+ main().catch((error) => {
143
153
  process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
144
154
  process.exit(1);
145
155
  });
@@ -1,3 +1,4 @@
1
+ import { AnomalyApiClient } from "@corelayer-ai/sdk";
1
2
  async function parseError(res) {
2
3
  let body = null;
3
4
  try {
@@ -18,9 +19,11 @@ async function parseError(res) {
18
19
  export class CorelayerClient {
19
20
  baseUrl;
20
21
  token;
22
+ anomalies;
21
23
  constructor(baseUrl, token) {
22
24
  this.baseUrl = baseUrl;
23
25
  this.token = token;
26
+ this.anomalies = new AnomalyApiClient((method, path) => this.request(method, path));
24
27
  }
25
28
  async request(method, path, body) {
26
29
  const controller = new AbortController();
@@ -61,6 +64,9 @@ export class CorelayerClient {
61
64
  }
62
65
  return this.request("GET", `/api/v1/issues?${params.toString()}`);
63
66
  }
67
+ async scheduleRequest(method, suffix, body) {
68
+ return this.request(method, `/api/v1/schedules${suffix}`, body);
69
+ }
64
70
  async getIssue(issueId) {
65
71
  return this.request("GET", `/api/v1/issues/${issueId}`);
66
72
  }
@@ -134,6 +140,9 @@ export class CorelayerClient {
134
140
  rawKey: data.rawKey,
135
141
  };
136
142
  }
143
+ async logout() {
144
+ await this.request("POST", "/api/v1/cli/logout");
145
+ }
137
146
  async listIntegrations(groupId) {
138
147
  const params = new URLSearchParams();
139
148
  if (groupId) {
@@ -0,0 +1,4 @@
1
+ export function readFlag(args, flag) {
2
+ const index = args.indexOf(flag);
3
+ return index === -1 ? undefined : args[index + 1];
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corelayer-ai/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Corelayer CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,10 +18,12 @@
18
18
  "dev": "tsx src/index.ts",
19
19
  "prepublishOnly": "npm run build"
20
20
  },
21
- "dependencies": {},
21
+ "dependencies": {
22
+ "@corelayer-ai/sdk": "^0.2.1"
23
+ },
22
24
  "devDependencies": {
23
25
  "@types/node": "^20.17.57",
24
- "tsx": "^4.19.4",
26
+ "tsx": "^4.22.4",
25
27
  "typescript": "^5.8.3"
26
28
  }
27
29
  }