conduyt 1.2.0 → 1.3.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.
Files changed (2) hide show
  1. package/dist/index.js +36 -0
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6,6 +6,17 @@ import { Command } from "commander";
6
6
  import { ConduytClient, buildQuery } from "./client.js";
7
7
  import { saveConfig, resolveConfig, configPath } from "./config.js";
8
8
  import { print, fail } from "./output.js";
9
+ // Exit cleanly when a downstream pipe closes early (e.g. `conduyt deals list |
10
+ // head`). Without this, the stdout write after the reader is gone throws an
11
+ // unhandled EPIPE and prints a Node stack trace — broken-pipe is normal Unix
12
+ // behavior, not an error. Mirror it on stderr too.
13
+ for (const stream of [process.stdout, process.stderr]) {
14
+ stream.on("error", (err) => {
15
+ if (err.code === "EPIPE")
16
+ process.exit(0);
17
+ throw err;
18
+ });
19
+ }
9
20
  // Derive the version from package.json at runtime so `--version` can never
10
21
  // drift from the published package (dist/index.js -> ../package.json). Read at
11
22
  // top level runs on every invocation, so fall back gracefully rather than
@@ -88,6 +99,13 @@ contacts
88
99
  Object.assign(body, jsonArg(opts.json, "--json"));
89
100
  return client.post("/api/v1/contacts", body);
90
101
  }));
102
+ contacts
103
+ .command("delete <id>")
104
+ .description("Delete a contact by id (soft-delete; retained for audit, the contact's deals are unaffected)")
105
+ .action(run(async (client, id) => {
106
+ assertUuid(id, "contact id");
107
+ return client.del(`/api/v1/contacts/${encodeURIComponent(id)}`);
108
+ }));
91
109
  // ---- deals ----
92
110
  const deals = program.command("deals").description("Manage deals");
93
111
  deals
@@ -254,6 +272,13 @@ deals
254
272
  assertFiniteValue(body.value);
255
273
  return client.post("/api/v1/deals", body);
256
274
  }));
275
+ deals
276
+ .command("delete <id>")
277
+ .description("Delete a deal by id (soft-delete; drops out of lists/boards and pipeline totals, retained for audit)")
278
+ .action(run(async (client, id) => {
279
+ assertUuid(id, "deal id");
280
+ return client.del(`/api/v1/deals/${encodeURIComponent(id)}`);
281
+ }));
257
282
  // ---- pipelines ----
258
283
  // Default action preserves the original published `conduyt pipelines` (list)
259
284
  // invocation now that pipelines is a group — without it, commander prints help
@@ -474,6 +499,17 @@ function assertFiniteValue(v) {
474
499
  throw new Error("value must be a finite number.");
475
500
  }
476
501
  }
502
+ // Validate an id is a UUID before it is interpolated into a DELETE path, so a
503
+ // malformed value with slashes can't form a different (destructive) subroute.
504
+ // The path is also URL-encoded at the call site as defence in depth. The regex
505
+ // is inlined (not a module-level const) so it isn't in the temporal dead zone
506
+ // when an action callback runs during program.parse().
507
+ function assertUuid(id, label = "id") {
508
+ const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
509
+ if (!uuidRe.test(id.trim())) {
510
+ throw new Error(`${label} must be a UUID.`);
511
+ }
512
+ }
477
513
  // Reject prototype-polluting keys in untrusted JSON. JSON.parse creates an OWN
478
514
  // "__proto__" property, but Object.assign'ing it onto a {} body invokes the
479
515
  // setter and changes the body's PROTOTYPE — so a validator could read an
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "conduyt",
3
- "version": "1.2.0",
4
- "description": "Command-line interface for Conduyt CRM \u2014 manage contacts, deals, pipelines, and run insight queries from your terminal.",
3
+ "version": "1.3.0",
4
+ "description": "Command-line interface for Conduyt CRM manage contacts, deals, pipelines, and run insight queries from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {