@wirecat/cli-core 0.544.0 → 0.546.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/CHANGELOG.md +88 -0
- package/dist/calendar/busy.js +4 -1
- package/dist/calendar/cli/answers.d.ts +31 -0
- package/dist/calendar/cli/answers.js +8 -0
- package/dist/calendar/cli/index.d.ts +6 -0
- package/dist/calendar/cli/index.js +43 -4
- package/dist/calendar/cli/remind.js +2 -1
- package/dist/calendar/expand.d.ts +17 -0
- package/dist/calendar/expand.js +340 -0
- package/dist/calendar/feeds.d.ts +96 -0
- package/dist/calendar/feeds.js +324 -0
- package/dist/calendar/ics.d.ts +15 -4
- package/dist/calendar/ics.js +18 -7
- package/dist/calendar/index.d.ts +3 -1
- package/dist/calendar/index.js +2 -0
- package/dist/calendar/server-search.js +3 -1
- package/dist/calendar/writes.js +15 -4
- package/dist/cli/after-dashes.d.ts +16 -0
- package/dist/cli/after-dashes.js +41 -0
- package/dist/cli/bot/context.d.ts +1 -1
- package/dist/cli/bot/context.js +9 -1
- package/dist/cli/bot/messages.js +2 -0
- package/dist/cli/bot/setup.js +1 -0
- package/dist/cli/contract-templates.js +2 -2
- package/dist/cli/entry.d.ts +2 -0
- package/dist/cli/entry.js +3 -2
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/messenger/admin-commands.js +5 -1
- package/dist/cli/messenger/admin-contacts-command.js +5 -1
- package/dist/cli/messenger/admin-folders-command.js +5 -1
- package/dist/cli/messenger/archive-commands.js +20 -2
- package/dist/cli/messenger/backfill-command.js +13 -2
- package/dist/cli/messenger/complete-command.js +1 -0
- package/dist/cli/messenger/context.js +3 -0
- package/dist/cli/messenger/history-gaps.d.ts +12 -0
- package/dist/cli/messenger/history-gaps.js +41 -0
- package/dist/cli/messenger/messages-command.js +9 -22
- package/dist/cli/messenger/messages-edit-command.js +1 -0
- package/dist/cli/messenger/messages-press-command.js +1 -0
- package/dist/cli/messenger/messages-search-command.js +5 -1
- package/dist/cli/messenger/messages-send-command.js +1 -0
- package/dist/cli/messenger/observed.js +2 -2
- package/dist/cli/messenger/polls-command.js +1 -0
- package/dist/cli/messenger/private-people-command.js +5 -1
- package/dist/cli/messenger/search-answers.d.ts +3 -2
- package/dist/cli/messenger/search-answers.js +12 -5
- package/dist/cli/messenger/search-command.js +3 -1
- package/dist/cli/messenger/stored.d.ts +6 -1
- package/dist/cli/messenger/stored.js +94 -5
- package/dist/cli/messenger/topics-command.js +1 -1
- package/dist/cli/program.js +17 -6
- package/dist/cli/schedule/schedule-command.js +1 -0
- package/dist/cli/settings.d.ts +9 -0
- package/dist/cli/settings.js +20 -0
- package/dist/cli/shared-command-exports.d.ts +24 -0
- package/dist/cli/steps.js +1 -0
- package/dist/domain/message-link.d.ts +3 -0
- package/dist/domain/message-link.js +9 -14
- package/dist/domain/money-words.d.ts +11 -0
- package/dist/domain/money-words.js +38 -0
- package/dist/domain/providers.d.ts +2 -0
- package/dist/domain/providers.js +15 -3
- package/dist/domain/ref-arguments.d.ts +31 -0
- package/dist/domain/ref-arguments.js +79 -0
- package/dist/domain/references.d.ts +20 -2
- package/dist/domain/references.js +132 -47
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/message-transfer.d.ts +4 -0
- package/dist/entities/message-transfer.js +2 -0
- package/dist/errors/reasons.js +18 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/oauth/client-file.d.ts +14 -0
- package/dist/oauth/client-file.js +28 -0
- package/dist/oauth/index.d.ts +1 -0
- package/dist/oauth/index.js +1 -0
- package/dist/ports/messenger.d.ts +3 -2
- package/dist/resolve.d.ts +3 -1
- package/dist/resolve.js +8 -0
- package/dist/search/task-signature.d.ts +5 -1
- package/dist/search/task-signature.js +27 -9
- package/dist/services/agenda.d.ts +5 -0
- package/dist/services/agenda.js +3 -1
- package/dist/services/history-limits.d.ts +45 -0
- package/dist/services/history-limits.js +50 -0
- package/dist/services/index.d.ts +5 -1
- package/dist/services/index.js +5 -1
- package/dist/services/message-resolution.d.ts +4 -1
- package/dist/services/message-resolution.js +19 -3
- package/dist/services/messages.js +15 -13
- package/dist/services/money-words.d.ts +3 -0
- package/dist/services/money-words.js +5 -0
- package/dist/services/questions.d.ts +7 -2
- package/dist/services/questions.js +13 -4
- package/dist/services/stored-project-gathering.d.ts +9 -0
- package/dist/services/stored-project-gathering.js +9 -4
- package/dist/services/task-categories.d.ts +2 -0
- package/dist/services/task-categories.js +19 -8
- package/dist/store/chats.d.ts +10 -0
- package/dist/store/chats.js +25 -0
- package/dist/store/sqlite/chats.js +2 -1
- package/dist/store/sqlite/evidence-sources.js +2 -2
- package/dist/store/sqlite/history-skips.d.ts +24 -0
- package/dist/store/sqlite/history-skips.js +95 -0
- package/dist/store/sqlite/identities.js +1 -1
- package/dist/store/sqlite/knowledge.js +7 -2
- package/dist/store/sqlite/notes.js +4 -4
- package/dist/store/sqlite/things.d.ts +0 -1
- package/dist/store/sqlite/things.js +25 -24
- package/dist/store/store.d.ts +1 -0
- package/package.json +1 -1
- package/parity.json +4 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import type { Id, Provider } from "./models.js";
|
|
3
|
+
/** A `chat:` or `msg:` reference given where a messenger command takes a chat or a message. */
|
|
4
|
+
export interface ChatReference {
|
|
5
|
+
text: string;
|
|
6
|
+
provider: Provider;
|
|
7
|
+
account: Id;
|
|
8
|
+
chat: Id;
|
|
9
|
+
message?: Id;
|
|
10
|
+
}
|
|
11
|
+
/** The account a reference must belong to; `account` is `undefined` while this profile's is not known yet. */
|
|
12
|
+
export interface RefScope {
|
|
13
|
+
account: Id | undefined;
|
|
14
|
+
profile?: string;
|
|
15
|
+
}
|
|
16
|
+
/** The profiles of this app the store saw on an account; `undefined` when that cannot be told. */
|
|
17
|
+
export type ProfilesOn = (account: Id) => Promise<readonly string[] | undefined>;
|
|
18
|
+
/**
|
|
19
|
+
* The chat or message a reference names, when `text` is one of this provider's; `undefined` for text that is no
|
|
20
|
+
* reference, which stays an id or a title. Another kind or another messenger is refused, never read as a title.
|
|
21
|
+
*/
|
|
22
|
+
export declare const chatReferenceOf: (text: string, provider: Provider, takes?: "chat" | "message") => ChatReference | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The identity a `contact:` reference names, when it is one of this provider's; `undefined` for any other text. A
|
|
25
|
+
* `person:` reference is the store's person, which the callers that take one read before this.
|
|
26
|
+
*/
|
|
27
|
+
export declare const contactReferenceOf: (text: string, provider: Provider | undefined) => Id | undefined;
|
|
28
|
+
/** The refusal of another account's reference: switching to it silently would read or send as somebody else. */
|
|
29
|
+
export declare const wrongAccount: (reference: Pick<ChatReference, "text" | "account">, scope: RefScope, profiles?: readonly string[]) => CliError;
|
|
30
|
+
/** Refuses a reference of another account than the scope's, naming the profiles that have it. */
|
|
31
|
+
export declare const ensureAccount: (reference: Pick<ChatReference, "text" | "account">, scope: RefScope, profilesOn?: ProfilesOn) => Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { providerLabel, providerNamed } from "./providers.js";
|
|
3
|
+
import { looksLikeReference, parseReference } from "./references.js";
|
|
4
|
+
const TAKES = {
|
|
5
|
+
chat: "a chat: chat:<provider>/<account>/<chat>, its id or its title",
|
|
6
|
+
message: "a message: msg:<provider>/<account>/<chat>/<message>, or a chat and the message's id",
|
|
7
|
+
};
|
|
8
|
+
const OPENS = {
|
|
9
|
+
chat: "messages list",
|
|
10
|
+
message: "messages show",
|
|
11
|
+
contact: "contacts show",
|
|
12
|
+
};
|
|
13
|
+
const article = (word) => (/^[aeiou]/.test(word) ? "an" : "a");
|
|
14
|
+
const wrongKind = (text, kind, takes) => new CliError("validation_error", `${text} names ${article(kind)} ${kind}; this takes ${TAKES[takes]}`, {
|
|
15
|
+
reason: "ref.wrong-kind",
|
|
16
|
+
});
|
|
17
|
+
const wrongProvider = (text, provider, kind) => {
|
|
18
|
+
const command = providerNamed(provider)?.command;
|
|
19
|
+
const opens = OPENS[kind];
|
|
20
|
+
const how = command && opens ? `open it with ${command} <profile> ${opens} ${text}` : "open it with its own CLI";
|
|
21
|
+
const label = providerLabel(provider);
|
|
22
|
+
return new CliError("validation_error", `${text} is ${article(label.toLowerCase())} ${label} ${kind}; ${how}`, {
|
|
23
|
+
reason: "ref.wrong-provider",
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The chat or message a reference names, when `text` is one of this provider's; `undefined` for text that is no
|
|
28
|
+
* reference, which stays an id or a title. Another kind or another messenger is refused, never read as a title.
|
|
29
|
+
*/
|
|
30
|
+
export const chatReferenceOf = (text, provider, takes = "chat") => {
|
|
31
|
+
const trimmed = text.trim();
|
|
32
|
+
if (!looksLikeReference(trimmed))
|
|
33
|
+
return undefined;
|
|
34
|
+
const reference = parseReference(trimmed);
|
|
35
|
+
if (reference.type !== "chat" && reference.type !== "message")
|
|
36
|
+
throw wrongKind(trimmed, reference.type, takes);
|
|
37
|
+
if (reference.provider !== provider)
|
|
38
|
+
throw wrongProvider(trimmed, reference.provider, reference.type);
|
|
39
|
+
const found = {
|
|
40
|
+
text: trimmed,
|
|
41
|
+
provider: reference.provider,
|
|
42
|
+
account: reference.account,
|
|
43
|
+
chat: reference.chat,
|
|
44
|
+
...(reference.type === "message" ? { message: reference.message } : {}),
|
|
45
|
+
};
|
|
46
|
+
return found;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The identity a `contact:` reference names, when it is one of this provider's; `undefined` for any other text. A
|
|
50
|
+
* `person:` reference is the store's person, which the callers that take one read before this.
|
|
51
|
+
*/
|
|
52
|
+
export const contactReferenceOf = (text, provider) => {
|
|
53
|
+
const trimmed = text.trim();
|
|
54
|
+
if (!trimmed.startsWith("contact:"))
|
|
55
|
+
return undefined;
|
|
56
|
+
const reference = parseReference(trimmed);
|
|
57
|
+
if (reference.type !== "contact")
|
|
58
|
+
return undefined;
|
|
59
|
+
if (provider !== undefined && reference.provider !== provider)
|
|
60
|
+
throw wrongProvider(trimmed, reference.provider, reference.type);
|
|
61
|
+
return reference.id;
|
|
62
|
+
};
|
|
63
|
+
/** The refusal of another account's reference: switching to it silently would read or send as somebody else. */
|
|
64
|
+
export const wrongAccount = (reference, scope, profiles) => {
|
|
65
|
+
const here = scope.account === undefined
|
|
66
|
+
? "the account of this profile is not known yet"
|
|
67
|
+
: `${scope.profile === undefined ? "this profile" : `profile ${scope.profile}`} is account ${scope.account}`;
|
|
68
|
+
const others = profiles === undefined
|
|
69
|
+
? ""
|
|
70
|
+
: `. Profiles signed in to ${reference.account}: ${profiles.length > 0 ? profiles.join(", ") : "none here"}`;
|
|
71
|
+
return new CliError("validation_error", `${reference.text} belongs to account ${reference.account}; ${here}${others}`, { reason: "ref.wrong-account" });
|
|
72
|
+
};
|
|
73
|
+
/** Refuses a reference of another account than the scope's, naming the profiles that have it. */
|
|
74
|
+
export const ensureAccount = async (reference, scope, profilesOn) => {
|
|
75
|
+
if (scope.account !== undefined && scope.account === reference.account)
|
|
76
|
+
return;
|
|
77
|
+
const profiles = await profilesOn?.(reference.account).catch(() => undefined);
|
|
78
|
+
throw wrongAccount(reference, scope, profiles);
|
|
79
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MeetingReference } from "../meetings/index.js";
|
|
1
2
|
/**
|
|
2
3
|
* One typed form for every kind of thing a link, a tag or an argument can name. The prefix makes a
|
|
3
4
|
* wrong kind fail loudly instead of matching another row. Parts that are ids of someone else's system
|
|
@@ -27,14 +28,31 @@ export type Reference = {
|
|
|
27
28
|
type: "folder";
|
|
28
29
|
id: string;
|
|
29
30
|
path: string | null;
|
|
30
|
-
};
|
|
31
|
+
} | MeetingReference;
|
|
31
32
|
/**
|
|
32
33
|
* Things named by their store id (a task also by its key or the task package's id). `entity` is the name
|
|
33
34
|
* before organizations and projects: it still parses, so an old file's reference resolves as not found.
|
|
34
35
|
*/
|
|
35
|
-
export declare const SIMPLE_REFERENCES: readonly ["note", "document", "person", "organization", "project", "task", "memory", "fact", "bot", "entity"];
|
|
36
|
+
export declare const SIMPLE_REFERENCES: readonly ["note", "document", "person", "organization", "project", "task", "memory", "fact", "bot", "event", "entity"];
|
|
36
37
|
export type SimpleReference = (typeof SIMPLE_REFERENCES)[number];
|
|
38
|
+
export type RefKindName = Exclude<Reference["type"], "entity">;
|
|
39
|
+
export interface RefKindSpec<R extends Reference = Reference> {
|
|
40
|
+
/** The word before the first colon: `chat`, `msg`, `document`. */
|
|
41
|
+
prefix: string;
|
|
42
|
+
type: R["type"];
|
|
43
|
+
/** For error texts: `chat:<provider>/<account>/<chat>`. */
|
|
44
|
+
shape: string;
|
|
45
|
+
parse(rest: string, text: string): R;
|
|
46
|
+
format(reference: R): string;
|
|
47
|
+
}
|
|
48
|
+
/** Every kind of reference. A new kind is one row here; nothing else switches on the prefix. */
|
|
49
|
+
export declare const REF_KINDS: readonly RefKindSpec[];
|
|
37
50
|
export declare const formatReference: (reference: Reference) => string;
|
|
38
51
|
export declare const parseReference: (text: string) => Reference;
|
|
39
52
|
/** The stored spelling of a reference, so two spellings of one thing compare equal. */
|
|
40
53
|
export declare const canonicalReference: (text: string) => string;
|
|
54
|
+
/**
|
|
55
|
+
* Strict: the whole text is one reference of a known kind, with no space. A title such as "Note: plan" is not, so an
|
|
56
|
+
* argument that takes either a reference or a title can tell them apart. The shape is checked only by parsing it.
|
|
57
|
+
*/
|
|
58
|
+
export declare const looksLikeReference: (text: string) => boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CliError, singleLine } from "@wirecat/cli-base";
|
|
2
|
+
import { formatMeetingReference, parseMeetingReference } from "../meetings/index.js";
|
|
2
3
|
import { formatLocator, parseLocator } from "./locator.js";
|
|
3
4
|
/**
|
|
4
5
|
* Things named by their store id (a task also by its key or the task package's id). `entity` is the name
|
|
@@ -14,70 +15,154 @@ export const SIMPLE_REFERENCES = [
|
|
|
14
15
|
"memory",
|
|
15
16
|
"fact",
|
|
16
17
|
"bot",
|
|
18
|
+
"event",
|
|
17
19
|
"entity",
|
|
18
20
|
];
|
|
19
|
-
const
|
|
21
|
+
const malformed = (text, shape) => new CliError("validation_error", `"${singleLine(text)}" is not a reference — expected ${shape}`, {
|
|
22
|
+
reason: "ref.malformed",
|
|
23
|
+
});
|
|
24
|
+
const parts = (rest, count, text, shape) => {
|
|
20
25
|
const split = rest.split("/");
|
|
21
26
|
if (split.length !== count || split.some((part) => part === ""))
|
|
22
|
-
throw
|
|
27
|
+
throw malformed(text, shape);
|
|
23
28
|
return split.map(decodeURIComponent);
|
|
24
29
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
case "folder":
|
|
34
|
-
return reference.path === null
|
|
35
|
-
? `folder:${encodeURIComponent(reference.id)}`
|
|
36
|
-
: `folder:${encodeURIComponent(reference.id)}/${encodeURIComponent(reference.path)}`;
|
|
37
|
-
default:
|
|
38
|
-
return `${reference.type}:${reference.id}`;
|
|
39
|
-
}
|
|
30
|
+
const encoded = (...values) => values.map(encodeURIComponent).join("/");
|
|
31
|
+
/** A subfolder spelled one way: no leading, trailing or doubled slash, and `.` for the folder itself. */
|
|
32
|
+
const folderPath = (path) => {
|
|
33
|
+
const clean = path
|
|
34
|
+
.split("/")
|
|
35
|
+
.filter((part) => part !== "" && part !== ".")
|
|
36
|
+
.join("/");
|
|
37
|
+
return clean === "" ? (path.trim() === "" ? "" : null) : clean;
|
|
40
38
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
const MEETING_SHAPE = "meeting:<account>/<meeting>[/<transcript>[/<cue>]] with safe integer ids";
|
|
40
|
+
const kind = (spec) => spec;
|
|
41
|
+
const simple = (prefix) => kind({
|
|
42
|
+
prefix,
|
|
43
|
+
type: prefix,
|
|
44
|
+
shape: `${prefix}:<id>`,
|
|
45
|
+
parse: (rest, text) => {
|
|
46
|
+
if (!rest.trim())
|
|
47
|
+
throw new CliError("validation_error", `"${singleLine(text)}" names no ${prefix}`, {
|
|
48
|
+
reason: "ref.malformed",
|
|
49
|
+
});
|
|
50
|
+
return { type: prefix, id: rest };
|
|
51
|
+
},
|
|
52
|
+
format: (reference) => `${reference.type}:${reference.id}`,
|
|
53
|
+
});
|
|
54
|
+
/** Every kind of reference. A new kind is one row here; nothing else switches on the prefix. */
|
|
55
|
+
export const REF_KINDS = [
|
|
56
|
+
kind({
|
|
57
|
+
prefix: "msg",
|
|
58
|
+
type: "message",
|
|
59
|
+
shape: "msg:<provider>/<account>/<chat>/<message>",
|
|
60
|
+
parse: (_, text) => {
|
|
61
|
+
try {
|
|
62
|
+
return { type: "message", ...parseLocator(text) };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw malformed(text, "msg:<provider>/<account>/<chat>/<message>");
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
format: formatLocator,
|
|
69
|
+
}),
|
|
70
|
+
kind({
|
|
71
|
+
prefix: "chat",
|
|
72
|
+
type: "chat",
|
|
73
|
+
shape: "chat:<provider>/<account>/<chat>",
|
|
74
|
+
parse: (rest, text) => {
|
|
75
|
+
const [provider, account, chat] = parts(rest, 3, text, "chat:<provider>/<account>/<chat>");
|
|
51
76
|
return { type: "chat", provider, account, chat };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
77
|
+
},
|
|
78
|
+
format: ({ provider, account, chat }) => `chat:${encoded(provider, account, chat)}`,
|
|
79
|
+
}),
|
|
80
|
+
kind({
|
|
81
|
+
prefix: "contact",
|
|
82
|
+
type: "contact",
|
|
83
|
+
shape: "contact:<provider>/<id>",
|
|
84
|
+
parse: (rest, text) => {
|
|
85
|
+
const [provider, id] = parts(rest, 2, text, "contact:<provider>/<id>");
|
|
55
86
|
return { type: "contact", provider, id };
|
|
56
|
-
}
|
|
57
|
-
|
|
87
|
+
},
|
|
88
|
+
format: ({ provider, id }) => `contact:${encoded(provider, id)}`,
|
|
89
|
+
}),
|
|
90
|
+
kind({
|
|
91
|
+
prefix: "folder",
|
|
92
|
+
type: "folder",
|
|
93
|
+
shape: "folder:<id>[/<path>]",
|
|
94
|
+
parse: (rest, text) => {
|
|
58
95
|
const slash = rest.indexOf("/");
|
|
59
96
|
const id = decodeURIComponent(slash < 0 ? rest : rest.slice(0, slash));
|
|
60
97
|
const path = slash < 0 ? null : folderPath(decodeURIComponent(rest.slice(slash + 1)));
|
|
61
98
|
if (!id.trim() || path === "")
|
|
62
|
-
throw
|
|
99
|
+
throw malformed(text, "folder:<id>[/<path>]");
|
|
63
100
|
return { type: "folder", id, path };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
101
|
+
},
|
|
102
|
+
format: ({ id, path }) => (path === null ? `folder:${encodeURIComponent(id)}` : `folder:${encoded(id, path)}`),
|
|
103
|
+
}),
|
|
104
|
+
kind({
|
|
105
|
+
prefix: "meeting",
|
|
106
|
+
type: "meeting",
|
|
107
|
+
shape: MEETING_SHAPE,
|
|
108
|
+
parse: (_, text) => {
|
|
109
|
+
try {
|
|
110
|
+
return parseMeetingReference(text);
|
|
70
111
|
}
|
|
71
|
-
|
|
112
|
+
catch {
|
|
113
|
+
throw malformed(text, MEETING_SHAPE);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
format: formatMeetingReference,
|
|
117
|
+
}),
|
|
118
|
+
...SIMPLE_REFERENCES.map(simple),
|
|
119
|
+
];
|
|
120
|
+
const byPrefix = new Map(REF_KINDS.map((spec) => [spec.prefix, spec]));
|
|
121
|
+
const byType = new Map(REF_KINDS.map((spec) => [spec.type, spec]));
|
|
122
|
+
/** The prefixes people are told about: `entity` only still parses. */
|
|
123
|
+
const offered = REF_KINDS.map(({ prefix }) => prefix).filter((prefix) => prefix !== "entity");
|
|
124
|
+
const distance = (a, b) => {
|
|
125
|
+
let row = Array.from({ length: b.length + 1 }, (_, at) => at);
|
|
126
|
+
for (const [i, x] of [...a].entries()) {
|
|
127
|
+
const next = [i + 1];
|
|
128
|
+
for (const [j, y] of [...b].entries())
|
|
129
|
+
next.push(Math.min(row[j + 1] + 1, next[j] + 1, row[j] + (x === y ? 0 : 1)));
|
|
130
|
+
row = next;
|
|
72
131
|
}
|
|
132
|
+
return row[b.length];
|
|
73
133
|
};
|
|
74
|
-
/**
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
.
|
|
78
|
-
.filter((
|
|
79
|
-
.
|
|
80
|
-
|
|
134
|
+
/** `doc` reads as `document`, not as `bot` one letter away: a typed start of a prefix wins over spelling. */
|
|
135
|
+
const nearest = (typed) => offered.find((prefix) => typed.length >= 2 && prefix.startsWith(typed)) ??
|
|
136
|
+
offered
|
|
137
|
+
.map((prefix) => ({ prefix, far: distance(typed, prefix) }))
|
|
138
|
+
.filter(({ far }) => far <= 2)
|
|
139
|
+
.sort((a, b) => a.far - b.far)[0]?.prefix;
|
|
140
|
+
const unknownKind = (text, prefix, rest) => {
|
|
141
|
+
const near = prefix === "" ? undefined : nearest(prefix);
|
|
142
|
+
const kinds = offered.map((one) => `${one}:`).join(", ");
|
|
143
|
+
const hint = near === undefined ? `; kinds: ${kinds}` : `; did you mean ${near}:${singleLine(rest)}? Kinds: ${kinds}`;
|
|
144
|
+
return new CliError("validation_error", `"${singleLine(text)}" is no kind of reference${hint}`, {
|
|
145
|
+
reason: "ref.unknown-kind",
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
export const formatReference = (reference) => byType.get(reference.type).format(reference);
|
|
149
|
+
export const parseReference = (text) => {
|
|
150
|
+
const reference = text.trim();
|
|
151
|
+
const colon = reference.indexOf(":");
|
|
152
|
+
const prefix = colon < 0 ? "" : reference.slice(0, colon);
|
|
153
|
+
const rest = reference.slice(colon + 1);
|
|
154
|
+
const spec = byPrefix.get(prefix);
|
|
155
|
+
if (!spec)
|
|
156
|
+
throw unknownKind(reference, prefix, rest);
|
|
157
|
+
return spec.parse(rest, reference);
|
|
81
158
|
};
|
|
82
159
|
/** The stored spelling of a reference, so two spellings of one thing compare equal. */
|
|
83
160
|
export const canonicalReference = (text) => formatReference(parseReference(text));
|
|
161
|
+
/**
|
|
162
|
+
* Strict: the whole text is one reference of a known kind, with no space. A title such as "Note: plan" is not, so an
|
|
163
|
+
* argument that takes either a reference or a title can tell them apart. The shape is checked only by parsing it.
|
|
164
|
+
*/
|
|
165
|
+
export const looksLikeReference = (text) => {
|
|
166
|
+
const match = /^([a-z]+):\S+$/.exec(text.trim());
|
|
167
|
+
return match !== null && byPrefix.has(match[1]);
|
|
168
|
+
};
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -1106,6 +1106,7 @@ export declare const ENTITIES: {
|
|
|
1106
1106
|
readonly held: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1107
1107
|
readonly held_chats: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1108
1108
|
}, undefined>, undefined>;
|
|
1109
|
+
readonly history_included: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1109
1110
|
}, undefined>, v.StrictObjectSchema<{
|
|
1110
1111
|
readonly batch: v.OptionalSchema<v.StrictObjectSchema<{
|
|
1111
1112
|
readonly attempted: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -209,6 +209,8 @@ export declare const messageSyncChat: v.StrictObjectSchema<{
|
|
|
209
209
|
readonly held: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
210
210
|
readonly held_chats: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
211
211
|
}, undefined>, undefined>;
|
|
212
|
+
/** `--full-history`: the chat's pushed history is now kept in full; `false` when the store does not know the chat. */
|
|
213
|
+
readonly history_included: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
212
214
|
}, undefined>;
|
|
213
215
|
export declare const messageSyncAll: v.StrictObjectSchema<{
|
|
214
216
|
readonly batch: v.OptionalSchema<v.StrictObjectSchema<{
|
|
@@ -333,6 +335,8 @@ export declare const messageSync: v.UnionSchema<[v.StrictObjectSchema<{
|
|
|
333
335
|
readonly held: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
334
336
|
readonly held_chats: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
335
337
|
}, undefined>, undefined>;
|
|
338
|
+
/** `--full-history`: the chat's pushed history is now kept in full; `false` when the store does not know the chat. */
|
|
339
|
+
readonly history_included: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
336
340
|
}, undefined>, v.StrictObjectSchema<{
|
|
337
341
|
readonly batch: v.OptionalSchema<v.StrictObjectSchema<{
|
|
338
342
|
readonly attempted: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -60,6 +60,8 @@ export const messageSyncChat = v.strictObject({
|
|
|
60
60
|
reached_last: v.optional(v.literal(true)),
|
|
61
61
|
stopped: v.optional(v.literal(true)),
|
|
62
62
|
tasks,
|
|
63
|
+
/** `--full-history`: the chat's pushed history is now kept in full; `false` when the store does not know the chat. */
|
|
64
|
+
history_included: v.optional(v.boolean()),
|
|
63
65
|
});
|
|
64
66
|
export const messageSyncAll = v.strictObject({
|
|
65
67
|
batch: v.optional(batchResult),
|
package/dist/errors/reasons.js
CHANGED
|
@@ -29,6 +29,8 @@ const SIGN_IN = [
|
|
|
29
29
|
["oauth.revoke-failed", "outcome_unknown", "the provider did not confirm the revoke"],
|
|
30
30
|
["oauth.invalid", "authentication_error", "the saved sign-in cannot be read"],
|
|
31
31
|
["oauth.not-saved", "configuration_error", "the sign-in cannot be saved or read where it is kept"],
|
|
32
|
+
["sign-in.client-file-open", "configuration_error", "other users of this computer can read the client file"],
|
|
33
|
+
["sign-in.client-file-invalid", "configuration_error", "the file is not a Desktop app OAuth client JSON"],
|
|
32
34
|
];
|
|
33
35
|
const OTHERS = [
|
|
34
36
|
["model.offline", "permission_error", "downloading a model needs the network; --offline was requested"],
|
|
@@ -78,6 +80,17 @@ const OTHERS = [
|
|
|
78
80
|
validation("ics.too-many", "the file holds more events than one import sends"),
|
|
79
81
|
validation("ics.too-large", "the file is larger than one import reads"),
|
|
80
82
|
["calendar.read-only", "permission_error", "the calendar takes no new events from this account"],
|
|
83
|
+
validation("feed.address-invalid", "a feed address is an http, https or webcal URL with no user or password in it"),
|
|
84
|
+
validation("feed.duplicate", "this profile follows a feed at this address already"),
|
|
85
|
+
validation("feed.not-a-feed", "the calendar is not a feed this profile follows"),
|
|
86
|
+
validation("feed.too-large", "the feed is larger than one sync reads"),
|
|
87
|
+
validation("feed.redirect-refused", "the feed sent the request on to an address that is not https"),
|
|
88
|
+
["feed.no-address", "configuration_error", "the feed's address is no longer kept on this computer"],
|
|
89
|
+
["feed.refused", "provider_error", "the feed's server refused the request"],
|
|
90
|
+
["feed.gone", "not_found", "the feed's server has no feed at this address any more"],
|
|
91
|
+
["feed.timeout", "timeout", "the feed's server did not answer in time"],
|
|
92
|
+
["feed.unreachable", "network_error", "the feed's server could not be reached"],
|
|
93
|
+
validation("busy.feed", "a feed calendar has no busy times to ask; read its events with events list"),
|
|
81
94
|
["calendar.unsynced", "not_found", "the calendar account has no synced calendar in the store"],
|
|
82
95
|
["calendar.sync-incomplete", "not_found", "a synced calendar's sync never finished or is mid-round"],
|
|
83
96
|
["calendar.no-upcoming", "not_found", "no upcoming meeting with other attendees is stored for this account"],
|
|
@@ -133,6 +146,11 @@ const OTHERS = [
|
|
|
133
146
|
validation("period.ambiguous", "the period can be read more than one way"),
|
|
134
147
|
validation("proposal.wrong-state", "the proposal is not in a state that allows this"),
|
|
135
148
|
validation("ranking.invalid", "the ranking is not valid"),
|
|
149
|
+
validation("ref.malformed", "the reference has a known kind but not its shape"),
|
|
150
|
+
validation("ref.unknown-kind", "the text names no kind of reference"),
|
|
151
|
+
validation("ref.wrong-kind", "the reference names another kind of thing than the argument takes"),
|
|
152
|
+
validation("ref.wrong-provider", "the reference belongs to another messenger; open it with that CLI"),
|
|
153
|
+
validation("ref.wrong-account", "the reference belongs to another account than this profile's"),
|
|
136
154
|
validation("selection.changed", "the stored selection changed meanwhile"),
|
|
137
155
|
validation("selection.invalid", "the selection is not valid"),
|
|
138
156
|
validation("search.kind", "the saved search belongs to another kind of search: messages or meetings"),
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { FOLDER_PROVIDERS, isFolderProvider, isKind, KINDS, type Kind, kindIn, k
|
|
|
12
12
|
export type { RankingGraphEvidence, RankingGraphLink } from "./domain/rankings-graph.js";
|
|
13
13
|
export type { RankingComponent, RankingInput, RankingMeasure, RankingOptions, RankingTarget, RankingWeights, ScorePreset, } from "./domain/rankings-options.js";
|
|
14
14
|
export { rankingOptions } from "./domain/rankings-options.js";
|
|
15
|
-
export { canonicalReference, formatReference, parseReference, type Reference } from "./domain/references.js";
|
|
15
|
+
export { canonicalReference, formatReference, looksLikeReference, parseReference, REF_KINDS, type Reference, type RefKindName, type RefKindSpec, } from "./domain/references.js";
|
|
16
16
|
export { normalizeTag, TAG_TYPES, type TagType } from "./domain/tags.js";
|
|
17
17
|
export { type AppErrors, DETAILS as CORE_DETAILS, failureContext, REASONS as CORE_REASONS } from "./errors/reasons.js";
|
|
18
18
|
export { canonicalMeetingReference, formatMeetingReference, type MeetingReference, parseMeetingReference, } from "./meetings/index.js";
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { parseMarkdownSpans } from "./domain/markdown-spans.js";
|
|
|
7
7
|
export { countedPoll } from "./domain/polls.js";
|
|
8
8
|
export { FOLDER_PROVIDERS, isFolderProvider, isKind, KINDS, kindIn, kindOf, PROVIDERS, providerNamed, providers, UNKNOWN_PROVIDER, useProviders, } from "./domain/providers.js";
|
|
9
9
|
export { rankingOptions } from "./domain/rankings-options.js";
|
|
10
|
-
export { canonicalReference, formatReference, parseReference } from "./domain/references.js";
|
|
10
|
+
export { canonicalReference, formatReference, looksLikeReference, parseReference, REF_KINDS, } from "./domain/references.js";
|
|
11
11
|
export { normalizeTag, TAG_TYPES } from "./domain/tags.js";
|
|
12
12
|
export { DETAILS as CORE_DETAILS, failureContext, REASONS as CORE_REASONS } from "./errors/reasons.js";
|
|
13
13
|
export { canonicalMeetingReference, formatMeetingReference, parseMeetingReference, } from "./meetings/index.js";
|