ente-cli 0.1.0 → 0.4.54
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 +274 -0
- package/dist/SKILL.md +344 -0
- package/dist/cli.mjs +16485 -0
- package/dist/cli.mjs.map +7 -0
- package/package.json +28 -15
- package/dist/api.d.ts +0 -221
- package/dist/api.js +0 -182
- package/dist/commands/activity.d.ts +0 -16
- package/dist/commands/activity.js +0 -345
- package/dist/commands/auth.d.ts +0 -7
- package/dist/commands/auth.js +0 -86
- package/dist/commands/entity.d.ts +0 -13
- package/dist/commands/entity.js +0 -205
- package/dist/commands/search.d.ts +0 -3
- package/dist/commands/search.js +0 -13
- package/dist/commands/task.d.ts +0 -3
- package/dist/commands/task.js +0 -305
- package/dist/commands/update.d.ts +0 -7
- package/dist/commands/update.js +0 -104
- package/dist/deadline.d.ts +0 -6
- package/dist/deadline.js +0 -171
- package/dist/format.d.ts +0 -21
- package/dist/format.js +0 -287
- package/dist/guide/index.d.ts +0 -2
- package/dist/guide/index.js +0 -27
- package/dist/guide/topics.d.ts +0 -5
- package/dist/guide/topics.js +0 -208
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -54
- package/dist/resolve.d.ts +0 -11
- package/dist/resolve.js +0 -66
- package/dist/utils.d.ts +0 -4
- package/dist/utils.js +0 -18
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import { resolveEntity, resolveEntities, resolveSpaceByName } from "../resolve.js";
|
|
2
|
-
import { formatActivityList, formatActivityDetail, formatTaskList, formatSpacesFooter, jsonOutputWithSpaces, } from "../format.js";
|
|
3
|
-
import { parseAttrs } from "../utils.js";
|
|
4
|
-
/**
|
|
5
|
-
* Shared activity-list logic used by both `activity list` and `ente tasks`.
|
|
6
|
-
*/
|
|
7
|
-
const VALID_SORT_FIELDS = ["deadline", "created"];
|
|
8
|
-
export async function runActivityList(client, opts) {
|
|
9
|
-
if (opts.sort && !VALID_SORT_FIELDS.includes(opts.sort)) {
|
|
10
|
-
throw new Error(`Invalid --sort value '${opts.sort}'. Allowed values: ${VALID_SORT_FIELDS.join(", ")}`);
|
|
11
|
-
}
|
|
12
|
-
let entityId;
|
|
13
|
-
if (opts.entity) {
|
|
14
|
-
entityId = await resolveEntity(client, opts.entity);
|
|
15
|
-
}
|
|
16
|
-
const spaces = await client.listSpaces();
|
|
17
|
-
// When --type task + --only-open, use the richer listTasks API
|
|
18
|
-
if (opts.type === "task" && opts.onlyOpen) {
|
|
19
|
-
if (opts.before !== undefined || opts.after !== undefined) {
|
|
20
|
-
throw new Error("--before/--after cannot be used with --type task --only-open. Remove --only-open to use --before/--after.");
|
|
21
|
-
}
|
|
22
|
-
const res = await client.listTasks({ entityId, onlyOpen: true });
|
|
23
|
-
let tasks = res.data;
|
|
24
|
-
// Apply --hours filter
|
|
25
|
-
if (opts.hours) {
|
|
26
|
-
const cutoff = Date.now() - opts.hours * 60 * 60 * 1000;
|
|
27
|
-
tasks = tasks.filter((t) => t.activity.createdAt >= cutoff);
|
|
28
|
-
}
|
|
29
|
-
// Apply --attr filter
|
|
30
|
-
const attrFilter = parseAttrs(opts.attr);
|
|
31
|
-
if (Object.keys(attrFilter).length > 0) {
|
|
32
|
-
tasks = tasks.filter((t) => {
|
|
33
|
-
const attrs = t.activity.attributes ?? {};
|
|
34
|
-
return Object.entries(attrFilter).every(([k, v]) => String(attrs[k] ?? "") === v);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
// Sort by deadline (default for tasks) or created
|
|
38
|
-
if (opts.sort === "created") {
|
|
39
|
-
tasks.sort((a, b) => b.activity.createdAt - a.activity.createdAt);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
// default: deadline ascending
|
|
43
|
-
tasks.sort((a, b) => {
|
|
44
|
-
const da = a.activity.deadline ?? Infinity;
|
|
45
|
-
const db = b.activity.deadline ?? Infinity;
|
|
46
|
-
return da - db;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
// Apply --limit after sorting
|
|
50
|
-
if (opts.limit) {
|
|
51
|
-
tasks = tasks.slice(0, opts.limit);
|
|
52
|
-
}
|
|
53
|
-
if (opts.json) {
|
|
54
|
-
console.log(jsonOutputWithSpaces({ data: tasks, users: res.users }, spaces));
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
console.log(formatTaskList(tasks, res.users));
|
|
58
|
-
console.log(formatSpacesFooter(spaces));
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// General activity list path
|
|
63
|
-
// Note: --sort is excluded from hasClientFilters because sorting can be
|
|
64
|
-
// applied to whatever page the server returns. Including it here would
|
|
65
|
-
// trigger full pagination (up to 10K items) just to re-order results.
|
|
66
|
-
const hasClientFilters = opts.type || opts.onlyOpen || (opts.attr && opts.attr.length > 0);
|
|
67
|
-
let activities;
|
|
68
|
-
let serverHasMore;
|
|
69
|
-
if (hasClientFilters) {
|
|
70
|
-
// Paginate through all server pages to avoid silent data truncation
|
|
71
|
-
// (server caps at 200 per page)
|
|
72
|
-
const MAX_PAGINATED_ITEMS = 10_000;
|
|
73
|
-
const MAX_PAGES = 50;
|
|
74
|
-
const hoursCutoff = opts.hours
|
|
75
|
-
? Date.now() - opts.hours * 60 * 60 * 1000
|
|
76
|
-
: undefined;
|
|
77
|
-
activities = [];
|
|
78
|
-
let before = opts.before;
|
|
79
|
-
let pages = 0;
|
|
80
|
-
let hitCap = false;
|
|
81
|
-
for (;;) {
|
|
82
|
-
const page = await client.listActivities({
|
|
83
|
-
entityId,
|
|
84
|
-
limit: 200,
|
|
85
|
-
before,
|
|
86
|
-
after: opts.after,
|
|
87
|
-
});
|
|
88
|
-
let pageData = page.data;
|
|
89
|
-
// Apply --hours as a client-side filter since server may not support it
|
|
90
|
-
if (hoursCutoff) {
|
|
91
|
-
pageData = pageData.filter((a) => a.createdAt >= hoursCutoff);
|
|
92
|
-
// If any items were filtered out, all subsequent pages will be older
|
|
93
|
-
if (pageData.length < page.data.length) {
|
|
94
|
-
activities.push(...pageData);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
activities.push(...pageData);
|
|
99
|
-
pages++;
|
|
100
|
-
if (!page.hasMore || page.data.length === 0)
|
|
101
|
-
break;
|
|
102
|
-
// Safety caps to prevent unbounded pagination
|
|
103
|
-
if (activities.length >= MAX_PAGINATED_ITEMS || pages >= MAX_PAGES) {
|
|
104
|
-
hitCap = true;
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
// Use the oldest item's createdAt as the cursor for the next page
|
|
108
|
-
before = page.data[page.data.length - 1].createdAt;
|
|
109
|
-
}
|
|
110
|
-
if (hitCap) {
|
|
111
|
-
console.error(`Warning: pagination cap reached (${activities.length} items / ${pages} pages). Results may be incomplete — narrow your query with --hours, --before, or --entity.`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
const res = await client.listActivities({
|
|
116
|
-
entityId,
|
|
117
|
-
hoursBack: opts.hours,
|
|
118
|
-
limit: opts.limit,
|
|
119
|
-
before: opts.before,
|
|
120
|
-
after: opts.after,
|
|
121
|
-
});
|
|
122
|
-
activities = res.data;
|
|
123
|
-
serverHasMore = res.hasMore;
|
|
124
|
-
}
|
|
125
|
-
// Client-side --type filter
|
|
126
|
-
if (opts.type) {
|
|
127
|
-
activities = activities.filter((a) => a.type === opts.type);
|
|
128
|
-
}
|
|
129
|
-
// Client-side --only-open filter
|
|
130
|
-
if (opts.onlyOpen) {
|
|
131
|
-
activities = activities.filter((a) => !a.completed);
|
|
132
|
-
}
|
|
133
|
-
// Client-side --attr filter
|
|
134
|
-
const attrFilter = parseAttrs(opts.attr);
|
|
135
|
-
if (Object.keys(attrFilter).length > 0) {
|
|
136
|
-
activities = activities.filter((a) => {
|
|
137
|
-
const attrs = a.attributes ?? {};
|
|
138
|
-
return Object.entries(attrFilter).every(([k, v]) => String(attrs[k] ?? "") === v);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
// Sort before limiting to get correct top-N results
|
|
142
|
-
if (opts.sort === "deadline") {
|
|
143
|
-
activities.sort((a, b) => {
|
|
144
|
-
const da = a.deadline ?? Infinity;
|
|
145
|
-
const db = b.deadline ?? Infinity;
|
|
146
|
-
return da - db;
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
else if (opts.sort === "created") {
|
|
150
|
-
activities.sort((a, b) => b.createdAt - a.createdAt);
|
|
151
|
-
}
|
|
152
|
-
// Apply --limit post-filter and post-sort
|
|
153
|
-
if (hasClientFilters && opts.limit) {
|
|
154
|
-
activities = activities.slice(0, opts.limit);
|
|
155
|
-
}
|
|
156
|
-
const hasMore = hasClientFilters ? undefined : serverHasMore;
|
|
157
|
-
if (opts.json) {
|
|
158
|
-
console.log(jsonOutputWithSpaces({ data: activities, ...(hasMore !== undefined && { hasMore }) }, spaces));
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
console.log(formatActivityList(activities));
|
|
162
|
-
if (hasMore && activities.length > 0) {
|
|
163
|
-
const oldest = activities[activities.length - 1];
|
|
164
|
-
console.log(`\n(more results available — use --before ${oldest.createdAt})`);
|
|
165
|
-
}
|
|
166
|
-
console.log(formatSpacesFooter(spaces));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
export function registerActivityCommands(program, getClient) {
|
|
170
|
-
const activity = program
|
|
171
|
-
.command("activity")
|
|
172
|
-
.description("Manage activities");
|
|
173
|
-
// -- create ---------------------------------------------------------------
|
|
174
|
-
activity
|
|
175
|
-
.command("create")
|
|
176
|
-
.description("Create an activity (note, conversation, call, message)")
|
|
177
|
-
.requiredOption("--type <type>", "Activity type (note, conversation, call, message)")
|
|
178
|
-
.requiredOption("--title <title>", "Activity title")
|
|
179
|
-
.requiredOption("--entity <names...>", "Linked entities (resolved by name)")
|
|
180
|
-
.requiredOption("--space <name-or-id>", "Space to create the activity in (name or ID). Determines who can see this activity.")
|
|
181
|
-
.option("--content <text>", "Activity content")
|
|
182
|
-
.option("--auto-link", "Auto-link entities not yet in the target space (only works for personal space). Use when referencing business entities in a private context.")
|
|
183
|
-
.option("--json", "Output raw JSON")
|
|
184
|
-
.action(async (opts) => {
|
|
185
|
-
const client = getClient();
|
|
186
|
-
const entityIds = await resolveEntities(client, opts.entity);
|
|
187
|
-
const spaceId = await resolveSpaceByName(client, opts.space);
|
|
188
|
-
const res = await client.createActivity({
|
|
189
|
-
entityIds,
|
|
190
|
-
type: opts.type,
|
|
191
|
-
title: opts.title,
|
|
192
|
-
content: opts.content,
|
|
193
|
-
spaceId,
|
|
194
|
-
autoLinkPersonalSpace: opts.autoLink ?? false,
|
|
195
|
-
});
|
|
196
|
-
const spaces = await client.listSpaces();
|
|
197
|
-
if (opts.json) {
|
|
198
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
console.log(`Created ${opts.type} '${opts.title}' (${res.data.activityId})`);
|
|
202
|
-
console.log(formatSpacesFooter(spaces));
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
// -- search ---------------------------------------------------------------
|
|
206
|
-
activity
|
|
207
|
-
.command("search <query>")
|
|
208
|
-
.description("Search activities by title or content")
|
|
209
|
-
.option("--entity <name>", "Filter by entity (name or ID)")
|
|
210
|
-
.option("--type <type>", "Filter by activity type (note, conversation, call, message, task)")
|
|
211
|
-
.option("--limit <n>", "Max results", parseInt)
|
|
212
|
-
.option("--json", "Output raw JSON")
|
|
213
|
-
.action(async (query, opts) => {
|
|
214
|
-
const client = getClient();
|
|
215
|
-
let entityId;
|
|
216
|
-
if (opts.entity) {
|
|
217
|
-
entityId = await resolveEntity(client, opts.entity);
|
|
218
|
-
}
|
|
219
|
-
const [res, spaces] = await Promise.all([
|
|
220
|
-
client.searchActivities(query, {
|
|
221
|
-
entityId,
|
|
222
|
-
type: opts.type,
|
|
223
|
-
limit: opts.limit,
|
|
224
|
-
}),
|
|
225
|
-
client.listSpaces(),
|
|
226
|
-
]);
|
|
227
|
-
if (opts.json) {
|
|
228
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
console.log(formatActivityList(res.data));
|
|
232
|
-
console.log(formatSpacesFooter(spaces));
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
// -- list -----------------------------------------------------------------
|
|
236
|
-
activity
|
|
237
|
-
.command("list")
|
|
238
|
-
.description("List activities")
|
|
239
|
-
.option("--entity <name>", "Filter by entity (name or ID)")
|
|
240
|
-
.option("--type <type>", "Filter by activity type (task, call, note, message, conversation)")
|
|
241
|
-
.option("--attr <pairs...>", "Filter by attribute key=value")
|
|
242
|
-
.option("--only-open", "Only show non-completed activities")
|
|
243
|
-
.option("--hours <n>", "Only from the last N hours", parseInt)
|
|
244
|
-
.option("--limit <n>", "Max results", parseInt)
|
|
245
|
-
.option("--sort <field>", "Sort by: deadline, created")
|
|
246
|
-
.option("--before <timestamp>", "Only activities before this timestamp (ms)", parseInt)
|
|
247
|
-
.option("--after <timestamp>", "Only activities after this timestamp (ms)", parseInt)
|
|
248
|
-
.option("--json", "Output raw JSON")
|
|
249
|
-
.action(async (opts) => {
|
|
250
|
-
const client = getClient();
|
|
251
|
-
await runActivityList(client, opts);
|
|
252
|
-
});
|
|
253
|
-
// -- get ------------------------------------------------------------------
|
|
254
|
-
activity
|
|
255
|
-
.command("get <id>")
|
|
256
|
-
.description("Get a single activity by ID")
|
|
257
|
-
.option("--json", "Output raw JSON")
|
|
258
|
-
.action(async (id, opts) => {
|
|
259
|
-
const client = getClient();
|
|
260
|
-
const [res, spaces] = await Promise.all([
|
|
261
|
-
client.getActivity(id),
|
|
262
|
-
client.listSpaces(),
|
|
263
|
-
]);
|
|
264
|
-
if (opts.json) {
|
|
265
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
console.log(formatActivityDetail(res.data));
|
|
269
|
-
console.log(formatSpacesFooter(spaces));
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
// -- update ---------------------------------------------------------------
|
|
273
|
-
activity
|
|
274
|
-
.command("update <id>")
|
|
275
|
-
.description("Update an activity")
|
|
276
|
-
.option("--content <text>", "Update content")
|
|
277
|
-
.option("--title <text>", "Update title")
|
|
278
|
-
.option("--entity-add <names...>", "Link additional entities (resolved by name)")
|
|
279
|
-
.option("--entity-remove <names...>", "Unlink entities (resolved by name)")
|
|
280
|
-
.option("--json", "Output raw JSON")
|
|
281
|
-
.action(async (id, opts) => {
|
|
282
|
-
const client = getClient();
|
|
283
|
-
const body = {};
|
|
284
|
-
if (opts.content !== undefined)
|
|
285
|
-
body.content = opts.content;
|
|
286
|
-
if (opts.title !== undefined)
|
|
287
|
-
body.title = opts.title;
|
|
288
|
-
// Handle entity add/remove by computing the full entityIds list
|
|
289
|
-
if (opts.entityAdd || opts.entityRemove) {
|
|
290
|
-
const activity = await client.getActivity(id);
|
|
291
|
-
const currentIds = activity.data.entityIds ?? [];
|
|
292
|
-
const addIds = opts.entityAdd
|
|
293
|
-
? await resolveEntities(client, opts.entityAdd)
|
|
294
|
-
: [];
|
|
295
|
-
const removeIds = opts.entityRemove
|
|
296
|
-
? await resolveEntities(client, opts.entityRemove)
|
|
297
|
-
: [];
|
|
298
|
-
const removeSet = new Set(removeIds);
|
|
299
|
-
const newIds = [
|
|
300
|
-
...currentIds.filter((eid) => !removeSet.has(eid)),
|
|
301
|
-
...addIds.filter((eid) => !currentIds.includes(eid)),
|
|
302
|
-
];
|
|
303
|
-
if (newIds.length === 0) {
|
|
304
|
-
console.error("Cannot remove all entities — an activity must have at least one linked entity.");
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
body.entityIds = newIds;
|
|
308
|
-
}
|
|
309
|
-
if (Object.keys(body).length === 0) {
|
|
310
|
-
console.error("Nothing to update. Specify --content, --title, --entity-add, or --entity-remove.");
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
const [res, spaces] = await Promise.all([
|
|
314
|
-
client.updateActivity(id, body),
|
|
315
|
-
client.listSpaces(),
|
|
316
|
-
]);
|
|
317
|
-
if (opts.json) {
|
|
318
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
console.log(`Updated activity ${id}`);
|
|
322
|
-
console.log(formatSpacesFooter(spaces));
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
// -- delete ---------------------------------------------------------------
|
|
326
|
-
activity
|
|
327
|
-
.command("delete <id>")
|
|
328
|
-
.description("Delete an activity")
|
|
329
|
-
.option("--json", "Output JSON")
|
|
330
|
-
.action(async (id, opts) => {
|
|
331
|
-
const client = getClient();
|
|
332
|
-
const [, spaces] = await Promise.all([
|
|
333
|
-
client.deleteActivity(id),
|
|
334
|
-
client.listSpaces(),
|
|
335
|
-
]);
|
|
336
|
-
if (opts.json) {
|
|
337
|
-
console.log(jsonOutputWithSpaces({ data: { success: true, activityId: id } }, spaces));
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
console.log(`Deleted activity ${id}`);
|
|
341
|
-
console.log(formatSpacesFooter(spaces));
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
//# sourceMappingURL=activity.js.map
|
package/dist/commands/auth.d.ts
DELETED
package/dist/commands/auth.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as os from "os";
|
|
4
|
-
const CONFIG_DIR = path.join(os.homedir(), ".config", "ente");
|
|
5
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
6
|
-
export function readStoredConfig() {
|
|
7
|
-
try {
|
|
8
|
-
const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
9
|
-
const data = JSON.parse(raw);
|
|
10
|
-
if (data.url && data.key)
|
|
11
|
-
return { url: data.url, key: data.key };
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function writeConfig(config) {
|
|
19
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
20
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", {
|
|
21
|
-
mode: 0o600, // owner read/write only
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
export function registerAuth(program) {
|
|
25
|
-
const auth = program.command("auth").description("Manage authentication");
|
|
26
|
-
auth
|
|
27
|
-
.command("login")
|
|
28
|
-
.description("Authenticate with an ente deployment")
|
|
29
|
-
.requiredOption("--url <url>", "Convex site URL (e.g. https://your-deployment.convex.site)")
|
|
30
|
-
.requiredOption("--key <key>", "API key (from ente Settings → API Keys)")
|
|
31
|
-
.action(async (opts) => {
|
|
32
|
-
const url = opts.url.replace(/\/+$/, "");
|
|
33
|
-
// Validate by calling the API
|
|
34
|
-
try {
|
|
35
|
-
const res = await fetch(`${url}/api/external/entities?limit=1`, {
|
|
36
|
-
headers: { Authorization: `Bearer ${opts.key}` },
|
|
37
|
-
});
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
console.error(`Authentication failed: ${res.status} ${res.statusText}`);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
console.error(`Could not reach ${url}: ${err.message}`);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
writeConfig({ url, key: opts.key });
|
|
48
|
-
console.log(`✓ Authenticated successfully`);
|
|
49
|
-
console.log(` Config saved to ${CONFIG_FILE}`);
|
|
50
|
-
});
|
|
51
|
-
auth
|
|
52
|
-
.command("status")
|
|
53
|
-
.description("Show current authentication status")
|
|
54
|
-
.action(() => {
|
|
55
|
-
const envUrl = process.env.ENTE_API_URL;
|
|
56
|
-
const envKey = process.env.ENTE_API_KEY;
|
|
57
|
-
const stored = readStoredConfig();
|
|
58
|
-
if (envUrl && envKey) {
|
|
59
|
-
console.log(`Source: environment variables`);
|
|
60
|
-
console.log(` URL: ${envUrl}`);
|
|
61
|
-
console.log(` Key: ${envKey.slice(0, 12)}...`);
|
|
62
|
-
}
|
|
63
|
-
else if (stored) {
|
|
64
|
-
console.log(`Source: ${CONFIG_FILE}`);
|
|
65
|
-
console.log(` URL: ${stored.url}`);
|
|
66
|
-
console.log(` Key: ${stored.key.slice(0, 12)}...`);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
console.log("Not authenticated.");
|
|
70
|
-
console.log("Run: ente auth login --url <url> --key <key>");
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
auth
|
|
74
|
-
.command("logout")
|
|
75
|
-
.description("Remove stored credentials")
|
|
76
|
-
.action(() => {
|
|
77
|
-
try {
|
|
78
|
-
fs.unlinkSync(CONFIG_FILE);
|
|
79
|
-
console.log("✓ Credentials removed");
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
console.log("No stored credentials found");
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=auth.js.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import type { EnteClient } from "../api.js";
|
|
3
|
-
/**
|
|
4
|
-
* Shared entity-list logic used by both `entity list` and `search`.
|
|
5
|
-
*/
|
|
6
|
-
export declare function runEntityList(client: EnteClient, opts: {
|
|
7
|
-
query?: string;
|
|
8
|
-
type?: string;
|
|
9
|
-
limit?: number;
|
|
10
|
-
space?: string;
|
|
11
|
-
json?: boolean;
|
|
12
|
-
}): Promise<void>;
|
|
13
|
-
export declare function registerEntityCommands(program: Command, getClient: () => EnteClient): void;
|
package/dist/commands/entity.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { resolveEntity, resolveEntities, resolveSpaceByName } from "../resolve.js";
|
|
2
|
-
import { formatEntityDetail, formatEntityList, formatSpacesFooter, jsonOutputWithSpaces, } from "../format.js";
|
|
3
|
-
import { parseAttrs } from "../utils.js";
|
|
4
|
-
/**
|
|
5
|
-
* Shared entity-list logic used by both `entity list` and `search`.
|
|
6
|
-
*/
|
|
7
|
-
export async function runEntityList(client, opts) {
|
|
8
|
-
let spaceId;
|
|
9
|
-
if (opts.space) {
|
|
10
|
-
spaceId = await resolveSpaceByName(client, opts.space);
|
|
11
|
-
}
|
|
12
|
-
const res = await client.searchEntities(opts.query, { limit: opts.limit ?? 50, spaceId });
|
|
13
|
-
let entities = res.data.entities;
|
|
14
|
-
if (opts.type) {
|
|
15
|
-
entities = entities.filter((e) => e.entity.type === opts.type);
|
|
16
|
-
}
|
|
17
|
-
if (opts.json) {
|
|
18
|
-
console.log(jsonOutputWithSpaces({ data: { ...res.data, entities } }, res.data.spaces));
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
console.log(formatEntityList(entities, res.data.spaces));
|
|
22
|
-
console.log(formatSpacesFooter(res.data.spaces));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
export function registerEntityCommands(program, getClient) {
|
|
26
|
-
const entity = program
|
|
27
|
-
.command("entity")
|
|
28
|
-
.description("Manage entities");
|
|
29
|
-
// -- list -----------------------------------------------------------------
|
|
30
|
-
entity
|
|
31
|
-
.command("list")
|
|
32
|
-
.description("List or search entities")
|
|
33
|
-
.option("--query <text>", "Fuzzy name search")
|
|
34
|
-
.option("--type <type>", "Filter by entity type (person, org, project, topic)")
|
|
35
|
-
.option("--space <name-or-id>", "Filter by space (name or ID)")
|
|
36
|
-
.option("--limit <n>", "Max results (default: 50)", parseInt)
|
|
37
|
-
.option("--json", "Output raw JSON")
|
|
38
|
-
.action(async (opts) => {
|
|
39
|
-
const client = getClient();
|
|
40
|
-
await runEntityList(client, opts);
|
|
41
|
-
});
|
|
42
|
-
// -- create ---------------------------------------------------------------
|
|
43
|
-
entity
|
|
44
|
-
.command("create <name>")
|
|
45
|
-
.description("Create a new entity")
|
|
46
|
-
.requiredOption("--type <type>", "Entity type (person, org, project, topic)")
|
|
47
|
-
.option("--pin <entities...>", "Pin to other entities (resolved by name)")
|
|
48
|
-
.option("--alias <aliases...>", "Alternative names")
|
|
49
|
-
.option("--attr <pairs...>", "Attributes as key=value pairs")
|
|
50
|
-
.option("--description <text>", "Description text")
|
|
51
|
-
.option("--json", "Output raw JSON")
|
|
52
|
-
.action(async (name, opts) => {
|
|
53
|
-
const client = getClient();
|
|
54
|
-
const attributes = parseAttrs(opts.attr);
|
|
55
|
-
const res = await client.createEntity({
|
|
56
|
-
name,
|
|
57
|
-
type: opts.type,
|
|
58
|
-
description: opts.description,
|
|
59
|
-
aliases: opts.alias,
|
|
60
|
-
attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
|
|
61
|
-
});
|
|
62
|
-
const entityId = res.data.entityId;
|
|
63
|
-
// Pin targets
|
|
64
|
-
if (opts.pin && opts.pin.length > 0) {
|
|
65
|
-
const pinIds = await resolveEntities(client, opts.pin);
|
|
66
|
-
for (const targetId of pinIds) {
|
|
67
|
-
await client.addPin(entityId, targetId);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const spaces = await client.listSpaces();
|
|
71
|
-
if (opts.json) {
|
|
72
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
console.log(`Created ${opts.type} '${name}' (${entityId})`);
|
|
76
|
-
if (opts.pin && opts.pin.length > 0) {
|
|
77
|
-
console.log(`Pinned to: ${opts.pin.join(", ")}`);
|
|
78
|
-
}
|
|
79
|
-
console.log(formatSpacesFooter(spaces));
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
// -- get ------------------------------------------------------------------
|
|
83
|
-
entity
|
|
84
|
-
.command("get <name-or-id>")
|
|
85
|
-
.description("Get entity details")
|
|
86
|
-
.option("--json", "Output raw JSON")
|
|
87
|
-
.action(async (nameOrId, opts) => {
|
|
88
|
-
const client = getClient();
|
|
89
|
-
const id = await resolveEntity(client, nameOrId);
|
|
90
|
-
const [res, spaces] = await Promise.all([
|
|
91
|
-
client.getEntity(id, { limit: 10 }),
|
|
92
|
-
client.listSpaces(),
|
|
93
|
-
]);
|
|
94
|
-
if (opts.json) {
|
|
95
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
console.log(formatEntityDetail(res.data));
|
|
99
|
-
console.log(formatSpacesFooter(spaces));
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
// -- update ---------------------------------------------------------------
|
|
103
|
-
entity
|
|
104
|
-
.command("update <name-or-id>")
|
|
105
|
-
.description("Update an entity")
|
|
106
|
-
.option("--alias-add <alias>", "Add an alias")
|
|
107
|
-
.option("--alias-remove <alias>", "Remove an alias")
|
|
108
|
-
.option("--attr <pairs...>", "Set attributes as key=value pairs")
|
|
109
|
-
.option("--description <text>", "Update description")
|
|
110
|
-
.option("--json", "Output raw JSON")
|
|
111
|
-
.action(async (nameOrId, opts) => {
|
|
112
|
-
const client = getClient();
|
|
113
|
-
const id = await resolveEntity(client, nameOrId);
|
|
114
|
-
const body = {};
|
|
115
|
-
const newAttrs = parseAttrs(opts.attr);
|
|
116
|
-
const needsFetch = opts.aliasAdd || opts.aliasRemove || Object.keys(newAttrs).length > 0;
|
|
117
|
-
// Fetch current entity once if we need to merge aliases or attributes
|
|
118
|
-
const currentEntity = needsFetch ? (await client.getEntity(id)).data.entity : null;
|
|
119
|
-
if (opts.aliasAdd || opts.aliasRemove) {
|
|
120
|
-
const aliases = [...(currentEntity?.aliases || [])];
|
|
121
|
-
if (opts.aliasAdd && !aliases.includes(opts.aliasAdd)) {
|
|
122
|
-
aliases.push(opts.aliasAdd);
|
|
123
|
-
}
|
|
124
|
-
if (opts.aliasRemove) {
|
|
125
|
-
const idx = aliases.indexOf(opts.aliasRemove);
|
|
126
|
-
if (idx !== -1)
|
|
127
|
-
aliases.splice(idx, 1);
|
|
128
|
-
}
|
|
129
|
-
body.aliases = aliases;
|
|
130
|
-
}
|
|
131
|
-
if (Object.keys(newAttrs).length > 0) {
|
|
132
|
-
// Server does full replace for entity attributes, so merge with existing
|
|
133
|
-
const existing = currentEntity?.attributes ?? {};
|
|
134
|
-
body.attributes = { ...existing, ...newAttrs };
|
|
135
|
-
}
|
|
136
|
-
if (opts.description !== undefined) {
|
|
137
|
-
body.description = opts.description;
|
|
138
|
-
}
|
|
139
|
-
if (Object.keys(body).length === 0) {
|
|
140
|
-
console.error("Nothing to update. Specify --alias-add, --alias-remove, --attr, or --description.");
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
const [res, spaces] = await Promise.all([
|
|
144
|
-
client.updateEntity(id, body),
|
|
145
|
-
client.listSpaces(),
|
|
146
|
-
]);
|
|
147
|
-
if (opts.json) {
|
|
148
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
console.log(`Updated entity ${id}`);
|
|
152
|
-
console.log(formatSpacesFooter(spaces));
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
// -- pin / unpin ----------------------------------------------------------
|
|
156
|
-
entity
|
|
157
|
-
.command("pin <source> <target>")
|
|
158
|
-
.description("Pin target entity to source entity")
|
|
159
|
-
.option("--json", "Output JSON")
|
|
160
|
-
.action(async (source, target, opts) => {
|
|
161
|
-
const client = getClient();
|
|
162
|
-
const [sourceId, targetId] = await Promise.all([
|
|
163
|
-
resolveEntity(client, source),
|
|
164
|
-
resolveEntity(client, target),
|
|
165
|
-
]);
|
|
166
|
-
const [res, spaces] = await Promise.all([
|
|
167
|
-
client.addPin(sourceId, targetId),
|
|
168
|
-
client.listSpaces(),
|
|
169
|
-
]);
|
|
170
|
-
if (opts.json) {
|
|
171
|
-
console.log(jsonOutputWithSpaces(res, spaces));
|
|
172
|
-
}
|
|
173
|
-
else if (res.data.alreadyExists) {
|
|
174
|
-
console.log("Pin already exists.");
|
|
175
|
-
console.log(formatSpacesFooter(spaces));
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
console.log("Pin added.");
|
|
179
|
-
console.log(formatSpacesFooter(spaces));
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
entity
|
|
183
|
-
.command("unpin <source> <target>")
|
|
184
|
-
.description("Remove pin between entities")
|
|
185
|
-
.option("--json", "Output JSON")
|
|
186
|
-
.action(async (source, target, opts) => {
|
|
187
|
-
const client = getClient();
|
|
188
|
-
const [sourceId, targetId] = await Promise.all([
|
|
189
|
-
resolveEntity(client, source),
|
|
190
|
-
resolveEntity(client, target),
|
|
191
|
-
]);
|
|
192
|
-
const [, spaces] = await Promise.all([
|
|
193
|
-
client.removePin(sourceId, targetId),
|
|
194
|
-
client.listSpaces(),
|
|
195
|
-
]);
|
|
196
|
-
if (opts.json) {
|
|
197
|
-
console.log(jsonOutputWithSpaces({ data: { success: true } }, spaces));
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
console.log("Pin removed.");
|
|
201
|
-
console.log(formatSpacesFooter(spaces));
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
//# sourceMappingURL=entity.js.map
|
package/dist/commands/search.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { runEntityList } from "./entity.js";
|
|
2
|
-
export function registerSearchCommand(program, getClient) {
|
|
3
|
-
program
|
|
4
|
-
.command("search <query>")
|
|
5
|
-
.description("Fuzzy search entities")
|
|
6
|
-
.option("--type <type>", "Filter by entity type (person, org, project, topic)")
|
|
7
|
-
.option("--json", "Output raw JSON")
|
|
8
|
-
.action(async (query, opts) => {
|
|
9
|
-
const client = getClient();
|
|
10
|
-
await runEntityList(client, { query, type: opts.type, json: opts.json });
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=search.js.map
|
package/dist/commands/task.d.ts
DELETED