@kinosec/cli 0.1.3 → 0.2.0

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/cli.js CHANGED
@@ -7771,6 +7771,7 @@ var KinoSecClient = class {
7771
7771
  api;
7772
7772
  assets;
7773
7773
  scans;
7774
+ schedules;
7774
7775
  findings;
7775
7776
  reports;
7776
7777
  tokens;
@@ -7781,7 +7782,7 @@ var KinoSecClient = class {
7781
7782
  this.api = distribution_default.create({
7782
7783
  prefixUrl: config.apiUrl,
7783
7784
  headers: {
7784
- "user-agent": config.userAgent ?? `kns/0.1.0 (${process.platform})`
7785
+ "user-agent": config.userAgent ?? `kinosec/0.1.0 (${process.platform})`
7785
7786
  },
7786
7787
  hooks: {
7787
7788
  beforeRequest: [
@@ -7819,6 +7820,7 @@ var KinoSecClient = class {
7819
7820
  });
7820
7821
  this.assets = new AssetsResource(this.api);
7821
7822
  this.scans = new ScansResource(this.api);
7823
+ this.schedules = new SchedulesResource(this.api);
7822
7824
  this.findings = new FindingsResource(this.api);
7823
7825
  this.reports = new ReportsResource(this.api);
7824
7826
  this.tokens = new TokensResource(this.api);
@@ -7868,6 +7870,33 @@ var ScansResource = class {
7868
7870
  return this.api.post(`v1/scans/${id}/cancel`, { json: {} }).json();
7869
7871
  }
7870
7872
  };
7873
+ var SchedulesResource = class {
7874
+ constructor(api) {
7875
+ this.api = api;
7876
+ }
7877
+ api;
7878
+ async list(params = {}) {
7879
+ return this.api.get("v1/scans/schedules", { searchParams: buildSearchParams(params) }).json();
7880
+ }
7881
+ async get(id) {
7882
+ return this.api.get(`v1/scans/schedules/${id}`).json();
7883
+ }
7884
+ async create(params) {
7885
+ return this.api.post("v1/scans/schedules", { json: params }).json();
7886
+ }
7887
+ async update(id, params) {
7888
+ return this.api.patch(`v1/scans/schedules/${id}`, { json: params }).json();
7889
+ }
7890
+ async delete(id) {
7891
+ await this.api.delete(`v1/scans/schedules/${id}`);
7892
+ }
7893
+ async pause(id) {
7894
+ return this.api.post(`v1/scans/schedules/${id}/pause`, { json: {} }).json();
7895
+ }
7896
+ async resume(id) {
7897
+ return this.api.post(`v1/scans/schedules/${id}/resume`, { json: {} }).json();
7898
+ }
7899
+ };
7871
7900
  var FindingsResource = class {
7872
7901
  constructor(api) {
7873
7902
  this.api = api;
@@ -8022,8 +8051,9 @@ function shouldUseJson() {
8022
8051
  return false;
8023
8052
  }
8024
8053
 
8025
- // src/commands/auth.ts
8026
- import { createInterface } from "readline";
8054
+ // src/lib/update-check.ts
8055
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
8056
+ import { join as join2, dirname as dirname2 } from "path";
8027
8057
 
8028
8058
  // src/lib/config.ts
8029
8059
  import { existsSync, mkdirSync, readFileSync, writeFileSync, openSync, writeSync, closeSync, unlinkSync, constants } from "fs";
@@ -8097,17 +8127,92 @@ function deleteCredentials() {
8097
8127
  }
8098
8128
  }
8099
8129
 
8130
+ // src/lib/update-check.ts
8131
+ var CACHE_FILE = join2(configDir, "update-check.json");
8132
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
8133
+ var FETCH_TIMEOUT_MS = 3e3;
8134
+ var NPM_PACKAGE = "@kinosec/cli";
8135
+ var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
8136
+ function isUpdateSuppressed() {
8137
+ return !!(process.env.KINOSEC_NO_UPDATE_CHECK === "1" || process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.JENKINS_URL);
8138
+ }
8139
+ function isNewerVersion(current, latest) {
8140
+ const c = current.split(".").map(Number);
8141
+ const l = latest.split(".").map(Number);
8142
+ for (let i = 0; i < 3; i++) {
8143
+ if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
8144
+ if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
8145
+ }
8146
+ return false;
8147
+ }
8148
+ function readCache() {
8149
+ if (!existsSync2(CACHE_FILE)) return null;
8150
+ try {
8151
+ const data = JSON.parse(readFileSync2(CACHE_FILE, "utf-8"));
8152
+ const age = Date.now() - new Date(data.checked_at).getTime();
8153
+ if (age > CACHE_TTL_MS) return null;
8154
+ return data;
8155
+ } catch {
8156
+ return null;
8157
+ }
8158
+ }
8159
+ function writeCache(latest) {
8160
+ try {
8161
+ const dir = dirname2(CACHE_FILE);
8162
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
8163
+ writeFileSync2(
8164
+ CACHE_FILE,
8165
+ JSON.stringify({ latest, checked_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n",
8166
+ "utf-8"
8167
+ );
8168
+ } catch {
8169
+ }
8170
+ }
8171
+ async function fetchLatestVersion() {
8172
+ try {
8173
+ const response = await fetch(REGISTRY_URL, {
8174
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
8175
+ });
8176
+ if (!response.ok) return null;
8177
+ const data = await response.json();
8178
+ return data.version ?? null;
8179
+ } catch {
8180
+ return null;
8181
+ }
8182
+ }
8183
+ async function checkForUpdate(currentVersion) {
8184
+ if (isUpdateSuppressed()) return;
8185
+ let latest = null;
8186
+ const cached = readCache();
8187
+ if (cached) {
8188
+ latest = cached.latest;
8189
+ } else {
8190
+ latest = await fetchLatestVersion();
8191
+ if (latest) writeCache(latest);
8192
+ }
8193
+ if (latest && isNewerVersion(currentVersion, latest)) {
8194
+ console.error(
8195
+ `
8196
+ A new version of KinoSec CLI is available: ${latest} (current: ${currentVersion})
8197
+ Update: npm update -g @kinosec/cli`
8198
+ );
8199
+ }
8200
+ }
8201
+
8202
+ // src/commands/auth.ts
8203
+ import { createInterface } from "readline";
8204
+
8100
8205
  // src/lib/auth.ts
8101
8206
  var DEFAULT_API_URL = "https://api.kinosec.ai/v1";
8102
8207
  function resolveToken(options) {
8103
8208
  const token = options.token ?? process.env.KINOSEC_TOKEN ?? readCredentials()?.token;
8104
8209
  if (!token) {
8105
- console.error('Not authenticated. Run "kns auth login" or set KINOSEC_TOKEN.');
8210
+ console.error('Not authenticated. Run "kinosec auth login" or set KINOSEC_TOKEN.');
8106
8211
  process.exit(77);
8107
8212
  }
8108
8213
  if (options.token) {
8109
8214
  console.error(
8110
- "Warning: passing a token via --token exposes it in shell history. Prefer: kns auth login (interactive), KINOSEC_TOKEN env var, or stdin pipe."
8215
+ "Warning: passing a token via --token exposes it in shell history. Prefer: kinosec auth login (interactive), KINOSEC_TOKEN env var, or stdin pipe."
8111
8216
  );
8112
8217
  }
8113
8218
  const apiUrl = options.apiUrl ?? process.env.KINOSEC_API_URL ?? getConfigValue("api_url") ?? DEFAULT_API_URL;
@@ -8132,14 +8237,14 @@ function registerAuthCommand(program3) {
8132
8237
  const auth = program3.command("auth").description("Manage authentication");
8133
8238
  auth.command("login").description("Authenticate with a KinoSec API token").addHelpText("after", `
8134
8239
  Examples:
8135
- $ kns auth login # interactive prompt
8136
- $ kns auth login --token kns_live_pat_...
8137
- $ echo $TOKEN | kns auth login # pipe from stdin`).action(async () => {
8240
+ $ kinosec auth login # interactive prompt
8241
+ $ kinosec auth login --token kns_live_pat_...
8242
+ $ echo $TOKEN | kinosec auth login # pipe from stdin`).action(async () => {
8138
8243
  try {
8139
8244
  let token = program3.opts().token;
8140
8245
  if (token) {
8141
8246
  console.error(
8142
- "Warning: passing a token via --token exposes it in shell history. Prefer: kns auth login (interactive), KINOSEC_TOKEN env var, or stdin pipe."
8247
+ "Warning: passing a token via --token exposes it in shell history. Prefer: kinosec auth login (interactive), KINOSEC_TOKEN env var, or stdin pipe."
8143
8248
  );
8144
8249
  }
8145
8250
  if (!token) {
@@ -8178,11 +8283,11 @@ Examples:
8178
8283
  });
8179
8284
  auth.command("status").description("Show current authentication status").addHelpText("after", `
8180
8285
  Examples:
8181
- $ kns auth status`).action(async () => {
8286
+ $ kinosec auth status`).action(async () => {
8182
8287
  try {
8183
8288
  const creds = readCredentials();
8184
8289
  if (!creds) {
8185
- console.error('Not authenticated. Run "kns auth login" or set KINOSEC_TOKEN.');
8290
+ console.error('Not authenticated. Run "kinosec auth login" or set KINOSEC_TOKEN.');
8186
8291
  process.exit(77);
8187
8292
  }
8188
8293
  const apiUrl = program3.opts().apiUrl ?? getConfigValue("api_url") ?? DEFAULT_API_URL;
@@ -8211,7 +8316,7 @@ Examples:
8211
8316
  });
8212
8317
  auth.command("logout").description("Remove stored credentials").addHelpText("after", `
8213
8318
  Examples:
8214
- $ kns auth logout`).action(() => {
8319
+ $ kinosec auth logout`).action(() => {
8215
8320
  deleteCredentials();
8216
8321
  printSuccess("Logged out. Credentials removed.");
8217
8322
  });
@@ -8223,8 +8328,8 @@ function registerConfigCommand(program3) {
8223
8328
  const config = program3.command("config").description("Manage CLI configuration");
8224
8329
  config.command("set").description("Set a config value").argument("<key>", `Config key (${VALID_KEYS.join(", ")})`).argument("<value>", "Config value").addHelpText("after", `
8225
8330
  Examples:
8226
- $ kns config set api_url https://custom.api.url/v1
8227
- $ kns config set output json`).action((key, value) => {
8331
+ $ kinosec config set api_url https://custom.api.url/v1
8332
+ $ kinosec config set output json`).action((key, value) => {
8228
8333
  if (!VALID_KEYS.includes(key)) {
8229
8334
  console.error(`Invalid config key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
8230
8335
  process.exit(2);
@@ -8234,7 +8339,7 @@ Examples:
8234
8339
  });
8235
8340
  config.command("get").description("Get a config value").argument("<key>", "Config key").addHelpText("after", `
8236
8341
  Examples:
8237
- $ kns config get api_url`).action((key) => {
8342
+ $ kinosec config get api_url`).action((key) => {
8238
8343
  const value = getConfigValue(key);
8239
8344
  if (value === void 0) {
8240
8345
  console.error(`Config key "${key}" is not set.`);
@@ -8244,7 +8349,7 @@ Examples:
8244
8349
  });
8245
8350
  config.command("list").description("List all config values").addHelpText("after", `
8246
8351
  Examples:
8247
- $ kns config list`).action(() => {
8352
+ $ kinosec config list`).action(() => {
8248
8353
  const cfg = readConfig();
8249
8354
  const opts = program3.opts();
8250
8355
  if (opts.json || opts.output === "json") {
@@ -8265,8 +8370,8 @@ Examples:
8265
8370
  function registerMeCommand(program3) {
8266
8371
  program3.command("me").description("Show authenticated user info").addHelpText("after", `
8267
8372
  Examples:
8268
- $ kns me
8269
- $ kns me --json`).action(async () => {
8373
+ $ kinosec me
8374
+ $ kinosec me --json`).action(async () => {
8270
8375
  try {
8271
8376
  const opts = program3.opts();
8272
8377
  const { token, apiUrl } = resolveToken(opts);
@@ -9713,13 +9818,278 @@ function ora(options) {
9713
9818
  }
9714
9819
 
9715
9820
  // src/commands/scan.ts
9821
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
9822
+
9823
+ // src/commands/schedule.ts
9716
9824
  var import_picocolors3 = __toESM(require_picocolors(), 1);
9825
+
9826
+ // src/lib/confirm.ts
9827
+ import { createInterface as createInterface2 } from "readline";
9828
+ async function confirmDestructive(message, yes) {
9829
+ if (yes) return;
9830
+ if (!isTTY()) {
9831
+ console.error("Use --yes to confirm destructive operations in non-interactive mode.");
9832
+ process.exit(2);
9833
+ }
9834
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
9835
+ const answer = await new Promise((resolve) => {
9836
+ rl.question(`${message} (y/N) `, resolve);
9837
+ });
9838
+ rl.close();
9839
+ if (answer.toLowerCase() !== "y") {
9840
+ console.log("Cancelled.");
9841
+ process.exit(0);
9842
+ }
9843
+ }
9844
+
9845
+ // src/commands/schedule.ts
9846
+ var VALID_PATTERNS = ["once", "daily", "weekly", "biweekly", "monthly"];
9847
+ var VALID_MODES = ["quick", "full"];
9848
+ var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
9849
+ function extractTime(scheduledAt) {
9850
+ if (!scheduledAt) return "\u2014";
9851
+ const d = new Date(scheduledAt);
9852
+ return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
9853
+ }
9854
+ function formatPattern(s) {
9855
+ const time = extractTime(s.scheduled_at);
9856
+ switch (s.recurrence_pattern) {
9857
+ case "once":
9858
+ return `Once at ${time}`;
9859
+ case "daily":
9860
+ return `Daily at ${time}`;
9861
+ case "weekly":
9862
+ return `Weekly ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
9863
+ case "biweekly":
9864
+ return `Every 2 weeks ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
9865
+ case "monthly":
9866
+ return `Monthly day ${s.day_of_month ?? "?"} at ${time}`;
9867
+ default:
9868
+ return s.recurrence_pattern;
9869
+ }
9870
+ }
9871
+ function registerScheduleCommand(scanCmd) {
9872
+ const schedule = scanCmd.command("schedule").description("Manage scheduled scans (continuous protection)");
9873
+ schedule.command("list").description("List scheduled scans").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--status <status>", "Filter: active, paused, or all", "all").addHelpText("after", `
9874
+ Examples:
9875
+ $ kinosec scan schedule list
9876
+ $ kinosec scan schedule list --status active --json`).action(async (options) => {
9877
+ try {
9878
+ const opts = schedule.parent.parent.opts();
9879
+ const { token, apiUrl } = resolveToken(opts);
9880
+ const client = new KinoSecClient({ apiUrl, token });
9881
+ const result = await client.schedules.list({
9882
+ limit: parseInt(options.limit, 10),
9883
+ cursor: options.cursor,
9884
+ status: options.status
9885
+ });
9886
+ if (opts.json || opts.output === "json") {
9887
+ printJson(result);
9888
+ return;
9889
+ }
9890
+ if (result.items.length === 0) {
9891
+ console.log("No scheduled scans found.");
9892
+ return;
9893
+ }
9894
+ printTable(
9895
+ ["ID", "Target", "Mode", "Schedule", "Status", "Next Run"],
9896
+ result.items.map((s) => [
9897
+ s.id,
9898
+ truncate(s.domain_or_ip, 25),
9899
+ s.scan_mode,
9900
+ formatPattern(s),
9901
+ s.is_active ? import_picocolors3.default.green("active") : import_picocolors3.default.yellow("paused"),
9902
+ s.next_scheduled_run ? new Date(s.next_scheduled_run).toLocaleString() : "\u2014"
9903
+ ])
9904
+ );
9905
+ if (result.next_cursor) {
9906
+ console.log(import_picocolors3.default.dim(`
9907
+ More results available. Use --cursor ${result.next_cursor}`));
9908
+ }
9909
+ } catch (error) {
9910
+ handleError(error);
9911
+ }
9912
+ });
9913
+ schedule.command("get").description("Get schedule details").argument("<id>", "Schedule ID").addHelpText("after", `
9914
+ Examples:
9915
+ $ kinosec scan schedule get abc123
9916
+ $ kinosec scan schedule get abc123 --json`).action(async (id) => {
9917
+ try {
9918
+ const opts = schedule.parent.parent.opts();
9919
+ const { token, apiUrl } = resolveToken(opts);
9920
+ const client = new KinoSecClient({ apiUrl, token });
9921
+ const s = await client.schedules.get(id);
9922
+ if (opts.json || opts.output === "json") {
9923
+ printJson(s);
9924
+ return;
9925
+ }
9926
+ printTable(
9927
+ ["Field", "Value"],
9928
+ [
9929
+ ["ID", s.id],
9930
+ ["Target", s.domain_or_ip],
9931
+ ["Name", s.name],
9932
+ ["Mode", s.scan_mode],
9933
+ ["Schedule", formatPattern(s)],
9934
+ ["Timezone", s.timezone],
9935
+ ["Status", s.is_active ? "Active" : "Paused"],
9936
+ ["Next Run", s.next_scheduled_run ?? "\u2014"],
9937
+ ["Last Run", s.last_scheduled_run ?? "Never"],
9938
+ ["Created", s.created_at]
9939
+ ]
9940
+ );
9941
+ } catch (error) {
9942
+ handleError(error);
9943
+ }
9944
+ });
9945
+ schedule.command("create").description("Create a scheduled scan").requiredOption("--asset <id>", "Asset ID to scan").requiredOption("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode: quick or full", "full").option("--time <HH:MM>", "Time of day (24h format)", "09:00").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat) for weekly/biweekly").option("--day-of-month <1-31>", "Day of month for monthly").option("--timezone <tz>", "Timezone (e.g. America/New_York)", Intl.DateTimeFormat().resolvedOptions().timeZone).addHelpText("after", `
9946
+ Examples:
9947
+ $ kinosec scan schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
9948
+ $ kinosec scan schedule create --asset abc123 --pattern daily --mode quick --time 14:00
9949
+ $ kinosec scan schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
9950
+ if (!VALID_PATTERNS.includes(options.pattern)) {
9951
+ console.error(`Invalid pattern "${options.pattern}". Must be: ${VALID_PATTERNS.join(", ")}`);
9952
+ process.exit(2);
9953
+ }
9954
+ if (!VALID_MODES.includes(options.mode)) {
9955
+ console.error(`Invalid mode "${options.mode}". Must be: ${VALID_MODES.join(", ")}`);
9956
+ process.exit(2);
9957
+ }
9958
+ if ((options.pattern === "weekly" || options.pattern === "biweekly") && options.dayOfWeek === void 0) {
9959
+ console.error(`--day-of-week is required for ${options.pattern} schedules.`);
9960
+ process.exit(2);
9961
+ }
9962
+ if (options.pattern === "monthly" && options.dayOfMonth === void 0) {
9963
+ console.error("--day-of-month is required for monthly schedules.");
9964
+ process.exit(2);
9965
+ }
9966
+ try {
9967
+ const opts = schedule.parent.parent.opts();
9968
+ const { token, apiUrl } = resolveToken(opts);
9969
+ const client = new KinoSecClient({ apiUrl, token });
9970
+ const params = {
9971
+ asset_id: options.domain_or_ip,
9972
+ name: options.name ?? `${options.pattern} ${options.mode} scan`,
9973
+ scan_mode: options.mode,
9974
+ recurrence_pattern: options.pattern,
9975
+ time_of_day: options.time,
9976
+ timezone: options.timezone
9977
+ };
9978
+ if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
9979
+ if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
9980
+ const s = await client.schedules.create(params);
9981
+ if (opts.json || opts.output === "json") {
9982
+ printJson(s);
9983
+ return;
9984
+ }
9985
+ printSuccess(`Schedule created: ${s.domain_or_ip}`);
9986
+ console.log(` ID: ${s.id}`);
9987
+ console.log(` Schedule: ${formatPattern(s)}`);
9988
+ console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
9989
+ console.log(`
9990
+ Manage: kinosec scan schedule get ${s.id}`);
9991
+ } catch (error) {
9992
+ handleError(error);
9993
+ }
9994
+ });
9995
+ schedule.command("update").description("Update a schedule").argument("<id>", "Schedule ID").option("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode: quick or full").option("--time <HH:MM>", "Time of day (24h format)").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat)").option("--day-of-month <1-31>", "Day of month").option("--timezone <tz>", "Timezone").addHelpText("after", `
9996
+ Examples:
9997
+ $ kinosec scan schedule update abc123 --pattern daily --time 14:00
9998
+ $ kinosec scan schedule update abc123 --mode quick`).action(async (id, options) => {
9999
+ const params = {};
10000
+ if (options.pattern) params.recurrence_pattern = options.pattern;
10001
+ if (options.name) params.name = options.name;
10002
+ if (options.mode) params.scan_mode = options.mode;
10003
+ if (options.time) params.time_of_day = options.time;
10004
+ if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
10005
+ if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
10006
+ if (options.timezone) params.timezone = options.timezone;
10007
+ if (Object.keys(params).length === 0) {
10008
+ console.error("Provide at least one field to update.");
10009
+ process.exit(2);
10010
+ }
10011
+ try {
10012
+ const opts = schedule.parent.parent.opts();
10013
+ const { token, apiUrl } = resolveToken(opts);
10014
+ const client = new KinoSecClient({ apiUrl, token });
10015
+ const s = await client.schedules.update(id, params);
10016
+ if (opts.json || opts.output === "json") {
10017
+ printJson(s);
10018
+ return;
10019
+ }
10020
+ printSuccess(`Schedule updated: ${s.domain_or_ip}`);
10021
+ console.log(` Schedule: ${formatPattern(s)}`);
10022
+ console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
10023
+ } catch (error) {
10024
+ handleError(error);
10025
+ }
10026
+ });
10027
+ schedule.command("delete").description("Delete a schedule").argument("<id>", "Schedule ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
10028
+ Examples:
10029
+ $ kinosec scan schedule delete abc123
10030
+ $ kinosec scan schedule delete abc123 --yes`).action(async (id, options) => {
10031
+ try {
10032
+ await confirmDestructive(`Delete schedule ${id}?`, Boolean(options.yes));
10033
+ const opts = schedule.parent.parent.opts();
10034
+ const { token, apiUrl } = resolveToken(opts);
10035
+ const client = new KinoSecClient({ apiUrl, token });
10036
+ await client.schedules.delete(id);
10037
+ if (opts.json || opts.output === "json") {
10038
+ printJson({ deleted: true, id });
10039
+ return;
10040
+ }
10041
+ printSuccess(`Schedule ${id} deleted.`);
10042
+ } catch (error) {
10043
+ handleError(error);
10044
+ }
10045
+ });
10046
+ schedule.command("pause").description("Pause a scheduled scan").argument("<id>", "Schedule ID").addHelpText("after", `
10047
+ Examples:
10048
+ $ kinosec scan schedule pause abc123`).action(async (id) => {
10049
+ try {
10050
+ const opts = schedule.parent.parent.opts();
10051
+ const { token, apiUrl } = resolveToken(opts);
10052
+ const client = new KinoSecClient({ apiUrl, token });
10053
+ const result = await client.schedules.pause(id);
10054
+ if (opts.json || opts.output === "json") {
10055
+ printJson(result);
10056
+ return;
10057
+ }
10058
+ printSuccess("Schedule paused.");
10059
+ } catch (error) {
10060
+ handleError(error);
10061
+ }
10062
+ });
10063
+ schedule.command("resume").description("Resume a paused schedule").argument("<id>", "Schedule ID").addHelpText("after", `
10064
+ Examples:
10065
+ $ kinosec scan schedule resume abc123`).action(async (id) => {
10066
+ try {
10067
+ const opts = schedule.parent.parent.opts();
10068
+ const { token, apiUrl } = resolveToken(opts);
10069
+ const client = new KinoSecClient({ apiUrl, token });
10070
+ const result = await client.schedules.resume(id);
10071
+ if (opts.json || opts.output === "json") {
10072
+ printJson(result);
10073
+ return;
10074
+ }
10075
+ printSuccess("Schedule resumed.");
10076
+ if (result.next_scheduled_run) {
10077
+ console.log(` Next run: ${new Date(result.next_scheduled_run).toLocaleString()}`);
10078
+ }
10079
+ } catch (error) {
10080
+ handleError(error);
10081
+ }
10082
+ });
10083
+ }
10084
+
10085
+ // src/commands/scan.ts
9717
10086
  function registerScanCommand(program3) {
9718
10087
  const scan = program3.command("scan").description("Manage security scans");
10088
+ registerScheduleCommand(scan);
9719
10089
  scan.command("list").description("List scans").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
9720
10090
  Examples:
9721
- $ kns scan list
9722
- $ kns scan list --limit 5 --json`).action(async (options) => {
10091
+ $ kinosec scan list
10092
+ $ kinosec scan list --limit 5 --json`).action(async (options) => {
9723
10093
  try {
9724
10094
  const opts = program3.opts();
9725
10095
  const { token, apiUrl } = resolveToken(opts);
@@ -9747,7 +10117,7 @@ Examples:
9747
10117
  ])
9748
10118
  );
9749
10119
  if (result.next_cursor) {
9750
- console.log(import_picocolors3.default.dim(`
10120
+ console.log(import_picocolors4.default.dim(`
9751
10121
  More results available. Use --cursor ${result.next_cursor}`));
9752
10122
  }
9753
10123
  } catch (error) {
@@ -9756,8 +10126,8 @@ More results available. Use --cursor ${result.next_cursor}`));
9756
10126
  });
9757
10127
  scan.command("get").description("Get scan details").argument("<id>", "Scan profile ID").addHelpText("after", `
9758
10128
  Examples:
9759
- $ kns scan get abc123
9760
- $ kns scan get abc123 --json`).action(async (id) => {
10129
+ $ kinosec scan get abc123
10130
+ $ kinosec scan get abc123 --json`).action(async (id) => {
9761
10131
  try {
9762
10132
  const opts = program3.opts();
9763
10133
  const { token, apiUrl } = resolveToken(opts);
@@ -9786,8 +10156,8 @@ Examples:
9786
10156
  });
9787
10157
  scan.command("create").description("Start a new scan").option("--asset <id>", "Asset ID to scan").option("--profile <id>", "Scan profile ID (alternative to --asset)").option("--mode <mode>", "Scan mode: quick or full", "quick").addHelpText("after", `
9788
10158
  Examples:
9789
- $ kns scan create --asset abc123 --mode quick
9790
- $ kns scan create --profile def456`).action(async (options) => {
10159
+ $ kinosec scan create --asset abc123 --mode quick
10160
+ $ kinosec scan create --profile def456`).action(async (options) => {
9791
10161
  if (!options.asset && !options.profile) {
9792
10162
  console.error("Provide --asset <id> or --profile <id>.");
9793
10163
  process.exit(2);
@@ -9797,7 +10167,7 @@ Examples:
9797
10167
  if (!uuidRe.test(idValue)) {
9798
10168
  console.error(`Invalid ID format: "${idValue}"`);
9799
10169
  console.error("IDs are UUIDs (e.g. 550e8400-e29b-41d4-a716-446655440000).");
9800
- console.error("Run kns asset list to find your asset ID.");
10170
+ console.error("Run kinosec asset list to find your asset ID.");
9801
10171
  process.exit(2);
9802
10172
  }
9803
10173
  try {
@@ -9815,14 +10185,14 @@ Examples:
9815
10185
  console.log(` Mode: ${result.scan_mode}`);
9816
10186
  console.log(` Status: ${result.scan_status}`);
9817
10187
  console.log(`
9818
- Watch progress: kns scan watch ${result.scan_profile_id}`);
10188
+ Watch progress: kinosec scan watch ${result.scan_profile_id}`);
9819
10189
  } catch (error) {
9820
10190
  handleError(error);
9821
10191
  }
9822
10192
  });
9823
10193
  scan.command("cancel").description("Cancel a running scan").argument("<id>", "Scan profile ID").addHelpText("after", `
9824
10194
  Examples:
9825
- $ kns scan cancel abc123`).action(async (id) => {
10195
+ $ kinosec scan cancel abc123`).action(async (id) => {
9826
10196
  try {
9827
10197
  const opts = program3.opts();
9828
10198
  const { token, apiUrl } = resolveToken(opts);
@@ -9839,9 +10209,9 @@ Examples:
9839
10209
  });
9840
10210
  scan.command("watch").description("Watch scan progress in real-time").argument("<id>", "Scan profile ID").option("--timeout <seconds>", "Max time to watch", "1800").option("--interval <seconds>", "Poll interval", "5").addHelpText("after", `
9841
10211
  Examples:
9842
- $ kns scan watch abc123
9843
- $ kns scan watch abc123 --timeout 600
9844
- $ kns scan watch abc123 --json`).action(async (id, options) => {
10212
+ $ kinosec scan watch abc123
10213
+ $ kinosec scan watch abc123 --timeout 600
10214
+ $ kinosec scan watch abc123 --json`).action(async (id, options) => {
9845
10215
  try {
9846
10216
  const opts = program3.opts();
9847
10217
  const { token, apiUrl } = resolveToken(opts);
@@ -9871,7 +10241,7 @@ Examples:
9871
10241
  process.stdout.write(JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n");
9872
10242
  } else if (spinner) {
9873
10243
  if (activity && activity !== lastActivity && lastActivity) {
9874
- spinner.stopAndPersist({ symbol: import_picocolors3.default.green("\u2713"), text: lastActivity });
10244
+ spinner.stopAndPersist({ symbol: import_picocolors4.default.green("\u2713"), text: lastActivity });
9875
10245
  spinner = ora().start();
9876
10246
  }
9877
10247
  lastActivity = activity || lastActivity;
@@ -9885,24 +10255,24 @@ Examples:
9885
10255
  break;
9886
10256
  }
9887
10257
  if (scanDetail.scan_status.includes("Failed")) {
9888
- console.log(import_picocolors3.default.red(`
10258
+ console.log(import_picocolors4.default.red(`
9889
10259
  Scan failed: ${scanDetail.failure_reason || "unknown reason"}`));
9890
10260
  process.exit(1);
9891
10261
  }
9892
10262
  if (scanDetail.scan_status.includes("Stopped")) {
9893
- console.log(import_picocolors3.default.yellow("\nScan was cancelled."));
10263
+ console.log(import_picocolors4.default.yellow("\nScan was cancelled."));
9894
10264
  break;
9895
10265
  }
9896
- console.log(import_picocolors3.default.green(`
10266
+ console.log(import_picocolors4.default.green(`
9897
10267
  Scan completed: ${scanDetail.domain_or_ip}`));
9898
- console.log(` View findings: kns finding list --scan ${id}`);
10268
+ console.log(` View findings: kinosec finding list --scan ${id}`);
9899
10269
  break;
9900
10270
  }
9901
10271
  if (Date.now() - startTime > timeoutMs) {
9902
10272
  cleanup();
9903
10273
  console.error(`
9904
10274
  Timeout: scan still running after ${options.timeout}s.`);
9905
- console.error(`Check status: kns scan get ${id}`);
10275
+ console.error(`Check status: kinosec scan get ${id}`);
9906
10276
  process.exit(3);
9907
10277
  }
9908
10278
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
@@ -9914,41 +10284,25 @@ Timeout: scan still running after ${options.timeout}s.`);
9914
10284
  }
9915
10285
 
9916
10286
  // src/commands/asset.ts
9917
- var import_picocolors4 = __toESM(require_picocolors(), 1);
9918
-
9919
- // src/lib/confirm.ts
9920
- import { createInterface as createInterface2 } from "readline";
9921
- async function confirmDestructive(message, yes) {
9922
- if (yes) return;
9923
- if (!isTTY()) {
9924
- console.error("Use --yes to confirm destructive operations in non-interactive mode.");
9925
- process.exit(2);
9926
- }
9927
- const rl = createInterface2({ input: process.stdin, output: process.stderr });
9928
- const answer = await new Promise((resolve) => {
9929
- rl.question(`${message} (y/N) `, resolve);
9930
- });
9931
- rl.close();
9932
- if (answer.toLowerCase() !== "y") {
9933
- console.log("Cancelled.");
9934
- process.exit(0);
9935
- }
9936
- }
9937
-
9938
- // src/commands/asset.ts
10287
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
9939
10288
  function registerAssetCommand(program3) {
9940
10289
  const asset = program3.command("asset").description("Manage assets");
9941
- asset.command("list").description("List assets").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10290
+ asset.command("list").description("List assets").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--search <term>", "Filter by asset value (substring match)").option("--status <status>", "Filter by status (verified, unverified)").option("--type <type>", "Filter by type (domain, subdomain, ip)").addHelpText("after", `
9942
10291
  Examples:
9943
- $ kns asset list
9944
- $ kns asset list --limit 50 --json`).action(async (options) => {
10292
+ $ kinosec asset list
10293
+ $ kinosec asset list --search example.com
10294
+ $ kinosec asset list --type subdomain --status verified
10295
+ $ kinosec asset list --limit 50 --json`).action(async (options) => {
9945
10296
  try {
9946
10297
  const opts = program3.opts();
9947
10298
  const { token, apiUrl } = resolveToken(opts);
9948
10299
  const client = new KinoSecClient({ apiUrl, token });
9949
10300
  const result = await client.assets.list({
9950
10301
  limit: parseInt(options.limit, 10),
9951
- cursor: options.cursor
10302
+ cursor: options.cursor,
10303
+ search: options.search,
10304
+ status: options.status,
10305
+ type: options.type
9952
10306
  });
9953
10307
  if (opts.json || opts.output === "json") {
9954
10308
  printJson(result);
@@ -9969,7 +10323,7 @@ Examples:
9969
10323
  ])
9970
10324
  );
9971
10325
  if (result.next_cursor) {
9972
- console.log(import_picocolors4.default.dim(`
10326
+ console.log(import_picocolors5.default.dim(`
9973
10327
  More results available. Use --cursor ${result.next_cursor}`));
9974
10328
  }
9975
10329
  } catch (error) {
@@ -9978,8 +10332,8 @@ More results available. Use --cursor ${result.next_cursor}`));
9978
10332
  });
9979
10333
  asset.command("get").description("Get asset details").argument("<id>", "Asset ID").addHelpText("after", `
9980
10334
  Examples:
9981
- $ kns asset get abc123
9982
- $ kns asset get abc123 --json`).action(async (id) => {
10335
+ $ kinosec asset get abc123
10336
+ $ kinosec asset get abc123 --json`).action(async (id) => {
9983
10337
  try {
9984
10338
  const opts = program3.opts();
9985
10339
  const { token, apiUrl } = resolveToken(opts);
@@ -10007,8 +10361,8 @@ Examples:
10007
10361
  });
10008
10362
  asset.command("create").description("Create a new asset").requiredOption("--type <type>", "Asset type (domain, subdomain, ip)").requiredOption("--value <value>", "Asset value (e.g. example.com)").requiredOption("--verify-method <method>", "Verification method: dns or file").option("--parent <domain>", "Parent domain (for subdomains)").addHelpText("after", `
10009
10363
  Examples:
10010
- $ kns asset create --type domain --value example.com --verify-method dns
10011
- $ kns asset create --type subdomain --value api.example.com --verify-method file --parent example.com`).action(async (options) => {
10364
+ $ kinosec asset create --type domain --value example.com --verify-method dns
10365
+ $ kinosec asset create --type subdomain --value api.example.com --verify-method file --parent example.com`).action(async (options) => {
10012
10366
  try {
10013
10367
  const opts = program3.opts();
10014
10368
  const { token, apiUrl } = resolveToken(opts);
@@ -10028,15 +10382,15 @@ Examples:
10028
10382
  console.log(` Type: ${result.asset_type}`);
10029
10383
  console.log(` Status: ${result.verification_status}`);
10030
10384
  console.log(`
10031
- Verify: kns asset verify ${result.id}`);
10385
+ Verify: kinosec asset verify ${result.id}`);
10032
10386
  } catch (error) {
10033
10387
  handleError(error);
10034
10388
  }
10035
10389
  });
10036
10390
  asset.command("delete").description("Delete an asset").argument("<id>", "Asset ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
10037
10391
  Examples:
10038
- $ kns asset delete abc123
10039
- $ kns asset delete abc123 --yes`).action(async (id, options) => {
10392
+ $ kinosec asset delete abc123
10393
+ $ kinosec asset delete abc123 --yes`).action(async (id, options) => {
10040
10394
  try {
10041
10395
  await confirmDestructive(`Delete asset ${id}?`, Boolean(options.yes));
10042
10396
  const opts = program3.opts();
@@ -10054,8 +10408,8 @@ Examples:
10054
10408
  });
10055
10409
  asset.command("verify").description("Trigger asset verification").argument("<id>", "Asset ID").addHelpText("after", `
10056
10410
  Examples:
10057
- $ kns asset verify abc123
10058
- $ kns asset verify abc123 --json`).action(async (id) => {
10411
+ $ kinosec asset verify abc123
10412
+ $ kinosec asset verify abc123 --json`).action(async (id) => {
10059
10413
  try {
10060
10414
  const opts = program3.opts();
10061
10415
  const { token, apiUrl } = resolveToken(opts);
@@ -10080,20 +10434,20 @@ Examples:
10080
10434
  }
10081
10435
 
10082
10436
  // src/commands/finding.ts
10083
- var import_picocolors5 = __toESM(require_picocolors(), 1);
10437
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
10084
10438
  function severityColor(level) {
10085
10439
  if (!isTTY()) return level;
10086
10440
  switch (level.toLowerCase()) {
10087
10441
  case "critical":
10088
- return import_picocolors5.default.red(import_picocolors5.default.bold(level));
10442
+ return import_picocolors6.default.red(import_picocolors6.default.bold(level));
10089
10443
  case "high":
10090
- return import_picocolors5.default.red(level);
10444
+ return import_picocolors6.default.red(level);
10091
10445
  case "medium":
10092
- return import_picocolors5.default.yellow(level);
10446
+ return import_picocolors6.default.yellow(level);
10093
10447
  case "low":
10094
- return import_picocolors5.default.blue(level);
10448
+ return import_picocolors6.default.blue(level);
10095
10449
  case "info":
10096
- return import_picocolors5.default.dim(level);
10450
+ return import_picocolors6.default.dim(level);
10097
10451
  default:
10098
10452
  return level;
10099
10453
  }
@@ -10103,8 +10457,8 @@ function registerFindingCommand(program3) {
10103
10457
  const finding = program3.command("finding").description("Manage findings");
10104
10458
  finding.command("list").description("List findings").option("--scan <id>", "Filter by scan profile ID").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10105
10459
  Examples:
10106
- $ kns finding list --scan abc123
10107
- $ kns finding list --limit 50 --json`).action(async (options) => {
10460
+ $ kinosec finding list --scan abc123
10461
+ $ kinosec finding list --limit 50 --json`).action(async (options) => {
10108
10462
  try {
10109
10463
  const opts = program3.opts();
10110
10464
  const { token, apiUrl } = resolveToken(opts);
@@ -10133,7 +10487,7 @@ Examples:
10133
10487
  ])
10134
10488
  );
10135
10489
  if (result.next_cursor) {
10136
- console.log(import_picocolors5.default.dim(`
10490
+ console.log(import_picocolors6.default.dim(`
10137
10491
  More results available. Use --cursor ${result.next_cursor}`));
10138
10492
  }
10139
10493
  } catch (error) {
@@ -10142,8 +10496,8 @@ More results available. Use --cursor ${result.next_cursor}`));
10142
10496
  });
10143
10497
  finding.command("get").description("Get finding details").argument("<id>", "Finding ID").addHelpText("after", `
10144
10498
  Examples:
10145
- $ kns finding get abc123
10146
- $ kns finding get abc123 --json`).action(async (id) => {
10499
+ $ kinosec finding get abc123
10500
+ $ kinosec finding get abc123 --json`).action(async (id) => {
10147
10501
  try {
10148
10502
  const opts = program3.opts();
10149
10503
  const { token, apiUrl } = resolveToken(opts);
@@ -10173,8 +10527,8 @@ Examples:
10173
10527
  });
10174
10528
  finding.command("update").description("Update finding status").argument("<id>", "Finding ID").requiredOption("--status <status>", "New status: open, fixed, false_positive, investigating, wont_fix").addHelpText("after", `
10175
10529
  Examples:
10176
- $ kns finding update abc123 --status false_positive
10177
- $ kns finding update abc123 --status fixed`).action(async (id, options) => {
10530
+ $ kinosec finding update abc123 --status false_positive
10531
+ $ kinosec finding update abc123 --status fixed`).action(async (id, options) => {
10178
10532
  if (!VALID_STATUSES.includes(options.status)) {
10179
10533
  console.error(`Invalid status "${options.status}". Valid: ${VALID_STATUSES.join(", ")}`);
10180
10534
  process.exit(2);
@@ -10197,8 +10551,8 @@ Examples:
10197
10551
  });
10198
10552
  finding.command("retest").description("Trigger a retest for a finding").argument("<id>", "Finding ID").addHelpText("after", `
10199
10553
  Examples:
10200
- $ kns finding retest abc123
10201
- $ kns finding retest abc123 --json`).action(async (id) => {
10554
+ $ kinosec finding retest abc123
10555
+ $ kinosec finding retest abc123 --json`).action(async (id) => {
10202
10556
  try {
10203
10557
  const opts = program3.opts();
10204
10558
  const { token, apiUrl } = resolveToken(opts);
@@ -10218,7 +10572,7 @@ Examples:
10218
10572
  }
10219
10573
 
10220
10574
  // src/commands/report.ts
10221
- var import_picocolors6 = __toESM(require_picocolors(), 1);
10575
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
10222
10576
  function renderMarkdown(data) {
10223
10577
  const { scan, findings } = data;
10224
10578
  const lines = [];
@@ -10266,8 +10620,8 @@ function registerReportCommand(program3) {
10266
10620
  const report = program3.command("report").description("Manage reports");
10267
10621
  report.command("list").description("List reports").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10268
10622
  Examples:
10269
- $ kns report list
10270
- $ kns report list --limit 10 --json`).action(async (options) => {
10623
+ $ kinosec report list
10624
+ $ kinosec report list --limit 10 --json`).action(async (options) => {
10271
10625
  try {
10272
10626
  const opts = program3.opts();
10273
10627
  const { token, apiUrl } = resolveToken(opts);
@@ -10297,7 +10651,7 @@ Examples:
10297
10651
  ])
10298
10652
  );
10299
10653
  if (result.next_cursor) {
10300
- console.log(import_picocolors6.default.dim(`
10654
+ console.log(import_picocolors7.default.dim(`
10301
10655
  More results available. Use --cursor ${result.next_cursor}`));
10302
10656
  }
10303
10657
  } catch (error) {
@@ -10306,8 +10660,8 @@ More results available. Use --cursor ${result.next_cursor}`));
10306
10660
  });
10307
10661
  report.command("get").description("Get report details").argument("<id>", "Report ID").addHelpText("after", `
10308
10662
  Examples:
10309
- $ kns report get abc123
10310
- $ kns report get abc123 --json`).action(async (id) => {
10663
+ $ kinosec report get abc123
10664
+ $ kinosec report get abc123 --json`).action(async (id) => {
10311
10665
  try {
10312
10666
  const opts = program3.opts();
10313
10667
  const { token, apiUrl } = resolveToken(opts);
@@ -10341,8 +10695,8 @@ Examples:
10341
10695
  });
10342
10696
  report.command("export").description("Export a scan report").requiredOption("--scan <id>", "Scan report ID").option("--format <format>", "Export format: json or markdown", "json").addHelpText("after", `
10343
10697
  Examples:
10344
- $ kns report export --scan abc123 --format json > report.json
10345
- $ kns report export --scan abc123 --format markdown > report.md`).action(async (options) => {
10698
+ $ kinosec report export --scan abc123 --format json > report.json
10699
+ $ kinosec report export --scan abc123 --format markdown > report.md`).action(async (options) => {
10346
10700
  const validFormats = ["json", "markdown"];
10347
10701
  if (!validFormats.includes(options.format)) {
10348
10702
  console.error(`Invalid format "${options.format}". Valid: ${validFormats.join(", ")}`);
@@ -10368,13 +10722,13 @@ Examples:
10368
10722
  }
10369
10723
 
10370
10724
  // src/commands/token.ts
10371
- var import_picocolors7 = __toESM(require_picocolors(), 1);
10725
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
10372
10726
  function registerTokenCommand(program3) {
10373
10727
  const token = program3.command("token").description("Manage API tokens");
10374
10728
  token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10375
10729
  Examples:
10376
- $ kns token list
10377
- $ kns token list --json`).action(async (options) => {
10730
+ $ kinosec token list
10731
+ $ kinosec token list --json`).action(async (options) => {
10378
10732
  try {
10379
10733
  const opts = program3.opts();
10380
10734
  const { token: authToken, apiUrl } = resolveToken(opts);
@@ -10403,7 +10757,7 @@ Examples:
10403
10757
  ])
10404
10758
  );
10405
10759
  if (result.next_cursor) {
10406
- console.log(import_picocolors7.default.dim(`
10760
+ console.log(import_picocolors8.default.dim(`
10407
10761
  More results available. Use --cursor ${result.next_cursor}`));
10408
10762
  }
10409
10763
  } catch (error) {
@@ -10412,8 +10766,8 @@ More results available. Use --cursor ${result.next_cursor}`));
10412
10766
  });
10413
10767
  token.command("create").description("Create a new API token").requiredOption("--name <name>", "Token name").requiredOption("--scopes <scopes>", "Comma-separated scopes (e.g. assets:read,scans:create)").option("--expires <days>", "Days until expiry", "90").addHelpText("after", `
10414
10768
  Examples:
10415
- $ kns token create --name "CI" --scopes assets:read,scans:create
10416
- $ kns token create --name "Full" --scopes assets:read,scans:read --expires 30`).action(async (options) => {
10769
+ $ kinosec token create --name "CI" --scopes assets:read,scans:create
10770
+ $ kinosec token create --name "Full" --scopes assets:read,scans:read --expires 30`).action(async (options) => {
10417
10771
  try {
10418
10772
  const opts = program3.opts();
10419
10773
  const { token: authToken, apiUrl } = resolveToken(opts);
@@ -10430,7 +10784,7 @@ Examples:
10430
10784
  }
10431
10785
  printSuccess(`Token created: ${result.name}`);
10432
10786
  console.log("");
10433
- console.log(import_picocolors7.default.bold(" Token: ") + result.token);
10787
+ console.log(import_picocolors8.default.bold(" Token: ") + result.token);
10434
10788
  console.log("");
10435
10789
  printWarning("Save this token \u2014 it will not be shown again.");
10436
10790
  console.log(` ID: ${result.id}`);
@@ -10442,8 +10796,8 @@ Examples:
10442
10796
  });
10443
10797
  token.command("revoke").description("Revoke an API token").argument("<id>", "Token ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
10444
10798
  Examples:
10445
- $ kns token revoke abc123
10446
- $ kns token revoke abc123 --yes`).action(async (id, options) => {
10799
+ $ kinosec token revoke abc123
10800
+ $ kinosec token revoke abc123 --yes`).action(async (id, options) => {
10447
10801
  try {
10448
10802
  await confirmDestructive(`Revoke token ${id}?`, Boolean(options.yes));
10449
10803
  const opts = program3.opts();
@@ -10462,9 +10816,8 @@ Examples:
10462
10816
  }
10463
10817
 
10464
10818
  // src/cli.ts
10465
- var VERSION = "0.1.0";
10466
10819
  var program2 = new Command();
10467
- program2.name("kns").description("KinoSec CLI \u2014 run pentests from your terminal").version(VERSION, "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN and config)").option("--api-url <url>", "API base URL (overrides config)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
10820
+ program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.2.0", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN and config)").option("--api-url <url>", "API base URL (overrides config)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
10468
10821
  registerAuthCommand(program2);
10469
10822
  registerConfigCommand(program2);
10470
10823
  registerMeCommand(program2);
@@ -10473,7 +10826,7 @@ registerAssetCommand(program2);
10473
10826
  registerFindingCommand(program2);
10474
10827
  registerReportCommand(program2);
10475
10828
  registerTokenCommand(program2);
10476
- program2.parseAsync(process.argv).catch(handleError);
10829
+ program2.parseAsync(process.argv).then(() => checkForUpdate("0.2.0")).catch(handleError);
10477
10830
  /*! Bundled license information:
10478
10831
 
10479
10832
  ky/distribution/index.js: