not-manage 0.2.1 → 0.2.3

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.
@@ -3,7 +3,10 @@ const {
3
3
  fetchResourcePage,
4
4
  getValidAccessToken,
5
5
  } = require("./clio-api");
6
+ const { readIdsInput } = require("./agent-input");
6
7
  const { fetchPages } = require("./resource-utils");
8
+ const { maybeCompactPayload } = require("./compact-output");
9
+ const { UsageError } = require("./cli-errors");
7
10
  const { maybeRedactData, maybeRedactPayload } = require("./redaction");
8
11
  const { getConfig, getTokenSet } = require("./store");
9
12
 
@@ -44,6 +47,16 @@ async function fetchDefaultItemPayload({ accessToken, apiPath, config, id, query
44
47
  return fetchResourceById(config, accessToken, apiPath, id, query);
45
48
  }
46
49
 
50
+ function buildGetJsonEnvelope(items, requestedCount) {
51
+ return {
52
+ data: items,
53
+ meta: {
54
+ requested_count: requestedCount,
55
+ returned_count: items.length,
56
+ },
57
+ };
58
+ }
59
+
47
60
  function createListCommand(commandConfig) {
48
61
  const {
49
62
  apiPath,
@@ -69,7 +82,10 @@ function createListCommand(commandConfig) {
69
82
  options,
70
83
  query,
71
84
  });
72
- const data = maybeRedactData(result.data, options, redactionResourceType);
85
+ const data = maybeCompactPayload(
86
+ maybeRedactData(result.data, options, redactionResourceType),
87
+ options
88
+ );
73
89
 
74
90
  if (options.json) {
75
91
  const jsonMeta = buildJsonMeta ? buildJsonMeta({ data, options, result }) : {};
@@ -105,11 +121,62 @@ function createGetCommand(commandConfig) {
105
121
  } = commandConfig;
106
122
 
107
123
  return async function getCommand(options = {}) {
108
- if (!options.id) {
109
- throw new Error(usage);
124
+ if (!options.id && !options.idsFile) {
125
+ throw new UsageError(usage, {
126
+ code: "missing_resource_id",
127
+ hint: usage.replace(/^Usage:\s*/, ""),
128
+ details: { missing: ["id"] },
129
+ });
110
130
  }
111
131
 
112
132
  const { accessToken, config } = await getAuthContext();
133
+
134
+ if (options.idsFile) {
135
+ if (options.id) {
136
+ throw new UsageError("Pass either an ID argument or `--ids-file`, not both.", {
137
+ code: "conflicting_id_inputs",
138
+ hint: "Use `not-manage <resource> get <id>` or `not-manage <resource> get --ids-file ids.txt --json`.",
139
+ });
140
+ }
141
+ if (!options.json) {
142
+ throw new UsageError("Bulk `get` from `--ids-file` requires `--json`.", {
143
+ code: "json_required_for_bulk_get",
144
+ hint: "Re-run with `--json` or `--agent`.",
145
+ });
146
+ }
147
+
148
+ const ids = await readIdsInput(options.idsFile);
149
+ if (ids.length === 0) {
150
+ throw new UsageError("Bulk `get` did not receive any IDs.", {
151
+ code: "missing_resource_id",
152
+ hint: "Provide one ID per line, a JSON array of IDs, or objects with an `id` field.",
153
+ details: { missing: ["id"] },
154
+ });
155
+ }
156
+
157
+ const items = [];
158
+ for (const id of ids) {
159
+ const payload = await fetchItemPayload({
160
+ accessToken,
161
+ apiPath,
162
+ config,
163
+ id,
164
+ options,
165
+ query: {
166
+ fields: options.fields || defaultFields,
167
+ },
168
+ });
169
+ const redactedPayload = maybeCompactPayload(
170
+ maybeRedactPayload(payload, options, redactionResourceType),
171
+ options
172
+ );
173
+ items.push(redactedPayload?.data ?? redactedPayload);
174
+ }
175
+
176
+ console.log(JSON.stringify(buildGetJsonEnvelope(items, ids.length), null, options.compact ? 0 : 2));
177
+ return;
178
+ }
179
+
113
180
  const payload = await fetchItemPayload({
114
181
  accessToken,
115
182
  apiPath,
@@ -120,7 +187,10 @@ function createGetCommand(commandConfig) {
120
187
  fields: options.fields || defaultFields,
121
188
  },
122
189
  });
123
- const redactedPayload = maybeRedactPayload(payload, options, redactionResourceType);
190
+ const redactedPayload = maybeCompactPayload(
191
+ maybeRedactPayload(payload, options, redactionResourceType),
192
+ options
193
+ );
124
194
 
125
195
  if (options.json) {
126
196
  console.log(JSON.stringify(redactedPayload, null, 2));
@@ -132,6 +202,7 @@ function createGetCommand(commandConfig) {
132
202
  }
133
203
 
134
204
  module.exports = {
205
+ buildGetJsonEnvelope,
135
206
  buildListJsonEnvelope,
136
207
  buildSummaryMessage,
137
208
  createGetCommand,
@@ -6,6 +6,7 @@ const {
6
6
  } = require("./resource-display");
7
7
  const { buildListQueryFromResource } = require("./resource-query-builder");
8
8
  const { createGetCommand, createListCommand } = require("./resource-command-runner");
9
+ const { getResourceMetadata } = require("./resource-metadata");
9
10
 
10
11
  const RESOURCE_HANDLERS = {
11
12
  activities: {
@@ -21,7 +22,7 @@ const RESOURCE_HANDLERS = {
21
22
  const GENERIC_HANDLER_CACHE = new Map();
22
23
 
23
24
  function createGenericHandlers(resourceMetadata) {
24
- if (!resourceMetadata || !resourceMetadata.display) {
25
+ if (!resourceMetadata) {
25
26
  return null;
26
27
  }
27
28
 
@@ -30,15 +31,23 @@ function createGenericHandlers(resourceMetadata) {
30
31
  return cached;
31
32
  }
32
33
 
34
+ const displayMetadata =
35
+ resourceMetadata.display
36
+ ? resourceMetadata
37
+ : getResourceMetadata(resourceMetadata.handlerKey);
38
+ if (!displayMetadata?.display) {
39
+ return null;
40
+ }
41
+
33
42
  const handlers = {};
34
43
 
35
- if (resourceMetadata.capabilities.list.enabled && resourceMetadata.display.list) {
36
- const printList = createListPrinter(resourceMetadata.display.list);
44
+ if (resourceMetadata.capabilities.list.enabled && displayMetadata.display.list) {
45
+ const printList = createListPrinter(displayMetadata.display.list);
37
46
  handlers.list = createListCommand({
38
47
  apiPath: resourceMetadata.apiPath,
39
48
  buildQuery: (options) =>
40
49
  buildListQueryFromResource(resourceMetadata, options, resourceMetadata.listQuery),
41
- formatRow: resourceMetadata.display.list.formatRow,
50
+ formatRow: displayMetadata.display.list.formatRow,
42
51
  pluralLabel: resourceMetadata.summaryLabels.plural,
43
52
  printList,
44
53
  redactionResourceType: resourceMetadata.redaction.resourceType,
@@ -46,8 +55,8 @@ function createGenericHandlers(resourceMetadata) {
46
55
  });
47
56
  }
48
57
 
49
- if (resourceMetadata.capabilities.get.enabled && resourceMetadata.display.get) {
50
- const printItem = createDetailPrinter(resourceMetadata.display.get);
58
+ if (resourceMetadata.capabilities.get.enabled && displayMetadata.display.get) {
59
+ const printItem = createDetailPrinter(displayMetadata.display.get);
51
60
  handlers.get = createGetCommand({
52
61
  apiPath: resourceMetadata.apiPath,
53
62
  defaultFields: resourceMetadata.defaultFields.get,
@@ -2217,9 +2217,27 @@ function listRequiredOptionFlags(resourceMetadata, sub) {
2217
2217
  });
2218
2218
  }
2219
2219
 
2220
+ function findMissingRequiredOptions(resourceMetadata, sub, options = {}) {
2221
+ if (!resourceMetadata) {
2222
+ return [];
2223
+ }
2224
+
2225
+ const requiredOptions = resourceMetadata.capabilities?.[sub]?.requiredOptions || [];
2226
+ return requiredOptions
2227
+ .filter((propertyName) => {
2228
+ const value = options[propertyName];
2229
+ return value === undefined || value === null || value === "";
2230
+ })
2231
+ .map((propertyName) => {
2232
+ const optionName = resourceMetadata.optionSchema?.[sub]?.[propertyName]?.option;
2233
+ return optionName ? `--${optionName}` : `--${propertyName}`;
2234
+ });
2235
+ }
2236
+
2220
2237
  module.exports = {
2221
2238
  RESOURCE_METADATA,
2222
2239
  RESOURCE_ORDER,
2240
+ findMissingRequiredOptions,
2223
2241
  getResourceMetadata,
2224
2242
  listResourceMetadata,
2225
2243
  listRequiredOptionFlags,
package/src/store.js CHANGED
@@ -153,7 +153,7 @@ async function getStoredTokenSet() {
153
153
  };
154
154
  } catch (_error) {
155
155
  throw new Error(
156
- "Stored token data is invalid. Run `not-manage auth revoke` then `not-manage auth login`."
156
+ "Stored token data is invalid. Run `not-manage auth revoke --yes` then `not-manage auth login`."
157
157
  );
158
158
  }
159
159
  }
@@ -1,140 +0,0 @@
1
- const { setupWizard } = require("./commands-auth");
2
- const { ask, withPrompt } = require("./prompt");
3
- const { findConfig } = require("./store");
4
-
5
- const SKIP_POSTINSTALL_ENV_VARS = [
6
- "NOT_MANAGE_SKIP_POSTINSTALL_SETUP",
7
- "CLIO_MANAGE_SKIP_POSTINSTALL_SETUP",
8
- ];
9
-
10
- function printConfidentialityNotice(log = console.log) {
11
- log("Confidentiality notice:");
12
- log(" not-manage can display client-identifying, confidential, or privileged matter data.");
13
- log(" `--redacted` is best-effort only and may miss identifiers in labels, custom fields, or free text.");
14
- log(" Review all output before sharing it with AI tools, tickets, chats, or other third parties.");
15
- log(" Use only with workflows and vendors your firm has approved.");
16
- }
17
-
18
- function shouldShowPostinstallNotice(options = {}) {
19
- const env = options.env || process.env;
20
-
21
- if (SKIP_POSTINSTALL_ENV_VARS.some((name) => env[name] === "1")) {
22
- return false;
23
- }
24
-
25
- if (env.CI) {
26
- return false;
27
- }
28
-
29
- if (env.npm_config_global !== "true") {
30
- return false;
31
- }
32
-
33
- return true;
34
- }
35
-
36
- function shouldRunPostinstallOnboarding(options = {}) {
37
- const stdin = options.stdin || process.stdin;
38
- const stdout = options.stdout || process.stdout;
39
-
40
- if (!shouldShowPostinstallNotice(options)) {
41
- return false;
42
- }
43
-
44
- if (!stdin.isTTY || !stdout.isTTY) {
45
- return false;
46
- }
47
-
48
- return true;
49
- }
50
-
51
- function printPostinstallIntro(log = console.log) {
52
- log("");
53
- log("+===========================================+");
54
- log("| NOT MANAGE IS INSTALLED |");
55
- log("+===========================================+");
56
- log("| Start first-time setup from npm? |");
57
- log("+===========================================+");
58
- log("");
59
- log("This prompt only appears on fresh interactive global installs.");
60
- log("If you skip it now, run `not-manage setup` whenever you are ready.");
61
- log("");
62
- printConfidentialityNotice(log);
63
- log("");
64
- }
65
-
66
- function printPostinstallInstalledNotice(log = console.log) {
67
- log("");
68
- log("not-manage is installed.");
69
- printConfidentialityNotice(log);
70
- log("Run `not-manage setup` whenever you are ready.");
71
- }
72
-
73
- function printPostinstallWelcomeBack(log = console.log) {
74
- log("");
75
- log("Welcome back. Clio is already configured on this machine.");
76
- log("Run `not-manage auth status` to verify the current connection, or `not-manage setup` to reconfigure.");
77
- }
78
-
79
- async function maybeRunPostinstallOnboarding(options = {}) {
80
- const log = options.log || console.log;
81
- const findConfigFn = options.findConfig || findConfig;
82
- const setupWizardFn = options.setupWizard || setupWizard;
83
- const withPromptFn = options.withPrompt || withPrompt;
84
- const askFn = options.ask || ask;
85
-
86
- if (!shouldShowPostinstallNotice(options)) {
87
- return false;
88
- }
89
-
90
- const config = await findConfigFn();
91
- if (config) {
92
- printPostinstallWelcomeBack(log);
93
- return false;
94
- }
95
-
96
- if (!shouldRunPostinstallOnboarding(options)) {
97
- printPostinstallInstalledNotice(log);
98
- return false;
99
- }
100
-
101
- printPostinstallIntro(log);
102
-
103
- const answer = String(
104
- await withPromptFn((rl) => askFn(rl, "Start guided Clio setup now", "yes"))
105
- )
106
- .trim()
107
- .toLowerCase();
108
-
109
- if (["n", "no", "skip"].includes(answer)) {
110
- log("Skipping setup for now. Run `not-manage setup` when you are ready.");
111
- return false;
112
- }
113
-
114
- await setupWizardFn();
115
- return true;
116
- }
117
-
118
- async function main(options = {}) {
119
- const log = options.log || console.log;
120
- const errorLog = options.errorLog || console.error;
121
-
122
- try {
123
- await maybeRunPostinstallOnboarding(options);
124
- } catch (_error) {
125
- errorLog("Post-install setup was skipped.");
126
- log("Run `not-manage setup` when you are ready.");
127
- }
128
- }
129
-
130
- if (require.main === module) {
131
- main();
132
- }
133
-
134
- module.exports = {
135
- main,
136
- maybeRunPostinstallOnboarding,
137
- printConfidentialityNotice,
138
- shouldShowPostinstallNotice,
139
- shouldRunPostinstallOnboarding,
140
- };