connection-centric 0.4.0 → 0.6.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/GUIDE.md +1 -0
- package/commands.mjs +47 -12
- package/package.json +1 -1
package/GUIDE.md
CHANGED
|
@@ -48,6 +48,7 @@ A tool lapses after 90 days unused. The person disconnects it from Me, or with `
|
|
|
48
48
|
- **active-day**: a day the person did something kept. Shown to them alone, never ranked.
|
|
49
49
|
- **me**: the person themselves: their display name, photo, and their own switch for the assistant.
|
|
50
50
|
- **assistant**: Centric's, which answers a person's questions from what they can see and corrects a message they are writing. It is not a connected tool: it never acts, and asking it needs the person's own switch on.
|
|
51
|
+
- **assistant-limit**: what one person's assistant may cost per 5 hours and per week. `show` says how much of yours is used and when it refills; an Operator lists everyone's spend and raises one person's limit. A question past it is refused with when to try again.
|
|
51
52
|
- **conversation**: a person's questions to the assistant and its answers, theirs alone; deleted outright.
|
|
52
53
|
- **memory**: a line the assistant keeps about a person because they told it to, read before each of their questions; theirs alone to see, add and delete.
|
|
53
54
|
- **linked-system**: a firm's own system, such as its stores or accounts, that its Company's admins link so its people's assistant can read from it, through its tools that only read.
|
package/commands.mjs
CHANGED
|
@@ -18,7 +18,7 @@ export const CLI_VERSION = JSON.parse(readFileSync(new URL("./package.json", imp
|
|
|
18
18
|
export const VERBS = ["list", "show", "make", "edit", "delete", "archive", "unarchive", "close", "reopen", "not-yet", "grant", "revoke", "appoint", "remove",
|
|
19
19
|
"issue", "acknowledge", "add", "revise", "restore", "download", "messages", "mark-read", "send", "forward", "react", "unreact", "history", "readers",
|
|
20
20
|
"pin", "unpin", "mute", "unmute", "private", "open-to-company", "set-picture", "remove-picture", "set-photo", "remove-photo",
|
|
21
|
-
"approve", "reject", "withdraw", "rotate-key", "dormant", "reactivate", "report", "record-pack", "audit", "question", "correct", "disconnect"];
|
|
21
|
+
"approve", "reject", "withdraw", "rotate-key", "dormant", "reactivate", "report", "record-pack", "audit", "question", "correct", "disconnect", "transcript"];
|
|
22
22
|
|
|
23
23
|
// What a value names, read by kind. Things are found by id, or by an exact name only one thing the person can see has.
|
|
24
24
|
const THINGS = ["project", "company", "container", "channel", "task", "document", "folder", "zone", "identity", "membership", "admin", "connected-tool"];
|
|
@@ -40,7 +40,7 @@ const enc = encodeURIComponent;
|
|
|
40
40
|
const qs = (o) => { const p = new URLSearchParams(Object.entries(o).filter(([, v]) => v != null && v !== false).map(([k, v]) => [k, v === true ? "1" : String(v)])); return p.size ? `?${p}` : ""; };
|
|
41
41
|
// Rule 9: what a list or a place prints, the same everywhere.
|
|
42
42
|
const listed = (items, more = null) => ({ items, more });
|
|
43
|
-
const meShown = async (c) => { const m = await c.get("/api/me"); return { identity: m.identity, assistant: m.assistant, opened_directs: m.opened_directs, application: m.application, operator: m.operator, containers: m.containers.length }; };
|
|
43
|
+
const meShown = async (c) => { const m = await c.get("/api/me"); return { identity: m.identity, assistant: m.assistant, speech: m.speech, claude: m.claude, opened_directs: m.opened_directs, application: m.application, operator: m.operator, containers: m.containers.length }; };
|
|
44
44
|
const newest = (x, y) => (y.last?.id ?? 0) - (x.last?.id ?? 0); // a place with nothing in it yet goes last
|
|
45
45
|
|
|
46
46
|
// Rule 11: a moment is shown in India's time, ISO 8601 with +05:30; a day stays YYYY-MM-DD. Field names stay the API's.
|
|
@@ -170,9 +170,16 @@ export const COMMANDS = [
|
|
|
170
170
|
} },
|
|
171
171
|
|
|
172
172
|
// ── work ──
|
|
173
|
-
{ noun: "task", verb: "list", flags: { in: IN,
|
|
174
|
-
|
|
173
|
+
{ noun: "task", verb: "list", flags: { in: { ...IN, need: false }, mine: { kind: "bool", about: "your own open tasks in every project instead, as Home groups them: needs you, overdue, this week, waiting; 200 at most, with the counts of each" },
|
|
174
|
+
state: { kind: "choice", of: ["open", "closed"] }, zone: { kind: "zone" }, before: { kind: "words", about: "with --state closed: the next page, from the more: of the one before" } },
|
|
175
|
+
about: "A container's tasks: open ones first, soonest due, then the most important; done ones newest first, a page at a time. Or, with --mine, your own across every project.",
|
|
175
176
|
run: async (c, a) => {
|
|
177
|
+
if (a.mine) {
|
|
178
|
+
if (a.in || a.state || a.zone || a.before) refuse("--mine is every project's open tasks of yours: leave --in, --state, --zone and --before off");
|
|
179
|
+
const r = await c.get("/api/me/tasks");
|
|
180
|
+
return { ...listed(r.tasks), counts: r.counts };
|
|
181
|
+
}
|
|
182
|
+
if (!a.in) refuse("task list needs --in, or --mine");
|
|
176
183
|
if (a.before && a.state !== "closed") refuse("--before pages done tasks: with --state closed");
|
|
177
184
|
const l = await c.get(`/api/containers/${a.in}/tasks${qs({ state: a.state, zone_id: a.zone, before: a.before })}`);
|
|
178
185
|
return listed(l, a.state === "closed" && l.length >= 100 ? l.at(-1).done_key : null); // the server's page of done ones (DONE_PAGE)
|
|
@@ -216,8 +223,8 @@ export const COMMANDS = [
|
|
|
216
223
|
run: (c, a) => (a.before && a.after ? refuse("--before or --after, not both") : c.get(`/api/messages${qs({ channel: a.channel, before: a.before, after: a.after, limit: a.limit, unread: a.unread, search: a.search })}`)) },
|
|
217
224
|
{ noun: "zone", verb: "list", flags: { in: { ...IN, kind: "project" } }, about: "A Project's zones.",
|
|
218
225
|
run: async (c, a) => listed((await c.get(`/api/containers/${a.in}?documents=0`)).zones) },
|
|
219
|
-
{ noun: "document", verb: "list", flags: { in: IN, folder: { kind: "folder", about: "only what is in this folder" } }, about: "A container's documents, newest revision first.",
|
|
220
|
-
run: async (c, a) =>
|
|
226
|
+
{ noun: "document", verb: "list", flags: { in: IN, folder: { kind: "folder", about: "only what is in this folder" }, before: { kind: "words", about: "the next page, from the more: of the one before" } }, about: "A container's documents, newest revision first, fifty at a time.",
|
|
227
|
+
run: async (c, a) => { const p = await c.get(`/api/containers/${a.in}/documents${qs({ sort: "newest", folder: a.folder, before: a.before })}`); return listed(p.items, p.more); } },
|
|
221
228
|
{ noun: "document", verb: "show", target: { name: "document", kind: "document" }, flags: { contents: { kind: "bool", about: "with what is inside its current revision" } },
|
|
222
229
|
about: "One document: its revisions, issuances and acknowledgements.",
|
|
223
230
|
run: (c, a) => c.get(`/api/documents/${a.document}${a.contents ? "?contents=1" : ""}`) },
|
|
@@ -355,6 +362,17 @@ export const COMMANDS = [
|
|
|
355
362
|
run: (c, a) => (a.name === undefined && a.folder === undefined ? refuse("say what changes: --name or --folder") : c.patch(`/api/documents/${a.document}`, { name: a.name, folder_id: a.folder })) },
|
|
356
363
|
{ noun: "document", verb: "delete", target: { name: "document", kind: "document" }, flags: {}, about: "Deletes a document, leaving a tombstone: staff only.",
|
|
357
364
|
run: (c, a) => c.del(`/api/documents/${a.document}`) },
|
|
365
|
+
// A voice note's Transcript (CONTEXT.md, issue 307): asked for by someone with Speech on, made once and kept for everyone
|
|
366
|
+
// who can hear the note; taken away by the note's sender alone, which the app asks about first (--dry-run).
|
|
367
|
+
{ noun: "document", verb: "transcript", target: { name: "document", kind: "document" }, flags: { remove: { kind: "bool", about: "take it away: the note's sender alone" }, "dry-run": { ...DRY, about: "with --remove: what would go" } },
|
|
368
|
+
about: "A voice note's Transcript: asked for, and made once, with Speech on; with --remove, taken away by the note's sender.",
|
|
369
|
+
run: async (c, a) => {
|
|
370
|
+
if (a["dry-run"] && !a.remove) refuse("--dry-run is for --remove");
|
|
371
|
+
if (!a.remove) return c.post(`/api/documents/${a.document}/transcript`);
|
|
372
|
+
if (!a["dry-run"]) return c.del(`/api/documents/${a.document}/transcript`);
|
|
373
|
+
const tr = revisionOf(await c.get(`/api/documents/${a.document}`))?.transcript;
|
|
374
|
+
return tr ? { dry_run: true, would_remove: { document: a.document, transcript: tr.text } } : refuse("it has no transcript to remove");
|
|
375
|
+
} },
|
|
358
376
|
{ noun: "document", verb: "download", target: { name: "document", kind: "document" }, flags: { revision: { kind: "int", about: "which revision; the current one if left out" }, out: { kind: "path", about: "where to write it; the file's own name here if left out" } },
|
|
359
377
|
about: "Writes the exact bytes kept for a revision to this computer, after checking them against the hash Centric kept.",
|
|
360
378
|
async run(c, a) {
|
|
@@ -366,8 +384,8 @@ export const COMMANDS = [
|
|
|
366
384
|
await c.save(path, bytes);
|
|
367
385
|
return { document: d.document.id, revision: r.n, path, bytes: bytes.length, sha256_hex };
|
|
368
386
|
} },
|
|
369
|
-
{ noun: "folder", verb: "make", flags: { in: IN, name: { kind: "words", need: true }, parent: { kind: "folder", about: "the folder it goes in; the top of Docs if left out" } }, about: "Makes a folder among a container's documents: staff only.",
|
|
370
|
-
run: (c, a) => c.post(`/api/containers/${a.in}/folders`, { name: a.name, parent_id: a.parent }) },
|
|
387
|
+
{ noun: "folder", verb: "make", flags: { in: IN, name: { kind: "words", need: true }, parent: { kind: "folder", about: "the folder it goes in; the top of Docs if left out" }, channel: { kind: "channel", about: "a private channel you read: the folder is its own, for its documents alone" } }, about: "Makes a folder among a container's documents: staff only.",
|
|
388
|
+
run: (c, a) => c.post(`/api/containers/${a.in}/folders`, { name: a.name, parent_id: a.parent, channel_id: a.channel }) },
|
|
371
389
|
{ noun: "folder", verb: "edit", target: { name: "folder", kind: "folder" }, flags: { name: { kind: "words" }, parent: { kind: "folder", none: true, about: "a folder, or none for the top of Docs" } },
|
|
372
390
|
about: "Renames a folder, or moves it into another; never into itself.",
|
|
373
391
|
run: (c, a) => (a.name === undefined && a.parent === undefined ? refuse("say what changes: --name or --parent") : c.patch(`/api/folders/${a.folder}`, { name: a.name, parent_id: a.parent })) },
|
|
@@ -463,12 +481,16 @@ export const COMMANDS = [
|
|
|
463
481
|
|
|
464
482
|
// ── me and the assistant ──
|
|
465
483
|
{ noun: "me", verb: "edit", flags: { "display-name": { kind: "words", about: "what you call yourself, everywhere you work" }, assistant: { kind: "choice", of: ["on", "off"], about: "your own switch for Centric's assistant (ADR 0002)" },
|
|
466
|
-
language: { kind: "choice", of: ["en", "gu", "hi"], about: "what your assistant and your digest answer in" }
|
|
467
|
-
|
|
484
|
+
language: { kind: "choice", of: ["en", "gu", "hi"], about: "what your assistant and your digest answer in" },
|
|
485
|
+
speech: { kind: "choice", of: ["on", "off"], about: "your own switch for read aloud, transcripts and dictation (ADR 0019)" },
|
|
486
|
+
claude: { kind: "choice", of: ["on", "off"], about: "your own switch for Claude on your questions and reports; Anthropic keeps what it is sent up to 30 days (ADR 0020)" } },
|
|
487
|
+
about: "Changes your display name, switches your assistant, speech or Claude on or off, or sets the assistant's language.",
|
|
468
488
|
async run(c, a) {
|
|
469
|
-
if (a["display-name"] === undefined && a.assistant === undefined && a.language === undefined) refuse("say what changes: --display-name, --assistant or --
|
|
489
|
+
if (a["display-name"] === undefined && a.assistant === undefined && a.language === undefined && a.speech === undefined && a.claude === undefined) refuse("say what changes: --display-name, --assistant, --language, --speech or --claude");
|
|
470
490
|
if (a["display-name"] !== undefined) await c.patch("/api/me", { display_name: a["display-name"] });
|
|
471
491
|
if (a.assistant !== undefined || a.language !== undefined) await c.patch("/api/me/assistant", { on: a.assistant && a.assistant === "on", lang: a.language });
|
|
492
|
+
if (a.speech !== undefined) await c.patch("/api/me/speech", { on: a.speech === "on" });
|
|
493
|
+
if (a.claude !== undefined) await c.patch("/api/me/claude", { on: a.claude === "on" });
|
|
472
494
|
return meShown(c);
|
|
473
495
|
} },
|
|
474
496
|
{ noun: "me", verb: "set-photo", flags: { path: { ...PATH, about: "a square JPEG, PNG or WebP, 300 KB at most" } }, about: "Sets the photo shown beside your name.",
|
|
@@ -476,7 +498,7 @@ export const COMMANDS = [
|
|
|
476
498
|
{ noun: "me", verb: "remove-photo", flags: {}, about: "Takes your photo away; it is gone, kept nowhere.", run: (c) => c.del("/api/me/photo") },
|
|
477
499
|
{ noun: "assistant", verb: "question", target: { name: "text", kind: "words" }, flags: { conversation: { kind: "id", about: "to go on from one" }, focus: { kind: "focus", about: "a Project, Company or channel for a new conversation to keep to" },
|
|
478
500
|
path: { kind: "path", about: "a file for it to read, kept with the conversation" } },
|
|
479
|
-
about: "Asks Centric's assistant, which answers from what you can see. Only while your own switch is on; it counts against
|
|
501
|
+
about: "Asks Centric's assistant, which answers from what you can see. Only while your own switch is on; it counts against your Assistant limit.",
|
|
480
502
|
async run(c, a) {
|
|
481
503
|
const up = a.path && (await c.upload(a.path)); // uploaded as the person, linked to no message: theirs alone
|
|
482
504
|
return c.post("/api/assist", { q: a.text, conversation: a.conversation, focus: a.focus, ...(up && { files: [{ path: up.path, name: up.name }] }) });
|
|
@@ -484,6 +506,19 @@ export const COMMANDS = [
|
|
|
484
506
|
{ noun: "assistant", verb: "correct", target: { name: "text", kind: "words" }, flags: {},
|
|
485
507
|
about: "The assistant puts a message's spelling, grammar and punctuation right; it is still yours to send.",
|
|
486
508
|
run: (c, a) => c.post("/api/assist/correct", { text: a.text }) },
|
|
509
|
+
// The Assistant limit (CONTEXT.md, issue 448): how much of one's own is used; an Operator's view of every person's
|
|
510
|
+
// spend, and raising one person's limit.
|
|
511
|
+
{ noun: "assistant-limit", verb: "show", flags: {}, about: "How much of your Assistant limit is used, and when it refills.",
|
|
512
|
+
run: (c) => c.get("/api/assistant/limit") },
|
|
513
|
+
{ noun: "assistant-limit", verb: "list", flags: {}, about: "Each person's assistant spend in rupees. Operators only.",
|
|
514
|
+
run: (c) => c.get("/api/ops/assistant-spend") },
|
|
515
|
+
{ noun: "assistant-limit", verb: "edit", target: { name: "identity", kind: "identity" },
|
|
516
|
+
flags: { add: { kind: "int", about: "rupees now" }, "five-hours": { kind: "int", about: "₹ per 5 hours" }, week: { kind: "int", about: "₹ per week" } },
|
|
517
|
+
about: "Raises one person's Assistant limit. Operators only.",
|
|
518
|
+
run: (c, a) => {
|
|
519
|
+
if (a.add === undefined && a["five-hours"] === undefined && a.week === undefined) refuse("say what changes: --add, --five-hours or --week");
|
|
520
|
+
return c.post(`/api/ops/assistant-limit/${a.identity}`, { credit_inr: a.add, session_inr: a["five-hours"], week_inr: a.week });
|
|
521
|
+
} },
|
|
487
522
|
{ noun: "conversation", verb: "delete", target: { name: "conversation", kind: "id" }, flags: {}, about: "Deletes a conversation with the assistant outright; it leaves no tombstone.",
|
|
488
523
|
run: (c, a) => c.del(`/api/assistant/conversations/${a.conversation}`) },
|
|
489
524
|
// A firm's own system its assistant reads from (CONTEXT.md Linked system): its admins link and remove one; its people list them.
|
package/package.json
CHANGED