@index365/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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.mjs +82 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@index365/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "index365 CLI - run AI-readiness and marketing-signal audits and read findings from your terminal, CI, or agents. Wraps the public /api/v1.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  writeConfigFile,
13
13
  } from "./config.mjs";
14
14
 
15
- export const CLI_VERSION = "0.1.0";
15
+ export const CLI_VERSION = "0.1.1";
16
16
 
17
17
  const HELP = `index365 - website audits from your terminal, CI, or agents
18
18
 
@@ -24,6 +24,8 @@ COMMANDS
24
24
  logout Remove the saved API key
25
25
  doctor Verify auth, scopes, API reachability, contract version
26
26
  projects list List projects in your organization
27
+ projects create Create a project (--domain <domain> [--name <name>])
28
+ projects delete Delete a project (<projectId> --confirm <domain>)
27
29
  runs start Start a paid AI-readiness audit (--project <id>, optional --wait)
28
30
  runs get Show one run (<runId>)
29
31
  findings list List findings for a run (--run <id>)
@@ -45,7 +47,7 @@ EXIT CODES
45
47
  0 ok · 1 error · 2 usage · 3 auth · 4 not found · 5 quota/conflict/rate
46
48
 
47
49
  AUTH
48
- Keys are created from an active Agency workspace in the dashboard
50
+ Keys are created from any active paid plan's workspace in the dashboard
49
51
  (Org settings -> API keys) and stored at ~/.config/index365/config.json
50
52
  (0600). INDEX365_API_KEY overrides the file.
51
53
  `;
@@ -185,7 +187,13 @@ async function cmdDoctor(argv, io, env) {
185
187
 
186
188
  async function cmdProjects(argv, io, env) {
187
189
  const [sub, ...rest] = argv;
188
- if (sub !== "list") throw new CliError("Usage: index365 projects list", EXIT.USAGE);
190
+ if (sub === "list") return cmdProjectsList(rest, io, env);
191
+ if (sub === "create") return cmdProjectsCreate(rest, io, env);
192
+ if (sub === "delete") return cmdProjectsDelete(rest, io, env);
193
+ throw new CliError("Usage: index365 projects <list|create|delete>", EXIT.USAGE);
194
+ }
195
+
196
+ async function cmdProjectsList(rest, io, env) {
189
197
  const { values } = parse(rest, {
190
198
  limit: { type: "string" },
191
199
  cursor: { type: "string" },
@@ -207,6 +215,77 @@ async function cmdProjects(argv, io, env) {
207
215
  return EXIT.OK;
208
216
  }
209
217
 
218
+ async function cmdProjectsCreate(rest, io, env) {
219
+ const { values } = parse(rest, {
220
+ domain: { type: "string" },
221
+ name: { type: "string" },
222
+ });
223
+ if (!values.domain) {
224
+ throw new CliError(
225
+ "Usage: index365 projects create --domain <domain> [--name <name>]",
226
+ EXIT.USAGE,
227
+ );
228
+ }
229
+ const settings = settingsFor(values, env);
230
+ const project = await apiRequest(settings, "POST", "/api/v1/projects", {
231
+ body: { domain: values.domain, name: values.name },
232
+ fetchImpl: io.fetch,
233
+ });
234
+ if (values.json) {
235
+ printJson(io, project);
236
+ } else {
237
+ const suffix = project.idempotent ? " [already existed]" : "";
238
+ out(
239
+ io,
240
+ `${project.projectId} ${project.domain} (${project.name}, ${project.status})${suffix}`,
241
+ );
242
+ }
243
+ return EXIT.OK;
244
+ }
245
+
246
+ async function cmdProjectsDelete(rest, io, env) {
247
+ const { values, positionals } = parse(rest, { confirm: { type: "string" } });
248
+ const projectId = positionals[0];
249
+ if (!projectId) {
250
+ throw new CliError(
251
+ "Usage: index365 projects delete <projectId> --confirm <domain>",
252
+ EXIT.USAGE,
253
+ );
254
+ }
255
+ const settings = settingsFor(values, env);
256
+ // Read-before-destroy: fetch the project so we confirm the exact domain first.
257
+ const project = await apiRequest(settings, "GET", `/api/v1/projects/${projectId}`, {
258
+ fetchImpl: io.fetch,
259
+ });
260
+ let confirm = values.confirm;
261
+ if (!confirm) {
262
+ if (input.isTTY) {
263
+ out(
264
+ io,
265
+ `About to permanently delete ${project.domain} (${project.projectId}). This cannot be undone.`,
266
+ );
267
+ const rl = createInterface({ input, output });
268
+ try {
269
+ confirm = (await rl.question(`Type the domain (${project.domain}) to confirm: `)).trim();
270
+ } finally {
271
+ rl.close();
272
+ }
273
+ } else {
274
+ throw new CliError(
275
+ `Refusing to delete ${project.domain} without confirmation. Re-run with --confirm ${project.domain}.`,
276
+ EXIT.USAGE,
277
+ );
278
+ }
279
+ }
280
+ const result = await apiRequest(settings, "DELETE", `/api/v1/projects/${projectId}`, {
281
+ query: { confirm },
282
+ fetchImpl: io.fetch,
283
+ });
284
+ if (values.json) printJson(io, result);
285
+ else out(io, `Deleted ${project.domain} (${projectId}).`);
286
+ return EXIT.OK;
287
+ }
288
+
210
289
  const WAIT_POLL_MS = 5_000;
211
290
  const WAIT_TIMEOUT_MS = 15 * 60_000;
212
291
  const TERMINAL_STATUSES = new Set(["completed", "failed", "failed_auto_credit", "refunded"]);