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.
- package/README.md +48 -12
- package/bin/not-manage.js +15 -2
- package/package.json +1 -2
- package/src/agent-input.js +101 -0
- package/src/cli-errors.js +109 -0
- package/src/cli.js +835 -64
- package/src/clio-api.js +108 -6
- package/src/commands-agent-context.js +119 -0
- package/src/commands-auth.js +336 -148
- package/src/commands-doctor.js +178 -0
- package/src/commands-request.js +144 -0
- package/src/compact-output.js +89 -0
- package/src/prompt.js +48 -0
- package/src/redaction.js +62 -25
- package/src/resource-command-runner.js +75 -4
- package/src/resource-handlers.js +15 -6
- package/src/resource-metadata.js +18 -0
- package/src/store.js +1 -1
- package/src/postinstall.js +0 -140
|
@@ -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 =
|
|
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
|
|
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 =
|
|
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,
|
package/src/resource-handlers.js
CHANGED
|
@@ -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
|
|
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 &&
|
|
36
|
-
const printList = createListPrinter(
|
|
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:
|
|
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 &&
|
|
50
|
-
const printItem = createDetailPrinter(
|
|
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,
|
package/src/resource-metadata.js
CHANGED
|
@@ -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
|
}
|
package/src/postinstall.js
DELETED
|
@@ -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
|
-
};
|