@tolinax/ayoune-cli 2026.3.0 → 2026.4.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/data/contextSlots.js +189 -0
- package/data/modelsAndRights.js +56 -0
- package/data/modules.js +16 -0
- package/lib/api/apiCallHandler.js +6 -2
- package/lib/api/apiClient.js +9 -1
- package/lib/api/auditCallHandler.js +2 -2
- package/lib/api/handleAPIError.js +20 -18
- package/lib/api/login.js +3 -3
- package/lib/api/searchClient.js +119 -0
- package/lib/commands/createAccessCommand.js +126 -0
- package/lib/commands/createActionsCommand.js +40 -9
- package/lib/commands/createAiCommand.js +17 -17
- package/lib/commands/createAliasCommand.js +4 -6
- package/lib/commands/createAuditCommand.js +5 -9
- package/lib/commands/createBatchCommand.js +15 -28
- package/lib/commands/createCompletionsCommand.js +6 -3
- package/lib/commands/createConfigCommand.js +8 -14
- package/lib/commands/createContextCommand.js +163 -0
- package/lib/commands/createCopyCommand.js +4 -7
- package/lib/commands/createCreateCommand.js +4 -7
- package/lib/commands/createDeleteCommand.js +4 -6
- package/lib/commands/createDeployCommand.js +31 -55
- package/lib/commands/createDescribeCommand.js +12 -10
- package/lib/commands/createEditCommand.js +13 -8
- package/lib/commands/createEventsCommand.js +4 -4
- package/lib/commands/createExecCommand.js +65 -35
- package/lib/commands/createExportCommand.js +21 -24
- package/lib/commands/createGetCommand.js +13 -14
- package/lib/commands/createJobsCommand.js +8 -13
- package/lib/commands/createListCommand.js +13 -14
- package/lib/commands/createLoginCommand.js +16 -4
- package/lib/commands/createLogoutCommand.js +2 -2
- package/lib/commands/createModulesCommand.js +16 -19
- package/lib/commands/createMonitorCommand.js +9 -16
- package/lib/commands/createPermissionsCommand.js +10 -18
- package/lib/commands/createProgram.js +47 -21
- package/lib/commands/createSearchCommand.js +219 -69
- package/lib/commands/createSelfHostUpdateCommand.js +166 -0
- package/lib/commands/createServicesCommand.js +5 -8
- package/lib/commands/createSetupCommand.js +305 -0
- package/lib/commands/createStatusCommand.js +147 -0
- package/lib/commands/createStorageCommand.js +2 -3
- package/lib/commands/createStreamCommand.js +4 -4
- package/lib/commands/createSyncCommand.js +5 -8
- package/lib/commands/createTemplateCommand.js +9 -16
- package/lib/commands/createUpdateCommand.js +12 -15
- package/lib/commands/createUsersCommand.js +21 -31
- package/lib/commands/createWebhooksCommand.js +15 -22
- package/lib/commands/createWhoAmICommand.js +8 -6
- package/lib/helpers/cliError.js +24 -0
- package/lib/helpers/config.js +1 -0
- package/lib/helpers/configLoader.js +6 -0
- package/lib/helpers/contextInjector.js +65 -0
- package/lib/helpers/contextResolver.js +70 -0
- package/lib/helpers/contextStore.js +46 -0
- package/lib/helpers/handleResponseFormatOptions.js +59 -10
- package/lib/helpers/logo.js +48 -0
- package/lib/helpers/resolveCollectionArgs.js +36 -0
- package/lib/helpers/sanitizeFields.js +18 -0
- package/lib/helpers/secureStorage.js +72 -0
- package/lib/helpers/tokenPayload.js +21 -0
- package/lib/helpers/updateNotifier.js +49 -0
- package/lib/models/getModuleFromCollection.js +4 -1
- package/lib/operations/handleCopySingleOperation.js +10 -2
- package/lib/operations/handleCreateSingleOperation.js +3 -0
- package/lib/operations/handleDescribeSingleOperation.js +23 -0
- package/lib/operations/handleGetOperation.js +9 -3
- package/lib/operations/handleListOperation.js +14 -10
- package/lib/prompts/promptModule.js +9 -6
- package/package.json +163 -158
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
export const contextSlots = [
|
|
2
|
+
// Tier 1 — Core
|
|
3
|
+
{
|
|
4
|
+
slot: "project",
|
|
5
|
+
collection: "projects",
|
|
6
|
+
module: "pm",
|
|
7
|
+
nameField: "subject",
|
|
8
|
+
injectAsCreateField: {
|
|
9
|
+
tasks: "project",
|
|
10
|
+
sprints: "_project",
|
|
11
|
+
milestones: "_project",
|
|
12
|
+
epics: "_project",
|
|
13
|
+
},
|
|
14
|
+
injectAsFilterField: {
|
|
15
|
+
tasks: "project",
|
|
16
|
+
sprints: "_project",
|
|
17
|
+
milestones: "_project",
|
|
18
|
+
epics: "_project",
|
|
19
|
+
},
|
|
20
|
+
tier: 1,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
slot: "consumer",
|
|
24
|
+
collection: "consumers",
|
|
25
|
+
module: "crm",
|
|
26
|
+
nameField: "name",
|
|
27
|
+
injectAsCreateField: {
|
|
28
|
+
tasks: "_consumerID",
|
|
29
|
+
opportunities: "_consumerID",
|
|
30
|
+
offers: "_consumerID",
|
|
31
|
+
invoices: "_consumerID",
|
|
32
|
+
orders: "_consumerID",
|
|
33
|
+
},
|
|
34
|
+
injectAsFilterField: {
|
|
35
|
+
tasks: "_consumerID",
|
|
36
|
+
opportunities: "_consumerID",
|
|
37
|
+
offers: "_consumerID",
|
|
38
|
+
invoices: "_consumerID",
|
|
39
|
+
orders: "_consumerID",
|
|
40
|
+
},
|
|
41
|
+
tier: 1,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
slot: "sprint",
|
|
45
|
+
collection: "sprints",
|
|
46
|
+
module: "pm",
|
|
47
|
+
nameField: "subject",
|
|
48
|
+
injectAsCreateField: {
|
|
49
|
+
tasks: "sprint",
|
|
50
|
+
},
|
|
51
|
+
injectAsFilterField: {
|
|
52
|
+
tasks: "sprint",
|
|
53
|
+
},
|
|
54
|
+
parent: "project",
|
|
55
|
+
tier: 1,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
slot: "milestone",
|
|
59
|
+
collection: "milestones",
|
|
60
|
+
module: "pm",
|
|
61
|
+
nameField: "subject",
|
|
62
|
+
injectAsCreateField: {
|
|
63
|
+
tasks: "milestone",
|
|
64
|
+
sprints: "_milestone",
|
|
65
|
+
epics: "_milestone",
|
|
66
|
+
},
|
|
67
|
+
injectAsFilterField: {
|
|
68
|
+
tasks: "milestone",
|
|
69
|
+
sprints: "_milestone",
|
|
70
|
+
epics: "_milestone",
|
|
71
|
+
},
|
|
72
|
+
parent: "project",
|
|
73
|
+
tier: 1,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
slot: "epic",
|
|
77
|
+
collection: "epics",
|
|
78
|
+
module: "pm",
|
|
79
|
+
nameField: "subject",
|
|
80
|
+
injectAsCreateField: {
|
|
81
|
+
tasks: "epic",
|
|
82
|
+
sprints: "_epic",
|
|
83
|
+
},
|
|
84
|
+
injectAsFilterField: {
|
|
85
|
+
tasks: "epic",
|
|
86
|
+
sprints: "_epic",
|
|
87
|
+
},
|
|
88
|
+
parent: "project",
|
|
89
|
+
tier: 1,
|
|
90
|
+
},
|
|
91
|
+
// Tier 2 — Domain
|
|
92
|
+
{
|
|
93
|
+
slot: "campaign",
|
|
94
|
+
collection: "campaigns",
|
|
95
|
+
module: "marketing",
|
|
96
|
+
nameField: "name",
|
|
97
|
+
injectAsCreateField: {
|
|
98
|
+
mailinglists: "_campaign",
|
|
99
|
+
},
|
|
100
|
+
injectAsFilterField: {
|
|
101
|
+
mailinglists: "_campaign",
|
|
102
|
+
},
|
|
103
|
+
tier: 2,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
slot: "opportunity",
|
|
107
|
+
collection: "opportunities",
|
|
108
|
+
module: "crm",
|
|
109
|
+
nameField: "name",
|
|
110
|
+
injectAsCreateField: {},
|
|
111
|
+
injectAsFilterField: {},
|
|
112
|
+
tier: 2,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
slot: "team",
|
|
116
|
+
collection: "teams",
|
|
117
|
+
module: "hr",
|
|
118
|
+
nameField: "name",
|
|
119
|
+
injectAsCreateField: {},
|
|
120
|
+
injectAsFilterField: {},
|
|
121
|
+
tier: 2,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
slot: "department",
|
|
125
|
+
collection: "departments",
|
|
126
|
+
module: "hr",
|
|
127
|
+
nameField: "name",
|
|
128
|
+
injectAsCreateField: {
|
|
129
|
+
teams: "_department",
|
|
130
|
+
},
|
|
131
|
+
injectAsFilterField: {
|
|
132
|
+
teams: "_department",
|
|
133
|
+
},
|
|
134
|
+
tier: 2,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
slot: "kanbanboard",
|
|
138
|
+
collection: "kanbanboards",
|
|
139
|
+
module: "pm",
|
|
140
|
+
nameField: "name",
|
|
141
|
+
injectAsCreateField: {},
|
|
142
|
+
injectAsFilterField: {},
|
|
143
|
+
tier: 2,
|
|
144
|
+
},
|
|
145
|
+
// Tier 3 — AI/Auto
|
|
146
|
+
{
|
|
147
|
+
slot: "agent",
|
|
148
|
+
collection: "aiagents",
|
|
149
|
+
module: "ai",
|
|
150
|
+
nameField: "name",
|
|
151
|
+
injectAsCreateField: {
|
|
152
|
+
aiconversations: "_agent",
|
|
153
|
+
},
|
|
154
|
+
injectAsFilterField: {
|
|
155
|
+
aiconversations: "_agent",
|
|
156
|
+
},
|
|
157
|
+
tier: 3,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
slot: "workflow",
|
|
161
|
+
collection: "automationworkflows",
|
|
162
|
+
module: "automation",
|
|
163
|
+
nameField: "name",
|
|
164
|
+
injectAsCreateField: {},
|
|
165
|
+
injectAsFilterField: {},
|
|
166
|
+
tier: 3,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
slot: "repository",
|
|
170
|
+
collection: "repositories",
|
|
171
|
+
module: "devops",
|
|
172
|
+
nameField: "name",
|
|
173
|
+
injectAsCreateField: {},
|
|
174
|
+
injectAsFilterField: {},
|
|
175
|
+
tier: 3,
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
export function getSlot(slotName) {
|
|
179
|
+
return contextSlots.find((s) => s.slot === slotName);
|
|
180
|
+
}
|
|
181
|
+
export function getSlotByCollection(collection) {
|
|
182
|
+
return contextSlots.find((s) => s.collection === collection.toLowerCase());
|
|
183
|
+
}
|
|
184
|
+
export function getChildSlots(parentSlot) {
|
|
185
|
+
return contextSlots.filter((s) => s.parent === parentSlot);
|
|
186
|
+
}
|
|
187
|
+
export function getSlotsByTier(tier) {
|
|
188
|
+
return contextSlots.filter((s) => s.tier === tier);
|
|
189
|
+
}
|
package/data/modelsAndRights.js
CHANGED
|
@@ -3184,6 +3184,62 @@ const modelsAndRights = [
|
|
|
3184
3184
|
module: "crm",
|
|
3185
3185
|
right: "crm.xingcontacts",
|
|
3186
3186
|
},
|
|
3187
|
+
// Config module additions
|
|
3188
|
+
{
|
|
3189
|
+
plural: "Credentials",
|
|
3190
|
+
singular: "Credential",
|
|
3191
|
+
module: "config",
|
|
3192
|
+
right: "config.credentials",
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
plural: "NotificationPolicies",
|
|
3196
|
+
singular: "NotificationPolicy",
|
|
3197
|
+
module: "config",
|
|
3198
|
+
right: "config.notificationpolicies",
|
|
3199
|
+
},
|
|
3200
|
+
{
|
|
3201
|
+
plural: "MessageTemplates",
|
|
3202
|
+
singular: "MessageTemplate",
|
|
3203
|
+
module: "config",
|
|
3204
|
+
right: "config.messagetemplates",
|
|
3205
|
+
},
|
|
3206
|
+
// DevOps module
|
|
3207
|
+
{
|
|
3208
|
+
plural: "K8Pods",
|
|
3209
|
+
singular: "K8Pod",
|
|
3210
|
+
module: "devops",
|
|
3211
|
+
right: "devops.k8pods",
|
|
3212
|
+
},
|
|
3213
|
+
{
|
|
3214
|
+
plural: "BitbucketPipelines",
|
|
3215
|
+
singular: "BitbucketPipeline",
|
|
3216
|
+
module: "devops",
|
|
3217
|
+
right: "devops.bitbucketpipelines",
|
|
3218
|
+
},
|
|
3219
|
+
{
|
|
3220
|
+
plural: "DevOpsAlerts",
|
|
3221
|
+
singular: "DevOpsAlert",
|
|
3222
|
+
module: "devops",
|
|
3223
|
+
right: "devops.devopsalerts",
|
|
3224
|
+
},
|
|
3225
|
+
{
|
|
3226
|
+
plural: "DeploymentActions",
|
|
3227
|
+
singular: "DeploymentAction",
|
|
3228
|
+
module: "devops",
|
|
3229
|
+
right: "devops.deploymentactions",
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
plural: "Repositories",
|
|
3233
|
+
singular: "Repository",
|
|
3234
|
+
module: "devops",
|
|
3235
|
+
right: "devops.repositories",
|
|
3236
|
+
},
|
|
3237
|
+
{
|
|
3238
|
+
plural: "DeploymentPlans",
|
|
3239
|
+
singular: "DeploymentPlan",
|
|
3240
|
+
module: "devops",
|
|
3241
|
+
right: "devops.deploymentplans",
|
|
3242
|
+
},
|
|
3187
3243
|
];
|
|
3188
3244
|
export default modelsAndRights;
|
|
3189
3245
|
export { modelsAndRights };
|
package/data/modules.js
CHANGED
|
@@ -106,6 +106,22 @@ const aYOUneModules = [
|
|
|
106
106
|
module: "audit",
|
|
107
107
|
host: "audit.ayoune.app",
|
|
108
108
|
},
|
|
109
|
+
{
|
|
110
|
+
label: "DevOps",
|
|
111
|
+
module: "devops",
|
|
112
|
+
host: "devops.ayoune.app",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
label: "Sync",
|
|
116
|
+
module: "sync",
|
|
117
|
+
host: "sync.ayoune.app",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
label: "System (Superuser)",
|
|
121
|
+
module: "su",
|
|
122
|
+
host: "api-v1.ayoune.app",
|
|
123
|
+
superuserOnly: true,
|
|
124
|
+
},
|
|
109
125
|
];
|
|
110
126
|
export default aYOUneModules;
|
|
111
127
|
export { aYOUneModules };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { api, getModuleBaseUrl } from "./apiClient.js";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import * as process from "process";
|
|
4
|
-
import {
|
|
4
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
5
5
|
import { handleAPIError } from "./handleAPIError.js";
|
|
6
6
|
import { login } from "./login.js";
|
|
7
7
|
import { spinner } from "../../index.js";
|
|
@@ -16,6 +16,10 @@ function cleanParams(params) {
|
|
|
16
16
|
continue;
|
|
17
17
|
cleaned[key] = value;
|
|
18
18
|
}
|
|
19
|
+
// The API only supports json, yaml, csv — table is formatted client-side from JSON
|
|
20
|
+
if (cleaned.responseFormat === "table") {
|
|
21
|
+
cleaned.responseFormat = "json";
|
|
22
|
+
}
|
|
19
23
|
const version = process.env.AYOUNE_VERSION;
|
|
20
24
|
if (version) {
|
|
21
25
|
cleaned.ref = `ayoune-cli@${version}`;
|
|
@@ -23,7 +27,7 @@ function cleanParams(params) {
|
|
|
23
27
|
return cleaned;
|
|
24
28
|
}
|
|
25
29
|
function getToken() {
|
|
26
|
-
return
|
|
30
|
+
return secureStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
|
|
27
31
|
}
|
|
28
32
|
const makeRequest = (module, url, method, data, params) => api({
|
|
29
33
|
baseURL: getModuleBaseUrl(module),
|
package/lib/api/apiClient.js
CHANGED
|
@@ -21,8 +21,16 @@ const audit = axios.create({
|
|
|
21
21
|
baseURL: config.auditUrl,
|
|
22
22
|
httpsAgent,
|
|
23
23
|
});
|
|
24
|
+
// Modules that don't follow the standard {module}-api.ayoune.app pattern
|
|
25
|
+
const MODULE_HOST_OVERRIDES = {
|
|
26
|
+
su: "https://api-v1.ayoune.app/api/su",
|
|
27
|
+
logs: "https://monitoring-api.ayoune.app",
|
|
28
|
+
general: "https://api-v1.ayoune.app/api/general",
|
|
29
|
+
usersettings: "https://api-v1.ayoune.app/api/usersettings",
|
|
30
|
+
auth: "https://auth.ayoune.app",
|
|
31
|
+
};
|
|
24
32
|
export function getModuleBaseUrl(module) {
|
|
25
|
-
return `https://${module}-api.ayoune.app`;
|
|
33
|
+
return MODULE_HOST_OVERRIDES[module] || `https://${module}-api.ayoune.app`;
|
|
26
34
|
}
|
|
27
35
|
function addDebugInterceptors(instance) {
|
|
28
36
|
instance.interceptors.request.use((req) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { audit } from "./apiClient.js";
|
|
2
2
|
import * as process from "process";
|
|
3
|
-
import {
|
|
3
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
4
4
|
import { handleAPIError } from "./handleAPIError.js";
|
|
5
5
|
export const auditCallHandler = async (url, method = "get", data = null, params = {}) => {
|
|
6
6
|
try {
|
|
@@ -10,7 +10,7 @@ export const auditCallHandler = async (url, method = "get", data = null, params
|
|
|
10
10
|
data,
|
|
11
11
|
params: { ...params, ref: `ayoune-cli@${process.env.AYOUNE_VERSION}` },
|
|
12
12
|
headers: {
|
|
13
|
-
Authorization: `Bearer ${
|
|
13
|
+
Authorization: `Bearer ${secureStorage.getItem("token")}`,
|
|
14
14
|
},
|
|
15
15
|
});
|
|
16
16
|
return response.data;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { spinner } from "../../index.js";
|
|
2
2
|
import { EXIT_GENERAL_ERROR, EXIT_AUTH_REQUIRED, EXIT_PERMISSION_DENIED, } from "../exitCodes.js";
|
|
3
|
-
|
|
4
|
-
export function enableJsonErrors() {
|
|
5
|
-
jsonErrorsEnabled = true;
|
|
6
|
-
}
|
|
3
|
+
import { isJsonErrorsEnabled } from "../helpers/cliError.js";
|
|
7
4
|
function emitJsonError(code, type, message, details) {
|
|
8
|
-
if (
|
|
5
|
+
if (isJsonErrorsEnabled()) {
|
|
9
6
|
const err = { error: type, code, message, ...(details ? { details } : {}) };
|
|
10
7
|
console.error(JSON.stringify(err));
|
|
11
8
|
}
|
|
@@ -13,6 +10,7 @@ function emitJsonError(code, type, message, details) {
|
|
|
13
10
|
}
|
|
14
11
|
export function handleAPIError(error) {
|
|
15
12
|
var _a, _b, _c, _d;
|
|
13
|
+
const jsonErrors = isJsonErrorsEnabled();
|
|
16
14
|
const apiError = error.response;
|
|
17
15
|
const requestConfig = error.config;
|
|
18
16
|
const method = ((requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.method) || "GET").toUpperCase();
|
|
@@ -20,35 +18,39 @@ export function handleAPIError(error) {
|
|
|
20
18
|
? `${requestConfig.baseURL || ""}/${requestConfig.url || ""}`.replace(/\/+/g, "/").replace(":/", "://")
|
|
21
19
|
: "unknown";
|
|
22
20
|
if (!apiError) {
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
if (!jsonErrors) {
|
|
22
|
+
spinner.error({ text: `Network error: ${error.message}` });
|
|
25
23
|
console.error(` Request: ${method} ${fullUrl}`);
|
|
24
|
+
}
|
|
26
25
|
emitJsonError(EXIT_GENERAL_ERROR, "network_error", error.message, { method, url: fullUrl });
|
|
27
26
|
}
|
|
28
27
|
const serverMessage = ((_b = (_a = apiError.data) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.message) || apiError.statusText;
|
|
29
28
|
if (apiError.status === 400) {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (!jsonErrors) {
|
|
30
|
+
spinner.error({ text: `Bad Request: ${serverMessage}` });
|
|
32
31
|
console.error(` Request: ${method} ${fullUrl}`);
|
|
32
|
+
}
|
|
33
33
|
emitJsonError(EXIT_GENERAL_ERROR, "bad_request", serverMessage, { method, url: fullUrl, status: 400 });
|
|
34
34
|
}
|
|
35
35
|
if (apiError.status === 401) {
|
|
36
|
-
|
|
36
|
+
if (!jsonErrors)
|
|
37
|
+
spinner.error({ text: "Session expired. Please run: ay login" });
|
|
37
38
|
emitJsonError(EXIT_AUTH_REQUIRED, "auth_required", "Session expired", { status: 401 });
|
|
38
39
|
}
|
|
39
40
|
if (apiError.status === 403) {
|
|
40
41
|
const rights = ((_d = (_c = apiError.data) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.requiredRights) || [];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (!jsonErrors) {
|
|
43
|
+
spinner.error({
|
|
44
|
+
text: `Forbidden. Required rights: [${rights.join(", ")}]`,
|
|
45
|
+
});
|
|
45
46
|
console.error(` Request: ${method} ${fullUrl}`);
|
|
47
|
+
}
|
|
46
48
|
emitJsonError(EXIT_PERMISSION_DENIED, "permission_denied", "Forbidden", { method, url: fullUrl, status: 403, requiredRights: rights });
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
if (!jsonErrors) {
|
|
51
|
+
spinner.error({
|
|
52
|
+
text: `Error ${apiError.status}: ${serverMessage}`,
|
|
53
|
+
});
|
|
52
54
|
console.error(` Request: ${method} ${fullUrl}`);
|
|
53
55
|
if (apiError.data && typeof apiError.data === "object") {
|
|
54
56
|
const debugInfo = apiError.data.meta || apiError.data;
|
package/lib/api/login.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { socket } from "../socket/socketClient.js";
|
|
2
2
|
import { randomAsciiString } from "../helpers/makeRandomToken.js";
|
|
3
3
|
import { spinner } from "../../index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
5
5
|
import { config } from "../helpers/config.js";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
function terminalLink(text, url) {
|
|
@@ -28,8 +28,8 @@ const waitForCredentials = async (token) => {
|
|
|
28
28
|
});
|
|
29
29
|
_socket.on("cli-token", async (data) => {
|
|
30
30
|
spinner.success({ text: `Received auth credentials` });
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
secureStorage.setItem("token", data.token);
|
|
32
|
+
secureStorage.setItem("refreshToken", data.refreshToken);
|
|
33
33
|
_socket.disconnect();
|
|
34
34
|
resolve(data);
|
|
35
35
|
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { api } from "./apiClient.js";
|
|
2
|
+
import { config } from "../helpers/config.js";
|
|
3
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
4
|
+
function getToken() {
|
|
5
|
+
return secureStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
|
|
6
|
+
}
|
|
7
|
+
function getSearchBaseURL() {
|
|
8
|
+
return config.searchUrl;
|
|
9
|
+
}
|
|
10
|
+
export async function searchModel(collection, params) {
|
|
11
|
+
const response = await api({
|
|
12
|
+
baseURL: getSearchBaseURL(),
|
|
13
|
+
method: "get",
|
|
14
|
+
url: `/${collection}`,
|
|
15
|
+
params,
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${getToken()}`,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return response.data;
|
|
21
|
+
}
|
|
22
|
+
export async function searchOne(collection, params) {
|
|
23
|
+
const response = await api({
|
|
24
|
+
baseURL: getSearchBaseURL(),
|
|
25
|
+
method: "get",
|
|
26
|
+
url: `/${collection}/one`,
|
|
27
|
+
params,
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${getToken()}`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return response.data;
|
|
33
|
+
}
|
|
34
|
+
export async function searchGlobal(query, limit = 25, callbacks = {}) {
|
|
35
|
+
const results = [];
|
|
36
|
+
const response = await api({
|
|
37
|
+
baseURL: getSearchBaseURL(),
|
|
38
|
+
method: "get",
|
|
39
|
+
url: "/",
|
|
40
|
+
params: { q: query, limit },
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${getToken()}`,
|
|
43
|
+
Accept: "text/event-stream",
|
|
44
|
+
},
|
|
45
|
+
responseType: "stream",
|
|
46
|
+
});
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let buffer = "";
|
|
49
|
+
let currentCollection = "";
|
|
50
|
+
let currentEntries = [];
|
|
51
|
+
const stream = response.data;
|
|
52
|
+
stream.on("data", (chunk) => {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
buffer += chunk.toString();
|
|
55
|
+
const lines = buffer.split("\n");
|
|
56
|
+
buffer = lines.pop() || "";
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
if (line.startsWith("event: ")) {
|
|
59
|
+
const event = line.slice(7).trim();
|
|
60
|
+
if (event === "model_done" && currentCollection) {
|
|
61
|
+
const result = {
|
|
62
|
+
collection: currentCollection,
|
|
63
|
+
entries: currentEntries,
|
|
64
|
+
total: currentEntries.length,
|
|
65
|
+
};
|
|
66
|
+
results.push(result);
|
|
67
|
+
(_a = callbacks.onResult) === null || _a === void 0 ? void 0 : _a.call(callbacks, result);
|
|
68
|
+
currentCollection = "";
|
|
69
|
+
currentEntries = [];
|
|
70
|
+
}
|
|
71
|
+
else if (event === "error") {
|
|
72
|
+
// Error data comes in the next data: line
|
|
73
|
+
}
|
|
74
|
+
else if (event === "done") {
|
|
75
|
+
(_b = callbacks.onDone) === null || _b === void 0 ? void 0 : _b.call(callbacks);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (line.startsWith("data: ")) {
|
|
79
|
+
const dataStr = line.slice(6).trim();
|
|
80
|
+
try {
|
|
81
|
+
const data = JSON.parse(dataStr);
|
|
82
|
+
if (data.model) {
|
|
83
|
+
currentCollection = data.model;
|
|
84
|
+
}
|
|
85
|
+
else if (data.entry) {
|
|
86
|
+
currentEntries.push(data.entry);
|
|
87
|
+
}
|
|
88
|
+
else if (data.error) {
|
|
89
|
+
(_c = callbacks.onError) === null || _c === void 0 ? void 0 : _c.call(callbacks, data.error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (_d) {
|
|
93
|
+
// Non-JSON data line, skip
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
stream.on("end", () => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
// Flush remaining buffer
|
|
101
|
+
if (currentCollection && currentEntries.length > 0) {
|
|
102
|
+
const result = {
|
|
103
|
+
collection: currentCollection,
|
|
104
|
+
entries: currentEntries,
|
|
105
|
+
total: currentEntries.length,
|
|
106
|
+
};
|
|
107
|
+
results.push(result);
|
|
108
|
+
(_a = callbacks.onResult) === null || _a === void 0 ? void 0 : _a.call(callbacks, result);
|
|
109
|
+
}
|
|
110
|
+
(_b = callbacks.onDone) === null || _b === void 0 ? void 0 : _b.call(callbacks);
|
|
111
|
+
resolve(results);
|
|
112
|
+
});
|
|
113
|
+
stream.on("error", (err) => {
|
|
114
|
+
var _a;
|
|
115
|
+
(_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, err.message);
|
|
116
|
+
reject(err);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { modelsAndRights } from "../../data/modelsAndRights.js";
|
|
3
|
+
import { aYOUneModules } from "../../data/modules.js";
|
|
4
|
+
import { isSuperUser, getTokenPayload } from "../helpers/tokenPayload.js";
|
|
5
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
6
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
7
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
|
+
function getAccessibleModules(su) {
|
|
9
|
+
var _a;
|
|
10
|
+
const moduleSummaries = [];
|
|
11
|
+
for (const mod of aYOUneModules) {
|
|
12
|
+
if (mod.superuserOnly && !su)
|
|
13
|
+
continue;
|
|
14
|
+
const collections = modelsAndRights.filter((m) => m.module === mod.module);
|
|
15
|
+
moduleSummaries.push({
|
|
16
|
+
module: mod.module,
|
|
17
|
+
label: mod.label,
|
|
18
|
+
collections: collections.length,
|
|
19
|
+
superuserOnly: (_a = mod.superuserOnly) !== null && _a !== void 0 ? _a : false,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return moduleSummaries;
|
|
23
|
+
}
|
|
24
|
+
function displayAccess(modules, su) {
|
|
25
|
+
const payload = getTokenPayload();
|
|
26
|
+
const name = (payload === null || payload === void 0 ? void 0 : payload.name) || (payload === null || payload === void 0 ? void 0 : payload.username) || (payload === null || payload === void 0 ? void 0 : payload.email) || "Unknown";
|
|
27
|
+
const type = (payload === null || payload === void 0 ? void 0 : payload.type) || "user";
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(chalk.bold(` Access Summary for ${chalk.cyan(name)}`));
|
|
30
|
+
console.log(chalk.dim(` Type: ${su ? chalk.yellow("superuser") : type}`));
|
|
31
|
+
console.log(chalk.dim(` ${"─".repeat(50)}`));
|
|
32
|
+
console.log();
|
|
33
|
+
const totalCollections = modules.reduce((sum, m) => sum + m.collections, 0);
|
|
34
|
+
// Column widths
|
|
35
|
+
const maxMod = Math.max(...modules.map((m) => m.module.length), 6);
|
|
36
|
+
const maxLabel = Math.max(...modules.map((m) => m.label.length), 5);
|
|
37
|
+
console.log(` ${chalk.dim("MODULE".padEnd(maxMod + 2))} ${chalk.dim("LABEL".padEnd(maxLabel + 2))} ${chalk.dim("COLLECTIONS")}`);
|
|
38
|
+
console.log(chalk.dim(` ${"─".repeat(maxMod + maxLabel + 18)}`));
|
|
39
|
+
for (const mod of modules) {
|
|
40
|
+
const modName = mod.superuserOnly
|
|
41
|
+
? chalk.yellow(mod.module.padEnd(maxMod + 2))
|
|
42
|
+
: mod.module.padEnd(maxMod + 2);
|
|
43
|
+
const label = mod.label.padEnd(maxLabel + 2);
|
|
44
|
+
const count = String(mod.collections).padStart(4);
|
|
45
|
+
console.log(` ${modName} ${label} ${count}`);
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(chalk.dim(` ${modules.length} modules, ${totalCollections} collections accessible`));
|
|
49
|
+
if (su) {
|
|
50
|
+
console.log(chalk.yellow(` Superuser access: all modules including system collections`));
|
|
51
|
+
}
|
|
52
|
+
console.log();
|
|
53
|
+
}
|
|
54
|
+
export function createAccessCommand(program) {
|
|
55
|
+
program
|
|
56
|
+
.command("access")
|
|
57
|
+
.description("Show accessible modules and collections for the current user")
|
|
58
|
+
.addHelpText("after", `
|
|
59
|
+
Examples:
|
|
60
|
+
ay access Show accessible modules and collection counts
|
|
61
|
+
ay access -r json Output as JSON
|
|
62
|
+
ay access --module crm Show collections in a specific module`)
|
|
63
|
+
.option("--module <name>", "Show collections for a specific module")
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
try {
|
|
66
|
+
const opts = { ...program.opts(), ...options };
|
|
67
|
+
const su = isSuperUser();
|
|
68
|
+
const modules = getAccessibleModules(su);
|
|
69
|
+
// Detail mode: list collections in a specific module
|
|
70
|
+
if (opts.module) {
|
|
71
|
+
const modName = opts.module.toLowerCase();
|
|
72
|
+
const mod = aYOUneModules.find((m) => m.module === modName);
|
|
73
|
+
if (!mod) {
|
|
74
|
+
cliError(`Unknown module: "${opts.module}". Use "ay access" to see available modules.`, EXIT_GENERAL_ERROR);
|
|
75
|
+
}
|
|
76
|
+
if (mod.superuserOnly && !su) {
|
|
77
|
+
cliError(`Module "${opts.module}" requires superuser access.`, EXIT_GENERAL_ERROR);
|
|
78
|
+
}
|
|
79
|
+
const collections = modelsAndRights
|
|
80
|
+
.filter((m) => m.module === modName)
|
|
81
|
+
.map((m) => ({
|
|
82
|
+
collection: m.plural,
|
|
83
|
+
singular: m.singular,
|
|
84
|
+
right: m.right,
|
|
85
|
+
}));
|
|
86
|
+
if (opts.responseFormat !== "json" || opts.save) {
|
|
87
|
+
handleResponseFormatOptions(opts, collections);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(chalk.bold(` Collections in ${chalk.cyan(mod.label)} (${modName})`));
|
|
92
|
+
console.log(chalk.dim(` ${"─".repeat(50)}`));
|
|
93
|
+
console.log();
|
|
94
|
+
const maxCol = Math.max(...collections.map((c) => c.collection.length), 10);
|
|
95
|
+
const maxRight = Math.max(...collections.map((c) => c.right.length), 5);
|
|
96
|
+
console.log(` ${chalk.dim("COLLECTION".padEnd(maxCol + 2))} ${chalk.dim("RIGHT")}`);
|
|
97
|
+
console.log(chalk.dim(` ${"─".repeat(maxCol + maxRight + 6)}`));
|
|
98
|
+
for (const col of collections) {
|
|
99
|
+
console.log(` ${col.collection.padEnd(maxCol + 2)} ${chalk.dim(col.right)}`);
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
102
|
+
console.log(chalk.dim(` ${collections.length} collections`));
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Summary mode: show all accessible modules
|
|
108
|
+
if (opts.responseFormat !== "json" || opts.save) {
|
|
109
|
+
// For non-default formats or save, use standard formatting
|
|
110
|
+
const data = modules.map((m) => ({
|
|
111
|
+
module: m.module,
|
|
112
|
+
label: m.label,
|
|
113
|
+
collections: m.collections,
|
|
114
|
+
...(m.superuserOnly ? { superuserOnly: true } : {}),
|
|
115
|
+
}));
|
|
116
|
+
handleResponseFormatOptions(opts, data);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
displayAccess(modules, su);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|