@dayofweek/dcli 1.9.1 → 1.11.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/README.md +32 -0
- package/dist/bin/dcli.js +157 -7
- package/dist/bundle/dcli.cjs +167 -10
- package/dist/client.d.ts +82 -7
- package/dist/client.js +106 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -188,6 +188,38 @@ customer is irreversible and outward-facing, so it cannot happen as a side
|
|
|
188
188
|
effect of reading the inbox — a person approves the exact text first, and
|
|
189
189
|
`--approved` records that they did.
|
|
190
190
|
|
|
191
|
+
## Cross-org reads (staff)
|
|
192
|
+
|
|
193
|
+
Admin tokens can read across every organization. Both listings are
|
|
194
|
+
cursor-paged: filters apply within each scanned page, so a page can be short
|
|
195
|
+
or empty while more rows remain — `--all` walks the pages for you.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
dcli admin entities --category supply --all --json # every producer, every org
|
|
199
|
+
dcli admin entities --role producer --with-roles --json # first page, roles attached
|
|
200
|
+
dcli admin entities --search huseby --json
|
|
201
|
+
dcli admin entities --limit 500 --cursor <nextCursor> --json
|
|
202
|
+
|
|
203
|
+
dcli admin market-intel stats --json # the market intelligence register, counted
|
|
204
|
+
dcli admin market-intel list --type farm --region Agder --all --json
|
|
205
|
+
dcli admin market-intel list --bbox 57.95,6.35,59.70,9.40 --json
|
|
206
|
+
dcli admin market-intel get <id> --json
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Entity rows carry `typeName` and `typeCategory` resolved from the entity's
|
|
210
|
+
type — count on those, not on the legacy `entityType` string. The market
|
|
211
|
+
intelligence register is a separate table from the entity hierarchy (every row
|
|
212
|
+
has coordinates; `linkedEntity` names the hierarchy entity it points at, if
|
|
213
|
+
any), so "how many producers do we know of" reads both.
|
|
214
|
+
|
|
215
|
+
An area's actors can be tied to the platform entity they are:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
dcli brain actors list --area <areaId> --match --json # unlinked actors get name-matched candidates (admins)
|
|
219
|
+
dcli brain actors link --area <areaId> --actor <actorId> --entity <entityId>
|
|
220
|
+
dcli brain actors link --area <areaId> --actor <actorId> --unlink
|
|
221
|
+
```
|
|
222
|
+
|
|
191
223
|
## Legacy platform commands
|
|
192
224
|
|
|
193
225
|
Existing read/proposal workflows remain compatible:
|
package/dist/bin/dcli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { ApiError, DayOfWeekClient, toArrayBuffer } from "../client.js";
|
|
3
|
+
import { ApiError, DayOfWeekClient, toArrayBuffer, collectAdminPages } from "../client.js";
|
|
4
4
|
import { getToken, getApiUrl, saveConfig, loadConfig, saveCredential, deleteCredential } from "../config.js";
|
|
5
5
|
import { browserLogin } from "../auth/login.js";
|
|
6
6
|
import { parseBrainResource } from "../uri.js";
|
|
@@ -326,8 +326,29 @@ brainActors
|
|
|
326
326
|
.command("list")
|
|
327
327
|
.description("List an area's actors (active, sorted by name)")
|
|
328
328
|
.requiredOption("--area <areaId>", "Area id (from `brain list`)")
|
|
329
|
+
.option("--match", "Suggest matching platform entities for unlinked actors (admins)")
|
|
329
330
|
.action(async (opts) => {
|
|
330
|
-
output(await getClient().listBrainActors(opts.area));
|
|
331
|
+
output(await getClient().listBrainActors(opts.area, { match: opts.match }));
|
|
332
|
+
});
|
|
333
|
+
brainActors
|
|
334
|
+
.command("link")
|
|
335
|
+
.description("Link an actor to the platform entity it is (or --unlink)")
|
|
336
|
+
.requiredOption("--area <areaId>", "Area id (from `brain list`)")
|
|
337
|
+
.requiredOption("--actor <actorId>", "Actor id (from `brain actors list`)")
|
|
338
|
+
.option("--entity <entityId>", "Platform entity id (from `admin entities` or `read entities`)")
|
|
339
|
+
.option("--unlink", "Remove the current link")
|
|
340
|
+
.action(async (opts) => {
|
|
341
|
+
if (!opts.entity && !opts.unlink) {
|
|
342
|
+
throw new Error("Provide --entity <entityId> to link, or --unlink to remove the link");
|
|
343
|
+
}
|
|
344
|
+
if (opts.entity && opts.unlink) {
|
|
345
|
+
throw new Error("--entity and --unlink are mutually exclusive");
|
|
346
|
+
}
|
|
347
|
+
output(await getClient().linkBrainActor({
|
|
348
|
+
areaId: opts.area,
|
|
349
|
+
actorId: opts.actor,
|
|
350
|
+
entityId: opts.unlink ? null : opts.entity,
|
|
351
|
+
}));
|
|
331
352
|
});
|
|
332
353
|
brainActors
|
|
333
354
|
.command("add")
|
|
@@ -430,6 +451,71 @@ brainSource
|
|
|
430
451
|
}
|
|
431
452
|
output(await getClient().scopeBrainSource(sourceId, opts.actor ?? null));
|
|
432
453
|
});
|
|
454
|
+
const brainTags = brain
|
|
455
|
+
.command("tags")
|
|
456
|
+
.description("Work with an area's tag vocabulary and resource couplings");
|
|
457
|
+
brainTags
|
|
458
|
+
.command("list")
|
|
459
|
+
.description("List an area's tags (predefined hierarchical set + user-defined)")
|
|
460
|
+
.requiredOption("--area <areaId>", "Area id (from `brain list`)")
|
|
461
|
+
.action(async (opts) => {
|
|
462
|
+
output(await getClient().listBrainTags(opts.area));
|
|
463
|
+
});
|
|
464
|
+
brainTags
|
|
465
|
+
.command("add")
|
|
466
|
+
.description("Add a user-defined tag (idempotent on the derived key)")
|
|
467
|
+
.requiredOption("--area <areaId>", "Area id (from `brain list`)")
|
|
468
|
+
.requiredOption("--label <label>", "Tag label")
|
|
469
|
+
.option("--parent <key>", "Parent tag key (makes this a child tag)")
|
|
470
|
+
.option("--description <text>", "What the tag means")
|
|
471
|
+
.action(async (opts) => {
|
|
472
|
+
output(await getClient().createBrainTag({
|
|
473
|
+
areaId: opts.area,
|
|
474
|
+
label: opts.label,
|
|
475
|
+
parentKey: opts.parent,
|
|
476
|
+
description: opts.description,
|
|
477
|
+
}));
|
|
478
|
+
});
|
|
479
|
+
brainTags
|
|
480
|
+
.command("couplings")
|
|
481
|
+
.description("List tag/actor couplings — filter by actor, tag, source or note")
|
|
482
|
+
.requiredOption("--area <areaId>", "Area id (from `brain list`)")
|
|
483
|
+
.option("--tag <key>", "Resources coupled to this tag")
|
|
484
|
+
.option("--actor <actorId>", "Resources coupled to this actor")
|
|
485
|
+
.option("--source <sourceId>", "One source's tags and actors")
|
|
486
|
+
.option("--note <noteId>", "One note/page's tags and actors")
|
|
487
|
+
.action(async (opts) => {
|
|
488
|
+
output(await getClient().listBrainCouplings({
|
|
489
|
+
areaId: opts.area,
|
|
490
|
+
tag: opts.tag,
|
|
491
|
+
actor: opts.actor,
|
|
492
|
+
source: opts.source,
|
|
493
|
+
note: opts.note,
|
|
494
|
+
}));
|
|
495
|
+
});
|
|
496
|
+
brainTags
|
|
497
|
+
.command("couple")
|
|
498
|
+
.description("Couple a source/note to a tag or an actor (--remove uncouples)")
|
|
499
|
+
.option("--source <sourceId>", "Source to couple")
|
|
500
|
+
.option("--note <noteId>", "Note/page to couple")
|
|
501
|
+
.option("--tag <key>", "Tag key (from `brain tags list`)")
|
|
502
|
+
.option("--actor <actorId>", "Actor id (from `brain actors list`)")
|
|
503
|
+
.option("--remove", "Remove the coupling instead of adding it")
|
|
504
|
+
.action(async (opts) => {
|
|
505
|
+
if (Boolean(opts.source) === Boolean(opts.note)) {
|
|
506
|
+
throw new Error("Pass exactly one of --source <sourceId> or --note <noteId>");
|
|
507
|
+
}
|
|
508
|
+
if (Boolean(opts.tag) === Boolean(opts.actor)) {
|
|
509
|
+
throw new Error("Pass exactly one of --tag <key> or --actor <actorId>");
|
|
510
|
+
}
|
|
511
|
+
output(await getClient().coupleBrainResource({
|
|
512
|
+
sourceId: opts.source,
|
|
513
|
+
noteId: opts.note,
|
|
514
|
+
tagKey: opts.tag,
|
|
515
|
+
actorId: opts.actor,
|
|
516
|
+
remove: opts.remove,
|
|
517
|
+
}));
|
|
518
|
+
});
|
|
433
519
|
auth
|
|
434
520
|
.command("devices")
|
|
435
521
|
.description("List your agent tokens")
|
|
@@ -1284,22 +1370,86 @@ function registerAdminCommands() {
|
|
|
1284
1370
|
.description("Admin-only cross-org operations (DoW staff)");
|
|
1285
1371
|
admin
|
|
1286
1372
|
.command("entities")
|
|
1287
|
-
.description("List entities across all orgs with admin filters")
|
|
1288
|
-
.option("--type <entityType>", "Filter by
|
|
1373
|
+
.description("List entities across all orgs with admin filters (cursor-paged)")
|
|
1374
|
+
.option("--type <entityType>", "Filter by type name (farm, producer, restaurant, …)")
|
|
1375
|
+
.option("--category <category>", "Filter by type category (supply, demand, partner, financial, …)")
|
|
1376
|
+
.option("--role <role>", "Only entities holding an active role: customer, partner, investor, producer, market_intel")
|
|
1377
|
+
.option("--with-roles", "Attach each entity's active roles")
|
|
1289
1378
|
.option("--missing-location", "Only entities lacking metadata.places[].lat/lng")
|
|
1290
1379
|
.option("--search <query>", "Substring match on name")
|
|
1291
1380
|
.option("--org <slug>", "Restrict to a single org slug or ID")
|
|
1292
|
-
.option("--limit <count>", "
|
|
1381
|
+
.option("--limit <count>", "Rows scanned per page (default 200, max 2000)", parseInt)
|
|
1382
|
+
.option("--cursor <cursor>", "Continue from a previous page's nextCursor")
|
|
1383
|
+
.option("--all", "Walk every page and return all matches")
|
|
1293
1384
|
.action(async (opts) => {
|
|
1294
1385
|
const client = getClient();
|
|
1295
|
-
const
|
|
1386
|
+
const fetchPage = (cursor) => client.adminListEntities({
|
|
1296
1387
|
org: opts.org,
|
|
1297
1388
|
type: opts.type,
|
|
1389
|
+
category: opts.category,
|
|
1390
|
+
role: opts.role,
|
|
1391
|
+
withRoles: opts.withRoles,
|
|
1298
1392
|
missingLocation: opts.missingLocation,
|
|
1299
1393
|
search: opts.search,
|
|
1300
1394
|
limit: opts.limit,
|
|
1395
|
+
cursor,
|
|
1301
1396
|
});
|
|
1302
|
-
output(
|
|
1397
|
+
output(opts.all ? await collectAdminPages(fetchPage, opts.cursor) : await fetchPage(opts.cursor));
|
|
1398
|
+
});
|
|
1399
|
+
// The market intelligence register is a separate table from the entity
|
|
1400
|
+
// hierarchy: businesses recorded for research and prospecting, each with
|
|
1401
|
+
// coordinates. A platform-wide "how many do we know of" reads both.
|
|
1402
|
+
const marketIntel = admin
|
|
1403
|
+
.command("market-intel")
|
|
1404
|
+
.description("The market intelligence register — businesses recorded for research and prospecting");
|
|
1405
|
+
marketIntel
|
|
1406
|
+
.command("list")
|
|
1407
|
+
.description("List register rows (cursor-paged, newest first)")
|
|
1408
|
+
.option("--type <entityType>", "farm, restaurant, farm_shop, retail_store, distributor, processor, market, cooperative, supplier, customer, competitor, other")
|
|
1409
|
+
.option("--relationship <type>", "prospect, active_customer, supplier, partner, competitor, market_research, other")
|
|
1410
|
+
.option("--priority <priority>", "critical, high, medium, low, watching")
|
|
1411
|
+
.option("--stage <stage>", "Opportunity stage (research, qualified_lead, negotiation, …)")
|
|
1412
|
+
.option("--region <region>", "Exact match on region (case-insensitive)")
|
|
1413
|
+
.option("--search <query>", "Substring match on name")
|
|
1414
|
+
.option("--bbox <latMin,lngMin,latMax,lngMax>", "Only rows inside a lat/lng box")
|
|
1415
|
+
.option("--active", "Only active rows")
|
|
1416
|
+
.option("--inactive", "Only inactive rows")
|
|
1417
|
+
.option("--org <slug>", "Restrict to the owning org")
|
|
1418
|
+
.option("--limit <count>", "Rows scanned per page (default 200, max 2000)", parseInt)
|
|
1419
|
+
.option("--cursor <cursor>", "Continue from a previous page's nextCursor")
|
|
1420
|
+
.option("--all", "Walk every page and return all matches")
|
|
1421
|
+
.action(async (opts) => {
|
|
1422
|
+
if (opts.active && opts.inactive) {
|
|
1423
|
+
throw new Error("--active and --inactive are mutually exclusive");
|
|
1424
|
+
}
|
|
1425
|
+
const client = getClient();
|
|
1426
|
+
const fetchPage = (cursor) => client.adminListMarketIntel({
|
|
1427
|
+
org: opts.org,
|
|
1428
|
+
type: opts.type,
|
|
1429
|
+
relationship: opts.relationship,
|
|
1430
|
+
priority: opts.priority,
|
|
1431
|
+
stage: opts.stage,
|
|
1432
|
+
region: opts.region,
|
|
1433
|
+
search: opts.search,
|
|
1434
|
+
bbox: opts.bbox,
|
|
1435
|
+
active: opts.active ? true : opts.inactive ? false : undefined,
|
|
1436
|
+
limit: opts.limit,
|
|
1437
|
+
cursor,
|
|
1438
|
+
});
|
|
1439
|
+
output(opts.all ? await collectAdminPages(fetchPage, opts.cursor) : await fetchPage(opts.cursor));
|
|
1440
|
+
});
|
|
1441
|
+
marketIntel
|
|
1442
|
+
.command("get <id>")
|
|
1443
|
+
.description("One register record with its latest interactions")
|
|
1444
|
+
.action(async (id) => {
|
|
1445
|
+
output(await getClient().adminGetMarketIntel(id));
|
|
1446
|
+
});
|
|
1447
|
+
marketIntel
|
|
1448
|
+
.command("stats")
|
|
1449
|
+
.description("Counts by type, relationship, priority, stage, region and country")
|
|
1450
|
+
.option("--org <slug>", "Restrict to the owning org")
|
|
1451
|
+
.action(async (opts) => {
|
|
1452
|
+
output(await getClient().adminMarketIntelStats(opts.org));
|
|
1303
1453
|
});
|
|
1304
1454
|
admin
|
|
1305
1455
|
.command("proposals")
|
package/dist/bundle/dcli.cjs
CHANGED
|
@@ -4209,6 +4209,21 @@ var ApiError = class extends Error {
|
|
|
4209
4209
|
this.name = "ApiError";
|
|
4210
4210
|
}
|
|
4211
4211
|
};
|
|
4212
|
+
async function collectAdminPages(fetchPage, startCursor) {
|
|
4213
|
+
const results = [];
|
|
4214
|
+
let cursor = startCursor;
|
|
4215
|
+
let scanned = 0;
|
|
4216
|
+
let pages = 0;
|
|
4217
|
+
for (; ; ) {
|
|
4218
|
+
const page = await fetchPage(cursor);
|
|
4219
|
+
results.push(...page.results);
|
|
4220
|
+
scanned += page.scanned ?? page.results.length;
|
|
4221
|
+
pages += 1;
|
|
4222
|
+
if (page.isDone || !page.nextCursor) break;
|
|
4223
|
+
cursor = page.nextCursor;
|
|
4224
|
+
}
|
|
4225
|
+
return { results, total: results.length, scanned, isDone: true, nextCursor: null, truncated: false, pages };
|
|
4226
|
+
}
|
|
4212
4227
|
var DayOfWeekClient = class {
|
|
4213
4228
|
baseUrl;
|
|
4214
4229
|
token;
|
|
@@ -4427,12 +4442,43 @@ var DayOfWeekClient = class {
|
|
|
4427
4442
|
const params = new URLSearchParams();
|
|
4428
4443
|
if (opts?.org) params.set("org", opts.org);
|
|
4429
4444
|
if (opts?.type) params.set("type", opts.type);
|
|
4445
|
+
if (opts?.category) params.set("category", opts.category);
|
|
4446
|
+
if (opts?.role) params.set("role", opts.role);
|
|
4447
|
+
if (opts?.withRoles) params.set("with-roles", "1");
|
|
4430
4448
|
if (opts?.missingLocation) params.set("missing-location", "1");
|
|
4431
4449
|
if (opts?.search) params.set("search", opts.search);
|
|
4432
4450
|
if (opts?.limit) params.set("limit", String(opts.limit));
|
|
4451
|
+
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
4433
4452
|
const qs = params.toString();
|
|
4434
4453
|
return this.get(`/admin/entities${qs ? `?${qs}` : ""}`);
|
|
4435
4454
|
}
|
|
4455
|
+
// The market intelligence register: businesses recorded for research and
|
|
4456
|
+
// prospecting, each with coordinates — a separate table from the entity
|
|
4457
|
+
// hierarchy, so a platform-wide count has to read both.
|
|
4458
|
+
async adminListMarketIntel(opts) {
|
|
4459
|
+
const params = new URLSearchParams();
|
|
4460
|
+
if (opts?.org) params.set("org", opts.org);
|
|
4461
|
+
if (opts?.type) params.set("type", opts.type);
|
|
4462
|
+
if (opts?.relationship) params.set("relationship", opts.relationship);
|
|
4463
|
+
if (opts?.priority) params.set("priority", opts.priority);
|
|
4464
|
+
if (opts?.stage) params.set("stage", opts.stage);
|
|
4465
|
+
if (opts?.region) params.set("region", opts.region);
|
|
4466
|
+
if (opts?.search) params.set("search", opts.search);
|
|
4467
|
+
if (opts?.bbox) params.set("bbox", opts.bbox);
|
|
4468
|
+
if (opts?.active !== void 0) params.set("active", opts.active ? "1" : "0");
|
|
4469
|
+
if (opts?.limit) params.set("limit", String(opts.limit));
|
|
4470
|
+
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
4471
|
+
const qs = params.toString();
|
|
4472
|
+
return this.get(`/admin/market-intel${qs ? `?${qs}` : ""}`);
|
|
4473
|
+
}
|
|
4474
|
+
/** One register record in full, with its latest interactions. */
|
|
4475
|
+
async adminGetMarketIntel(id) {
|
|
4476
|
+
return this.get(`/admin/market-intel/${encodeURIComponent(id)}`);
|
|
4477
|
+
}
|
|
4478
|
+
/** Counts over the whole register — by type, relationship, priority, stage, region, country. */
|
|
4479
|
+
async adminMarketIntelStats(org) {
|
|
4480
|
+
return this.get(`/admin/market-intel/stats${org ? `?org=${encodeURIComponent(org)}` : ""}`);
|
|
4481
|
+
}
|
|
4436
4482
|
async adminListProposals(opts) {
|
|
4437
4483
|
const params = new URLSearchParams();
|
|
4438
4484
|
if (opts?.status) params.set("status", opts.status);
|
|
@@ -4472,9 +4518,19 @@ var DayOfWeekClient = class {
|
|
|
4472
4518
|
async listBrainSources(areaId) {
|
|
4473
4519
|
return this.get(`/brain/sources?area=${encodeURIComponent(areaId)}`);
|
|
4474
4520
|
}
|
|
4475
|
-
/**
|
|
4476
|
-
|
|
4477
|
-
|
|
4521
|
+
/**
|
|
4522
|
+
* An area's actors (people and organizations, the Actors tab). Active only.
|
|
4523
|
+
* With `match`, admins also get `candidates` on unlinked actors — the
|
|
4524
|
+
* platform entities whose name matches — to review before linking.
|
|
4525
|
+
*/
|
|
4526
|
+
async listBrainActors(areaId, opts) {
|
|
4527
|
+
const params = new URLSearchParams({ area: areaId });
|
|
4528
|
+
if (opts?.match) params.set("match", "1");
|
|
4529
|
+
return this.get(`/brain/actors?${params.toString()}`);
|
|
4530
|
+
}
|
|
4531
|
+
/** Link an actor to the platform entity it is; `entityId: null` unlinks. */
|
|
4532
|
+
async linkBrainActor(input) {
|
|
4533
|
+
return this.post("/brain/actors/link", input);
|
|
4478
4534
|
}
|
|
4479
4535
|
/**
|
|
4480
4536
|
* Create an actor in an area. Idempotent on (area, name): an existing active
|
|
@@ -4518,6 +4574,27 @@ var DayOfWeekClient = class {
|
|
|
4518
4574
|
async scopeBrainSource(sourceId, actorId) {
|
|
4519
4575
|
return this.patch(`/brain/sources/${encodeURIComponent(sourceId)}/actor`, { actorId });
|
|
4520
4576
|
}
|
|
4577
|
+
/** The area's tag vocabulary: predefined hierarchical set + user-defined. */
|
|
4578
|
+
async listBrainTags(areaId) {
|
|
4579
|
+
return this.get(`/brain/tags?area=${encodeURIComponent(areaId)}`);
|
|
4580
|
+
}
|
|
4581
|
+
/** Add a user-defined tag to an area's vocabulary. Idempotent on the key. */
|
|
4582
|
+
async createBrainTag(input) {
|
|
4583
|
+
return this.post("/brain/tags", input);
|
|
4584
|
+
}
|
|
4585
|
+
/** Tag/actor couplings, filterable by actor, tag, source or note. */
|
|
4586
|
+
async listBrainCouplings(input) {
|
|
4587
|
+
const params = new URLSearchParams({ area: input.areaId });
|
|
4588
|
+
if (input.tag) params.set("tag", input.tag);
|
|
4589
|
+
if (input.actor) params.set("actor", input.actor);
|
|
4590
|
+
if (input.source) params.set("source", input.source);
|
|
4591
|
+
if (input.note) params.set("note", input.note);
|
|
4592
|
+
return this.get(`/brain/tags/couplings?${params.toString()}`);
|
|
4593
|
+
}
|
|
4594
|
+
/** Couple (or uncouple, with remove) a source/note to a tag or an actor. */
|
|
4595
|
+
async coupleBrainResource(input) {
|
|
4596
|
+
return this.post("/brain/tags/couplings", input);
|
|
4597
|
+
}
|
|
4521
4598
|
/** Entities with an active customer role and no investor/partner/producer role. */
|
|
4522
4599
|
async adminCustomersOnly() {
|
|
4523
4600
|
return this.get("/admin/customers-only");
|
|
@@ -5144,7 +5221,7 @@ var import_promises4 = require("node:readline/promises");
|
|
|
5144
5221
|
// package.json
|
|
5145
5222
|
var package_default = {
|
|
5146
5223
|
name: "@dayofweek/dcli",
|
|
5147
|
-
version: "1.
|
|
5224
|
+
version: "1.11.0",
|
|
5148
5225
|
description: "CLI for the Day of Week AgTech platform \u2014 read data and submit proposals for review",
|
|
5149
5226
|
license: "MIT",
|
|
5150
5227
|
type: "module",
|
|
@@ -5475,8 +5552,21 @@ brainSource.command("download <uri>").description("Download exact original bytes
|
|
|
5475
5552
|
}));
|
|
5476
5553
|
});
|
|
5477
5554
|
var brainActors = brain.command("actors").description("Work with an area's actors (people and organizations)");
|
|
5478
|
-
brainActors.command("list").description("List an area's actors (active, sorted by name)").requiredOption("--area <areaId>", "Area id (from `brain list`)").action(async (opts) => {
|
|
5479
|
-
output(await getClient().listBrainActors(opts.area));
|
|
5555
|
+
brainActors.command("list").description("List an area's actors (active, sorted by name)").requiredOption("--area <areaId>", "Area id (from `brain list`)").option("--match", "Suggest matching platform entities for unlinked actors (admins)").action(async (opts) => {
|
|
5556
|
+
output(await getClient().listBrainActors(opts.area, { match: opts.match }));
|
|
5557
|
+
});
|
|
5558
|
+
brainActors.command("link").description("Link an actor to the platform entity it is (or --unlink)").requiredOption("--area <areaId>", "Area id (from `brain list`)").requiredOption("--actor <actorId>", "Actor id (from `brain actors list`)").option("--entity <entityId>", "Platform entity id (from `admin entities` or `read entities`)").option("--unlink", "Remove the current link").action(async (opts) => {
|
|
5559
|
+
if (!opts.entity && !opts.unlink) {
|
|
5560
|
+
throw new Error("Provide --entity <entityId> to link, or --unlink to remove the link");
|
|
5561
|
+
}
|
|
5562
|
+
if (opts.entity && opts.unlink) {
|
|
5563
|
+
throw new Error("--entity and --unlink are mutually exclusive");
|
|
5564
|
+
}
|
|
5565
|
+
output(await getClient().linkBrainActor({
|
|
5566
|
+
areaId: opts.area,
|
|
5567
|
+
actorId: opts.actor,
|
|
5568
|
+
entityId: opts.unlink ? null : opts.entity
|
|
5569
|
+
}));
|
|
5480
5570
|
});
|
|
5481
5571
|
brainActors.command("add").description("Add an actor to an area (idempotent on name)").requiredOption("--area <areaId>", "Area id (from `brain list`)").requiredOption("--name <name>", "Actor name (person or organization)").option("--kind <kind>", "person | organization", "organization").option("--role <text>", "Role or relationship in the project").option("--description <text>", "Longer free-text description").action(async (opts) => {
|
|
5482
5572
|
if (opts.kind !== "person" && opts.kind !== "organization") {
|
|
@@ -5532,6 +5622,42 @@ brainSource.command("scope <sourceId>").description("Scope a source to an actor
|
|
|
5532
5622
|
}
|
|
5533
5623
|
output(await getClient().scopeBrainSource(sourceId, opts.actor ?? null));
|
|
5534
5624
|
});
|
|
5625
|
+
var brainTags = brain.command("tags").description("Work with an area's tag vocabulary and resource couplings");
|
|
5626
|
+
brainTags.command("list").description("List an area's tags (predefined hierarchical set + user-defined)").requiredOption("--area <areaId>", "Area id (from `brain list`)").action(async (opts) => {
|
|
5627
|
+
output(await getClient().listBrainTags(opts.area));
|
|
5628
|
+
});
|
|
5629
|
+
brainTags.command("add").description("Add a user-defined tag (idempotent on the derived key)").requiredOption("--area <areaId>", "Area id (from `brain list`)").requiredOption("--label <label>", "Tag label").option("--parent <key>", "Parent tag key (makes this a child tag)").option("--description <text>", "What the tag means").action(async (opts) => {
|
|
5630
|
+
output(await getClient().createBrainTag({
|
|
5631
|
+
areaId: opts.area,
|
|
5632
|
+
label: opts.label,
|
|
5633
|
+
parentKey: opts.parent,
|
|
5634
|
+
description: opts.description
|
|
5635
|
+
}));
|
|
5636
|
+
});
|
|
5637
|
+
brainTags.command("couplings").description("List tag/actor couplings \u2014 filter by actor, tag, source or note").requiredOption("--area <areaId>", "Area id (from `brain list`)").option("--tag <key>", "Resources coupled to this tag").option("--actor <actorId>", "Resources coupled to this actor").option("--source <sourceId>", "One source's tags and actors").option("--note <noteId>", "One note/page's tags and actors").action(async (opts) => {
|
|
5638
|
+
output(await getClient().listBrainCouplings({
|
|
5639
|
+
areaId: opts.area,
|
|
5640
|
+
tag: opts.tag,
|
|
5641
|
+
actor: opts.actor,
|
|
5642
|
+
source: opts.source,
|
|
5643
|
+
note: opts.note
|
|
5644
|
+
}));
|
|
5645
|
+
});
|
|
5646
|
+
brainTags.command("couple").description("Couple a source/note to a tag or an actor (--remove uncouples)").option("--source <sourceId>", "Source to couple").option("--note <noteId>", "Note/page to couple").option("--tag <key>", "Tag key (from `brain tags list`)").option("--actor <actorId>", "Actor id (from `brain actors list`)").option("--remove", "Remove the coupling instead of adding it").action(async (opts) => {
|
|
5647
|
+
if (Boolean(opts.source) === Boolean(opts.note)) {
|
|
5648
|
+
throw new Error("Pass exactly one of --source <sourceId> or --note <noteId>");
|
|
5649
|
+
}
|
|
5650
|
+
if (Boolean(opts.tag) === Boolean(opts.actor)) {
|
|
5651
|
+
throw new Error("Pass exactly one of --tag <key> or --actor <actorId>");
|
|
5652
|
+
}
|
|
5653
|
+
output(await getClient().coupleBrainResource({
|
|
5654
|
+
sourceId: opts.source,
|
|
5655
|
+
noteId: opts.note,
|
|
5656
|
+
tagKey: opts.tag,
|
|
5657
|
+
actorId: opts.actor,
|
|
5658
|
+
remove: opts.remove
|
|
5659
|
+
}));
|
|
5660
|
+
});
|
|
5535
5661
|
auth.command("devices").description("List your agent tokens").action(async () => {
|
|
5536
5662
|
const client = getClient();
|
|
5537
5663
|
const devices = await client.listDevices();
|
|
@@ -6097,16 +6223,47 @@ function registerAdminCommands() {
|
|
|
6097
6223
|
const cfg = loadConfig();
|
|
6098
6224
|
if (!cfg.isAdmin) return;
|
|
6099
6225
|
const admin = program2.command("admin", { hidden: true }).description("Admin-only cross-org operations (DoW staff)");
|
|
6100
|
-
admin.command("entities").description("List entities across all orgs with admin filters").option("--type <entityType>", "Filter by
|
|
6226
|
+
admin.command("entities").description("List entities across all orgs with admin filters (cursor-paged)").option("--type <entityType>", "Filter by type name (farm, producer, restaurant, \u2026)").option("--category <category>", "Filter by type category (supply, demand, partner, financial, \u2026)").option("--role <role>", "Only entities holding an active role: customer, partner, investor, producer, market_intel").option("--with-roles", "Attach each entity's active roles").option("--missing-location", "Only entities lacking metadata.places[].lat/lng").option("--search <query>", "Substring match on name").option("--org <slug>", "Restrict to a single org slug or ID").option("--limit <count>", "Rows scanned per page (default 200, max 2000)", parseInt).option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Walk every page and return all matches").action(async (opts) => {
|
|
6101
6227
|
const client = getClient();
|
|
6102
|
-
const
|
|
6228
|
+
const fetchPage = (cursor) => client.adminListEntities({
|
|
6103
6229
|
org: opts.org,
|
|
6104
6230
|
type: opts.type,
|
|
6231
|
+
category: opts.category,
|
|
6232
|
+
role: opts.role,
|
|
6233
|
+
withRoles: opts.withRoles,
|
|
6105
6234
|
missingLocation: opts.missingLocation,
|
|
6106
6235
|
search: opts.search,
|
|
6107
|
-
limit: opts.limit
|
|
6236
|
+
limit: opts.limit,
|
|
6237
|
+
cursor
|
|
6108
6238
|
});
|
|
6109
|
-
output(
|
|
6239
|
+
output(opts.all ? await collectAdminPages(fetchPage, opts.cursor) : await fetchPage(opts.cursor));
|
|
6240
|
+
});
|
|
6241
|
+
const marketIntel = admin.command("market-intel").description("The market intelligence register \u2014 businesses recorded for research and prospecting");
|
|
6242
|
+
marketIntel.command("list").description("List register rows (cursor-paged, newest first)").option("--type <entityType>", "farm, restaurant, farm_shop, retail_store, distributor, processor, market, cooperative, supplier, customer, competitor, other").option("--relationship <type>", "prospect, active_customer, supplier, partner, competitor, market_research, other").option("--priority <priority>", "critical, high, medium, low, watching").option("--stage <stage>", "Opportunity stage (research, qualified_lead, negotiation, \u2026)").option("--region <region>", "Exact match on region (case-insensitive)").option("--search <query>", "Substring match on name").option("--bbox <latMin,lngMin,latMax,lngMax>", "Only rows inside a lat/lng box").option("--active", "Only active rows").option("--inactive", "Only inactive rows").option("--org <slug>", "Restrict to the owning org").option("--limit <count>", "Rows scanned per page (default 200, max 2000)", parseInt).option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Walk every page and return all matches").action(async (opts) => {
|
|
6243
|
+
if (opts.active && opts.inactive) {
|
|
6244
|
+
throw new Error("--active and --inactive are mutually exclusive");
|
|
6245
|
+
}
|
|
6246
|
+
const client = getClient();
|
|
6247
|
+
const fetchPage = (cursor) => client.adminListMarketIntel({
|
|
6248
|
+
org: opts.org,
|
|
6249
|
+
type: opts.type,
|
|
6250
|
+
relationship: opts.relationship,
|
|
6251
|
+
priority: opts.priority,
|
|
6252
|
+
stage: opts.stage,
|
|
6253
|
+
region: opts.region,
|
|
6254
|
+
search: opts.search,
|
|
6255
|
+
bbox: opts.bbox,
|
|
6256
|
+
active: opts.active ? true : opts.inactive ? false : void 0,
|
|
6257
|
+
limit: opts.limit,
|
|
6258
|
+
cursor
|
|
6259
|
+
});
|
|
6260
|
+
output(opts.all ? await collectAdminPages(fetchPage, opts.cursor) : await fetchPage(opts.cursor));
|
|
6261
|
+
});
|
|
6262
|
+
marketIntel.command("get <id>").description("One register record with its latest interactions").action(async (id) => {
|
|
6263
|
+
output(await getClient().adminGetMarketIntel(id));
|
|
6264
|
+
});
|
|
6265
|
+
marketIntel.command("stats").description("Counts by type, relationship, priority, stage, region and country").option("--org <slug>", "Restrict to the owning org").action(async (opts) => {
|
|
6266
|
+
output(await getClient().adminMarketIntelStats(opts.org));
|
|
6110
6267
|
});
|
|
6111
6268
|
admin.command("proposals").description("List agent proposals across all orgs").option("--status <status>", "Filter: pending, approved, rejected, failed").option("--source-agent <name>", "Filter by sourceAgent identifier").option("--limit <count>", "Max results (default 100)", parseInt).action(async (opts) => {
|
|
6112
6269
|
const client = getClient();
|
package/dist/client.d.ts
CHANGED
|
@@ -13,6 +13,27 @@ export declare class ApiError extends Error {
|
|
|
13
13
|
requestId?: string | undefined;
|
|
14
14
|
constructor(status: number, message: string, code?: string | undefined, requestId?: string | undefined);
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* One page of a cursor-paged admin read. Filters apply within the scanned
|
|
18
|
+
* page, so `results` can be shorter than the page size — even empty — while
|
|
19
|
+
* `isDone` is still false. `nextCursor` is the only way forward.
|
|
20
|
+
*/
|
|
21
|
+
export type AdminPage<T = any> = {
|
|
22
|
+
results: T[];
|
|
23
|
+
total: number;
|
|
24
|
+
scanned: number;
|
|
25
|
+
isDone: boolean;
|
|
26
|
+
nextCursor: string | null;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Walk a cursor-paged admin read to the end and return every match as one
|
|
31
|
+
* page. This is the loop behind `--all`; without it every caller re-invents
|
|
32
|
+
* it, or reaches for a giant `--limit` and still misses rows.
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectAdminPages<T>(fetchPage: (cursor?: string) => Promise<AdminPage<T>>, startCursor?: string): Promise<AdminPage<T> & {
|
|
35
|
+
pages: number;
|
|
36
|
+
}>;
|
|
16
37
|
export declare class DayOfWeekClient {
|
|
17
38
|
private baseUrl;
|
|
18
39
|
private token;
|
|
@@ -146,14 +167,31 @@ export declare class DayOfWeekClient {
|
|
|
146
167
|
adminListEntities(opts?: {
|
|
147
168
|
org?: string;
|
|
148
169
|
type?: string;
|
|
170
|
+
category?: string;
|
|
171
|
+
role?: string;
|
|
172
|
+
withRoles?: boolean;
|
|
149
173
|
missingLocation?: boolean;
|
|
150
174
|
search?: string;
|
|
151
175
|
limit?: number;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
176
|
+
cursor?: string;
|
|
177
|
+
}): Promise<AdminPage>;
|
|
178
|
+
adminListMarketIntel(opts?: {
|
|
179
|
+
org?: string;
|
|
180
|
+
type?: string;
|
|
181
|
+
relationship?: string;
|
|
182
|
+
priority?: string;
|
|
183
|
+
stage?: string;
|
|
184
|
+
region?: string;
|
|
185
|
+
search?: string;
|
|
186
|
+
bbox?: string;
|
|
187
|
+
active?: boolean;
|
|
188
|
+
limit?: number;
|
|
189
|
+
cursor?: string;
|
|
190
|
+
}): Promise<AdminPage>;
|
|
191
|
+
/** One register record in full, with its latest interactions. */
|
|
192
|
+
adminGetMarketIntel(id: string): Promise<any>;
|
|
193
|
+
/** Counts over the whole register — by type, relationship, priority, stage, region, country. */
|
|
194
|
+
adminMarketIntelStats(org?: string): Promise<any>;
|
|
157
195
|
adminListProposals(opts?: {
|
|
158
196
|
status?: string;
|
|
159
197
|
sourceAgent?: string;
|
|
@@ -199,8 +237,20 @@ export declare class DayOfWeekClient {
|
|
|
199
237
|
}): Promise<any>;
|
|
200
238
|
/** Area sources, metadata only, newest first — the freshness evidence. */
|
|
201
239
|
listBrainSources(areaId: string): Promise<any>;
|
|
202
|
-
/**
|
|
203
|
-
|
|
240
|
+
/**
|
|
241
|
+
* An area's actors (people and organizations, the Actors tab). Active only.
|
|
242
|
+
* With `match`, admins also get `candidates` on unlinked actors — the
|
|
243
|
+
* platform entities whose name matches — to review before linking.
|
|
244
|
+
*/
|
|
245
|
+
listBrainActors(areaId: string, opts?: {
|
|
246
|
+
match?: boolean;
|
|
247
|
+
}): Promise<any>;
|
|
248
|
+
/** Link an actor to the platform entity it is; `entityId: null` unlinks. */
|
|
249
|
+
linkBrainActor(input: {
|
|
250
|
+
areaId: string;
|
|
251
|
+
actorId: string;
|
|
252
|
+
entityId: string | null;
|
|
253
|
+
}): Promise<any>;
|
|
204
254
|
/**
|
|
205
255
|
* Create an actor in an area. Idempotent on (area, name): an existing active
|
|
206
256
|
* actor comes back with `created: false` instead of a duplicate, so imports
|
|
@@ -251,6 +301,31 @@ export declare class DayOfWeekClient {
|
|
|
251
301
|
}): Promise<any>;
|
|
252
302
|
/** Scope a source to an actor (actorId null returns it to area level). */
|
|
253
303
|
scopeBrainSource(sourceId: string, actorId: string | null): Promise<any>;
|
|
304
|
+
/** The area's tag vocabulary: predefined hierarchical set + user-defined. */
|
|
305
|
+
listBrainTags(areaId: string): Promise<any>;
|
|
306
|
+
/** Add a user-defined tag to an area's vocabulary. Idempotent on the key. */
|
|
307
|
+
createBrainTag(input: {
|
|
308
|
+
areaId: string;
|
|
309
|
+
label: string;
|
|
310
|
+
parentKey?: string;
|
|
311
|
+
description?: string;
|
|
312
|
+
}): Promise<any>;
|
|
313
|
+
/** Tag/actor couplings, filterable by actor, tag, source or note. */
|
|
314
|
+
listBrainCouplings(input: {
|
|
315
|
+
areaId: string;
|
|
316
|
+
tag?: string;
|
|
317
|
+
actor?: string;
|
|
318
|
+
source?: string;
|
|
319
|
+
note?: string;
|
|
320
|
+
}): Promise<any>;
|
|
321
|
+
/** Couple (or uncouple, with remove) a source/note to a tag or an actor. */
|
|
322
|
+
coupleBrainResource(input: {
|
|
323
|
+
sourceId?: string;
|
|
324
|
+
noteId?: string;
|
|
325
|
+
tagKey?: string;
|
|
326
|
+
actorId?: string;
|
|
327
|
+
remove?: boolean;
|
|
328
|
+
}): Promise<any>;
|
|
254
329
|
/** Entities with an active customer role and no investor/partner/producer role. */
|
|
255
330
|
adminCustomersOnly(): Promise<any>;
|
|
256
331
|
/** Tips mailed to press@mail.dayofweek.com, read by the press-scan skill. */
|
package/dist/client.js
CHANGED
|
@@ -29,6 +29,27 @@ export class ApiError extends Error {
|
|
|
29
29
|
this.name = "ApiError";
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Walk a cursor-paged admin read to the end and return every match as one
|
|
34
|
+
* page. This is the loop behind `--all`; without it every caller re-invents
|
|
35
|
+
* it, or reaches for a giant `--limit` and still misses rows.
|
|
36
|
+
*/
|
|
37
|
+
export async function collectAdminPages(fetchPage, startCursor) {
|
|
38
|
+
const results = [];
|
|
39
|
+
let cursor = startCursor;
|
|
40
|
+
let scanned = 0;
|
|
41
|
+
let pages = 0;
|
|
42
|
+
for (;;) {
|
|
43
|
+
const page = await fetchPage(cursor);
|
|
44
|
+
results.push(...page.results);
|
|
45
|
+
scanned += page.scanned ?? page.results.length;
|
|
46
|
+
pages += 1;
|
|
47
|
+
if (page.isDone || !page.nextCursor)
|
|
48
|
+
break;
|
|
49
|
+
cursor = page.nextCursor;
|
|
50
|
+
}
|
|
51
|
+
return { results, total: results.length, scanned, isDone: true, nextCursor: null, truncated: false, pages };
|
|
52
|
+
}
|
|
32
53
|
export class DayOfWeekClient {
|
|
33
54
|
baseUrl;
|
|
34
55
|
token;
|
|
@@ -280,15 +301,61 @@ export class DayOfWeekClient {
|
|
|
280
301
|
params.set("org", opts.org);
|
|
281
302
|
if (opts?.type)
|
|
282
303
|
params.set("type", opts.type);
|
|
304
|
+
if (opts?.category)
|
|
305
|
+
params.set("category", opts.category);
|
|
306
|
+
if (opts?.role)
|
|
307
|
+
params.set("role", opts.role);
|
|
308
|
+
if (opts?.withRoles)
|
|
309
|
+
params.set("with-roles", "1");
|
|
283
310
|
if (opts?.missingLocation)
|
|
284
311
|
params.set("missing-location", "1");
|
|
285
312
|
if (opts?.search)
|
|
286
313
|
params.set("search", opts.search);
|
|
287
314
|
if (opts?.limit)
|
|
288
315
|
params.set("limit", String(opts.limit));
|
|
316
|
+
if (opts?.cursor)
|
|
317
|
+
params.set("cursor", opts.cursor);
|
|
289
318
|
const qs = params.toString();
|
|
290
319
|
return this.get(`/admin/entities${qs ? `?${qs}` : ""}`);
|
|
291
320
|
}
|
|
321
|
+
// The market intelligence register: businesses recorded for research and
|
|
322
|
+
// prospecting, each with coordinates — a separate table from the entity
|
|
323
|
+
// hierarchy, so a platform-wide count has to read both.
|
|
324
|
+
async adminListMarketIntel(opts) {
|
|
325
|
+
const params = new URLSearchParams();
|
|
326
|
+
if (opts?.org)
|
|
327
|
+
params.set("org", opts.org);
|
|
328
|
+
if (opts?.type)
|
|
329
|
+
params.set("type", opts.type);
|
|
330
|
+
if (opts?.relationship)
|
|
331
|
+
params.set("relationship", opts.relationship);
|
|
332
|
+
if (opts?.priority)
|
|
333
|
+
params.set("priority", opts.priority);
|
|
334
|
+
if (opts?.stage)
|
|
335
|
+
params.set("stage", opts.stage);
|
|
336
|
+
if (opts?.region)
|
|
337
|
+
params.set("region", opts.region);
|
|
338
|
+
if (opts?.search)
|
|
339
|
+
params.set("search", opts.search);
|
|
340
|
+
if (opts?.bbox)
|
|
341
|
+
params.set("bbox", opts.bbox);
|
|
342
|
+
if (opts?.active !== undefined)
|
|
343
|
+
params.set("active", opts.active ? "1" : "0");
|
|
344
|
+
if (opts?.limit)
|
|
345
|
+
params.set("limit", String(opts.limit));
|
|
346
|
+
if (opts?.cursor)
|
|
347
|
+
params.set("cursor", opts.cursor);
|
|
348
|
+
const qs = params.toString();
|
|
349
|
+
return this.get(`/admin/market-intel${qs ? `?${qs}` : ""}`);
|
|
350
|
+
}
|
|
351
|
+
/** One register record in full, with its latest interactions. */
|
|
352
|
+
async adminGetMarketIntel(id) {
|
|
353
|
+
return this.get(`/admin/market-intel/${encodeURIComponent(id)}`);
|
|
354
|
+
}
|
|
355
|
+
/** Counts over the whole register — by type, relationship, priority, stage, region, country. */
|
|
356
|
+
async adminMarketIntelStats(org) {
|
|
357
|
+
return this.get(`/admin/market-intel/stats${org ? `?org=${encodeURIComponent(org)}` : ""}`);
|
|
358
|
+
}
|
|
292
359
|
async adminListProposals(opts) {
|
|
293
360
|
const params = new URLSearchParams();
|
|
294
361
|
if (opts?.status)
|
|
@@ -332,9 +399,20 @@ export class DayOfWeekClient {
|
|
|
332
399
|
async listBrainSources(areaId) {
|
|
333
400
|
return this.get(`/brain/sources?area=${encodeURIComponent(areaId)}`);
|
|
334
401
|
}
|
|
335
|
-
/**
|
|
336
|
-
|
|
337
|
-
|
|
402
|
+
/**
|
|
403
|
+
* An area's actors (people and organizations, the Actors tab). Active only.
|
|
404
|
+
* With `match`, admins also get `candidates` on unlinked actors — the
|
|
405
|
+
* platform entities whose name matches — to review before linking.
|
|
406
|
+
*/
|
|
407
|
+
async listBrainActors(areaId, opts) {
|
|
408
|
+
const params = new URLSearchParams({ area: areaId });
|
|
409
|
+
if (opts?.match)
|
|
410
|
+
params.set("match", "1");
|
|
411
|
+
return this.get(`/brain/actors?${params.toString()}`);
|
|
412
|
+
}
|
|
413
|
+
/** Link an actor to the platform entity it is; `entityId: null` unlinks. */
|
|
414
|
+
async linkBrainActor(input) {
|
|
415
|
+
return this.post("/brain/actors/link", input);
|
|
338
416
|
}
|
|
339
417
|
/**
|
|
340
418
|
* Create an actor in an area. Idempotent on (area, name): an existing active
|
|
@@ -378,6 +456,31 @@ export class DayOfWeekClient {
|
|
|
378
456
|
async scopeBrainSource(sourceId, actorId) {
|
|
379
457
|
return this.patch(`/brain/sources/${encodeURIComponent(sourceId)}/actor`, { actorId });
|
|
380
458
|
}
|
|
459
|
+
/** The area's tag vocabulary: predefined hierarchical set + user-defined. */
|
|
460
|
+
async listBrainTags(areaId) {
|
|
461
|
+
return this.get(`/brain/tags?area=${encodeURIComponent(areaId)}`);
|
|
462
|
+
}
|
|
463
|
+
/** Add a user-defined tag to an area's vocabulary. Idempotent on the key. */
|
|
464
|
+
async createBrainTag(input) {
|
|
465
|
+
return this.post("/brain/tags", input);
|
|
466
|
+
}
|
|
467
|
+
/** Tag/actor couplings, filterable by actor, tag, source or note. */
|
|
468
|
+
async listBrainCouplings(input) {
|
|
469
|
+
const params = new URLSearchParams({ area: input.areaId });
|
|
470
|
+
if (input.tag)
|
|
471
|
+
params.set("tag", input.tag);
|
|
472
|
+
if (input.actor)
|
|
473
|
+
params.set("actor", input.actor);
|
|
474
|
+
if (input.source)
|
|
475
|
+
params.set("source", input.source);
|
|
476
|
+
if (input.note)
|
|
477
|
+
params.set("note", input.note);
|
|
478
|
+
return this.get(`/brain/tags/couplings?${params.toString()}`);
|
|
479
|
+
}
|
|
480
|
+
/** Couple (or uncouple, with remove) a source/note to a tag or an actor. */
|
|
481
|
+
async coupleBrainResource(input) {
|
|
482
|
+
return this.post("/brain/tags/couplings", input);
|
|
483
|
+
}
|
|
381
484
|
/** Entities with an active customer role and no investor/partner/producer role. */
|
|
382
485
|
async adminCustomersOnly() {
|
|
383
486
|
return this.get("/admin/customers-only");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dayofweek/dcli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI for the Day of Week AgTech platform
|
|
3
|
+
"version": "1.11.0",
|
|
4
|
+
"description": "CLI for the Day of Week AgTech platform — read data and submit proposals for review",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|