@dayofweek/dcli 1.10.0 → 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 +92 -7
- package/dist/bundle/dcli.cjs +110 -10
- package/dist/client.d.ts +57 -7
- package/dist/client.js +81 -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")
|
|
@@ -1349,22 +1370,86 @@ function registerAdminCommands() {
|
|
|
1349
1370
|
.description("Admin-only cross-org operations (DoW staff)");
|
|
1350
1371
|
admin
|
|
1351
1372
|
.command("entities")
|
|
1352
|
-
.description("List entities across all orgs with admin filters")
|
|
1353
|
-
.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")
|
|
1354
1378
|
.option("--missing-location", "Only entities lacking metadata.places[].lat/lng")
|
|
1355
1379
|
.option("--search <query>", "Substring match on name")
|
|
1356
1380
|
.option("--org <slug>", "Restrict to a single org slug or ID")
|
|
1357
|
-
.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")
|
|
1358
1384
|
.action(async (opts) => {
|
|
1359
1385
|
const client = getClient();
|
|
1360
|
-
const
|
|
1386
|
+
const fetchPage = (cursor) => client.adminListEntities({
|
|
1361
1387
|
org: opts.org,
|
|
1362
1388
|
type: opts.type,
|
|
1389
|
+
category: opts.category,
|
|
1390
|
+
role: opts.role,
|
|
1391
|
+
withRoles: opts.withRoles,
|
|
1363
1392
|
missingLocation: opts.missingLocation,
|
|
1364
1393
|
search: opts.search,
|
|
1365
1394
|
limit: opts.limit,
|
|
1395
|
+
cursor,
|
|
1366
1396
|
});
|
|
1367
|
-
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));
|
|
1368
1453
|
});
|
|
1369
1454
|
admin
|
|
1370
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
|
|
@@ -5165,7 +5221,7 @@ var import_promises4 = require("node:readline/promises");
|
|
|
5165
5221
|
// package.json
|
|
5166
5222
|
var package_default = {
|
|
5167
5223
|
name: "@dayofweek/dcli",
|
|
5168
|
-
version: "1.
|
|
5224
|
+
version: "1.11.0",
|
|
5169
5225
|
description: "CLI for the Day of Week AgTech platform \u2014 read data and submit proposals for review",
|
|
5170
5226
|
license: "MIT",
|
|
5171
5227
|
type: "module",
|
|
@@ -5496,8 +5552,21 @@ brainSource.command("download <uri>").description("Download exact original bytes
|
|
|
5496
5552
|
}));
|
|
5497
5553
|
});
|
|
5498
5554
|
var brainActors = brain.command("actors").description("Work with an area's actors (people and organizations)");
|
|
5499
|
-
brainActors.command("list").description("List an area's actors (active, sorted by name)").requiredOption("--area <areaId>", "Area id (from `brain list`)").action(async (opts) => {
|
|
5500
|
-
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
|
+
}));
|
|
5501
5570
|
});
|
|
5502
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) => {
|
|
5503
5572
|
if (opts.kind !== "person" && opts.kind !== "organization") {
|
|
@@ -6154,16 +6223,47 @@ function registerAdminCommands() {
|
|
|
6154
6223
|
const cfg = loadConfig();
|
|
6155
6224
|
if (!cfg.isAdmin) return;
|
|
6156
6225
|
const admin = program2.command("admin", { hidden: true }).description("Admin-only cross-org operations (DoW staff)");
|
|
6157
|
-
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) => {
|
|
6158
6227
|
const client = getClient();
|
|
6159
|
-
const
|
|
6228
|
+
const fetchPage = (cursor) => client.adminListEntities({
|
|
6160
6229
|
org: opts.org,
|
|
6161
6230
|
type: opts.type,
|
|
6231
|
+
category: opts.category,
|
|
6232
|
+
role: opts.role,
|
|
6233
|
+
withRoles: opts.withRoles,
|
|
6162
6234
|
missingLocation: opts.missingLocation,
|
|
6163
6235
|
search: opts.search,
|
|
6164
|
-
limit: opts.limit
|
|
6236
|
+
limit: opts.limit,
|
|
6237
|
+
cursor
|
|
6165
6238
|
});
|
|
6166
|
-
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));
|
|
6167
6267
|
});
|
|
6168
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) => {
|
|
6169
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
|
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
|
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": {
|