papiers 0.1.4 → 0.1.7
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 +91 -16
- package/dist/index.js +400 -275
- package/package.json +1 -1
- package/skills/papiers-research/SKILL.md +21 -2
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Search and read your Papiers library from the terminal.
|
|
4
4
|
|
|
5
|
+
The CLI can inspect your saved documents, highlights, notes, and
|
|
6
|
+
conversations. It changes your library only to import a public PDF or webpage
|
|
7
|
+
(`papiers import --help`) or highlight a passage in a saved PDF or webpage
|
|
8
|
+
(`papiers highlight --help`), and only with a token that allows it.
|
|
9
|
+
|
|
5
10
|
## Install
|
|
6
11
|
|
|
7
12
|
```bash
|
|
@@ -10,17 +15,60 @@ npm install -g papiers
|
|
|
10
15
|
|
|
11
16
|
Requires Node.js 22 or newer.
|
|
12
17
|
|
|
13
|
-
##
|
|
18
|
+
## First run
|
|
14
19
|
|
|
15
|
-
In Papiers, open Settings -> API tokens
|
|
20
|
+
1. In Papiers, open Settings -> API tokens and create a token.
|
|
21
|
+
2. Sign in and check the connection:
|
|
16
22
|
|
|
17
23
|
```bash
|
|
18
24
|
papiers auth login
|
|
25
|
+
papiers auth status
|
|
26
|
+
papiers doctor
|
|
19
27
|
```
|
|
20
28
|
|
|
21
29
|
Paste the token when prompted. The CLI saves it locally so you do not need to
|
|
22
30
|
paste it again.
|
|
23
31
|
|
|
32
|
+
3. Explore your library with human-readable output:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# See what you saved recently.
|
|
36
|
+
papiers list --kind document --limit 5 --pretty
|
|
37
|
+
|
|
38
|
+
# Search across documents, passages, highlights, notes, and conversations.
|
|
39
|
+
papiers search "retrieval augmented generation" --limit 5 --pretty
|
|
40
|
+
|
|
41
|
+
# List notes, then read one using the nte_... ID from the result.
|
|
42
|
+
papiers list --kind note --limit 5 --pretty
|
|
43
|
+
papiers read nte_... --format markdown --pretty
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Run `papiers <command> --help` for command-specific options or
|
|
47
|
+
`papiers help --all` for the complete CLI reference.
|
|
48
|
+
|
|
49
|
+
## Common workflows
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Recent highlights.
|
|
53
|
+
papiers list --kind highlight --since 30d --limit 10 --pretty
|
|
54
|
+
|
|
55
|
+
# Read the matching passages from a search through stdin.
|
|
56
|
+
papiers search "retrieval" --kind passage --limit 5 --passage-ids \
|
|
57
|
+
| papiers read @- --format plain --pretty
|
|
58
|
+
|
|
59
|
+
# Read document metadata without fetching its full content.
|
|
60
|
+
papiers read doc_... --format metadata --pretty
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Data commands emit stable JSON by default. Use `--pretty` for terminal reading;
|
|
64
|
+
use `--json`, `--ids`, `--ids0`, and `--doc-ids` for scripts and pipelines.
|
|
65
|
+
|
|
66
|
+
To forget the locally saved token:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
papiers auth logout
|
|
70
|
+
```
|
|
71
|
+
|
|
24
72
|
## Use with an AI agent
|
|
25
73
|
|
|
26
74
|
This is what `papiers` is really for. Point a coding or research agent (Claude
|
|
@@ -33,9 +81,26 @@ The package ships a ready-to-use agent skill at
|
|
|
33
81
|
encodes the loop — survey what you've been reading, search across passages +
|
|
34
82
|
highlights + notes + conversations in one ranking, then read and cite your own
|
|
35
83
|
sources.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
84
|
+
|
|
85
|
+
After a global install, locate the packaged skill under the npm root:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
PAPIERS_ROOT="$(npm root -g)/papiers"
|
|
89
|
+
|
|
90
|
+
# Claude Code, project-local skill
|
|
91
|
+
mkdir -p .claude/skills/papiers-research
|
|
92
|
+
cp "$PAPIERS_ROOT/skills/papiers-research/SKILL.md" \
|
|
93
|
+
.claude/skills/papiers-research/SKILL.md
|
|
94
|
+
|
|
95
|
+
# Codex, project-local skill
|
|
96
|
+
mkdir -p .codex/skills/papiers-research
|
|
97
|
+
cp "$PAPIERS_ROOT/skills/papiers-research/SKILL.md" \
|
|
98
|
+
.codex/skills/papiers-research/SKILL.md
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For another agent, add the same file through its normal skill or project
|
|
102
|
+
instructions mechanism. Then ask the agent to propose an idea, design an
|
|
103
|
+
experiment, or review a draft. It will think with your library, not around it.
|
|
39
104
|
|
|
40
105
|
```bash
|
|
41
106
|
# the agent only needs your token in its environment:
|
|
@@ -45,18 +110,28 @@ export PAPIERS_TOKEN=pap_live_...
|
|
|
45
110
|
Every data command emits a stable JSON envelope (`--json` makes errors JSON
|
|
46
111
|
too), so an agent can learn the whole tool from `papiers --help --all` alone.
|
|
47
112
|
|
|
48
|
-
##
|
|
113
|
+
## Automation
|
|
49
114
|
|
|
50
115
|
```bash
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
papiers
|
|
116
|
+
# Keep the token in the process environment rather than passing it as an
|
|
117
|
+
# argument. Do not commit it to source control.
|
|
118
|
+
export PAPIERS_TOKEN=pap_live_...
|
|
119
|
+
papiers search "mixture of experts" --limit 10 --json
|
|
55
120
|
```
|
|
56
121
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
##
|
|
61
|
-
|
|
62
|
-
|
|
122
|
+
`PAPIERS_TOKEN` takes precedence over a token saved by `papiers auth login`.
|
|
123
|
+
Run `papiers doctor` to see which token source and API endpoint are active.
|
|
124
|
+
|
|
125
|
+
## Troubleshooting
|
|
126
|
+
|
|
127
|
+
- **`papiers: command not found`:** run `npm prefix -g` and make sure that
|
|
128
|
+
installation's binary directory is on `PATH`.
|
|
129
|
+
- **Unsupported Node.js version:** run `node --version` and install Node.js 22
|
|
130
|
+
or newer.
|
|
131
|
+
- **Authentication failed:** run `papiers auth status`. If necessary, run
|
|
132
|
+
`papiers auth logout`, create a fresh token in Papiers, and sign in again.
|
|
133
|
+
- **Wrong token or endpoint:** run `papiers doctor`. Unset `PAPIERS_TOKEN` or
|
|
134
|
+
`PAPIERS_API_BASE_URL` if an automation environment is overriding your saved
|
|
135
|
+
login or the production endpoint.
|
|
136
|
+
- **Connection or API error:** run `papiers doctor`, then retry the failing
|
|
137
|
+
command with `--json` to get a machine-readable error and request ID.
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { realpathSync } from "fs";
|
|
|
9
9
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
10
|
|
|
11
11
|
// src/commands.ts
|
|
12
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
12
13
|
import { readFile, stat } from "fs/promises";
|
|
13
14
|
import { isAbsolute, resolve as resolve2 } from "path";
|
|
14
15
|
|
|
@@ -412,6 +413,63 @@ function isNodeError(error) {
|
|
|
412
413
|
return Boolean(error && typeof error === "object" && "code" in error);
|
|
413
414
|
}
|
|
414
415
|
|
|
416
|
+
// ../../src/shared/papiers-public-ids.ts
|
|
417
|
+
var PAPIERS_PUBLIC_ID_PREFIX_BY_KIND = {
|
|
418
|
+
document: "doc",
|
|
419
|
+
highlight: "hl",
|
|
420
|
+
conversation: "cnv",
|
|
421
|
+
section: "sec",
|
|
422
|
+
figure: "fig",
|
|
423
|
+
table: "tbl",
|
|
424
|
+
note: "nte"
|
|
425
|
+
};
|
|
426
|
+
var PAPIERS_PUBLIC_ID_KIND_BY_PREFIX = Object.fromEntries(
|
|
427
|
+
Object.entries(PAPIERS_PUBLIC_ID_PREFIX_BY_KIND).map(([kind, prefix]) => [
|
|
428
|
+
prefix,
|
|
429
|
+
kind
|
|
430
|
+
])
|
|
431
|
+
);
|
|
432
|
+
var PAPIERS_PUBLIC_ID_PATTERN = /^(doc|hl|cnv|sec|fig|tbl|nte)_([a-f0-9]{32})$/;
|
|
433
|
+
var PAPIERS_PUBLIC_REF_PATTERN = /^(doc_[a-f0-9]{32})#(?:p(0|[1-9][0-9]{0,9})|e([a-z0-9_]{1,64}))\.([a-f0-9]{8})$/;
|
|
434
|
+
var MAX_CHUNK_INDEX = 2147483647;
|
|
435
|
+
function parsePapiersPublicId(value) {
|
|
436
|
+
const id = value.trim();
|
|
437
|
+
const ref = id.match(PAPIERS_PUBLIC_REF_PATTERN);
|
|
438
|
+
if (ref) {
|
|
439
|
+
const [, documentId, chunkIndex, astNodeId, fingerprint] = ref;
|
|
440
|
+
if (chunkIndex !== void 0) {
|
|
441
|
+
const index = Number(chunkIndex);
|
|
442
|
+
if (index > MAX_CHUNK_INDEX) return null;
|
|
443
|
+
return {
|
|
444
|
+
id,
|
|
445
|
+
kind: "passage",
|
|
446
|
+
documentId,
|
|
447
|
+
chunkIndex: index,
|
|
448
|
+
fingerprint
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
return { id, kind: "equation", documentId, astNodeId, fingerprint };
|
|
452
|
+
}
|
|
453
|
+
const match = id.match(PAPIERS_PUBLIC_ID_PATTERN);
|
|
454
|
+
if (!match) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const prefix = match[1];
|
|
458
|
+
const kind = PAPIERS_PUBLIC_ID_KIND_BY_PREFIX[prefix];
|
|
459
|
+
if (!kind) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
id,
|
|
464
|
+
kind,
|
|
465
|
+
prefix,
|
|
466
|
+
opaqueId: match[2]
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function staleReferenceMessage(kind) {
|
|
470
|
+
return kind === "passage" ? "Passage text has changed. Search or read the document again." : "Equation text has changed. Search or read the document again.";
|
|
471
|
+
}
|
|
472
|
+
|
|
415
473
|
// ../../src/shared/papiers-resource-locators.ts
|
|
416
474
|
var UUID_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
|
|
417
475
|
var PAPIERS_RESOURCE_LOCATOR_PATTERN_SOURCE = `papiers://(?:note|highlight|item|conversation|artifact)/${UUID_PATTERN}`;
|
|
@@ -533,54 +591,20 @@ var DOCUMENT_MAX_FILTER_VALUES = DOCUMENT_MAX_IDS;
|
|
|
533
591
|
var DOCUMENT_DEFAULT_READ_CHARS_PER_ITEM = 5e4;
|
|
534
592
|
var DOCUMENT_MAX_READ_CHARS_PER_ITEM = 2e5;
|
|
535
593
|
var DOCUMENT_MAX_READ_CHARS_PER_REQUEST = 5e5;
|
|
536
|
-
var DOCUMENT_PUBLIC_ID_PATTERN = /^doc_[a-f0-9]{32}$/u;
|
|
537
|
-
var PASSAGE_PUBLIC_ID_PATTERN = /^pas_[a-f0-9]{32}$/u;
|
|
538
|
-
var HIGHLIGHT_PUBLIC_ID_PATTERN = /^hl_[a-f0-9]{32}$/u;
|
|
539
|
-
var CONVERSATION_PUBLIC_ID_PATTERN = /^cnv_[a-f0-9]{32}$/u;
|
|
540
|
-
var EQUATION_PUBLIC_ID_PATTERN = /^eq_[a-f0-9]{32}$/u;
|
|
541
|
-
var NOTE_PUBLIC_ID_PATTERN = /^nte_[a-f0-9]{32}$/u;
|
|
542
594
|
var SEARCH_RANKING_ID_PATTERN = /^srch_[a-f0-9]{32}$/u;
|
|
543
595
|
function isSearchRankingId(value) {
|
|
544
596
|
return SEARCH_RANKING_ID_PATTERN.test(value);
|
|
545
597
|
}
|
|
546
|
-
|
|
547
|
-
return DOCUMENT_PUBLIC_ID_PATTERN.test(value);
|
|
548
|
-
}
|
|
549
|
-
function isPassagePublicId(value) {
|
|
550
|
-
return PASSAGE_PUBLIC_ID_PATTERN.test(value);
|
|
551
|
-
}
|
|
552
|
-
function isHighlightPublicId(value) {
|
|
553
|
-
return HIGHLIGHT_PUBLIC_ID_PATTERN.test(value);
|
|
554
|
-
}
|
|
555
|
-
function isConversationPublicId(value) {
|
|
556
|
-
return CONVERSATION_PUBLIC_ID_PATTERN.test(value);
|
|
557
|
-
}
|
|
558
|
-
function isEquationPublicId(value) {
|
|
559
|
-
return EQUATION_PUBLIC_ID_PATTERN.test(value);
|
|
560
|
-
}
|
|
561
|
-
function isNotePublicId(value) {
|
|
562
|
-
return NOTE_PUBLIC_ID_PATTERN.test(value);
|
|
563
|
-
}
|
|
598
|
+
var CLI_READABLE_KINDS = new Set(PUBLIC_KINDS);
|
|
564
599
|
function publicReadKindForId(value) {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (isPassagePublicId(value)) {
|
|
569
|
-
return "passage";
|
|
570
|
-
}
|
|
571
|
-
if (isHighlightPublicId(value)) {
|
|
572
|
-
return "highlight";
|
|
573
|
-
}
|
|
574
|
-
if (isConversationPublicId(value)) {
|
|
575
|
-
return "conversation";
|
|
576
|
-
}
|
|
577
|
-
if (isEquationPublicId(value)) {
|
|
578
|
-
return "equation";
|
|
579
|
-
}
|
|
580
|
-
if (isNotePublicId(value)) {
|
|
581
|
-
return "note";
|
|
600
|
+
const parsed = parsePapiersPublicId(value);
|
|
601
|
+
if (!parsed || parsed.id !== value || !CLI_READABLE_KINDS.has(parsed.kind)) {
|
|
602
|
+
return null;
|
|
582
603
|
}
|
|
583
|
-
return
|
|
604
|
+
return parsed.kind;
|
|
605
|
+
}
|
|
606
|
+
function isDocumentPublicId(value) {
|
|
607
|
+
return publicReadKindForId(value) === "document";
|
|
584
608
|
}
|
|
585
609
|
function readKindForIdentifier(value) {
|
|
586
610
|
const publicKind = publicReadKindForId(value);
|
|
@@ -607,8 +631,10 @@ var PAPIERS_SCHEMA_VERSIONS = {
|
|
|
607
631
|
readRequest: "papiers.read.request.v1",
|
|
608
632
|
read: "papiers.read.v1",
|
|
609
633
|
capabilities: "papiers.capabilities.v1",
|
|
610
|
-
|
|
611
|
-
|
|
634
|
+
importRequest: "papiers.import.request.v1",
|
|
635
|
+
import: "papiers.import.v1",
|
|
636
|
+
highlightRequest: "papiers.highlight.request.v1",
|
|
637
|
+
highlight: "papiers.highlight.v1",
|
|
612
638
|
error: "papiers.error.v1"
|
|
613
639
|
};
|
|
614
640
|
var PAPIERS_PUBLIC_DATA_PLANE_SCOPES = [
|
|
@@ -665,10 +691,7 @@ var PAPIERS_SEARCH_MATCH_REASONS = [
|
|
|
665
691
|
var PAPIERS_PUBLIC_NOT_READY_REASONS_BY_FEATURE = {
|
|
666
692
|
document_content_read: ["explicit_enablement_required"],
|
|
667
693
|
non_document_kinds: ["public_id_projection_not_ready"],
|
|
668
|
-
content_search: [
|
|
669
|
-
"content_search_not_ready",
|
|
670
|
-
"public_passage_projection_not_ready"
|
|
671
|
-
],
|
|
694
|
+
content_search: ["content_search_not_ready"],
|
|
672
695
|
list_cursors: ["cursor_signing_not_configured"],
|
|
673
696
|
read_cursors: [
|
|
674
697
|
"cursor_signing_not_configured",
|
|
@@ -703,6 +726,7 @@ var PAPIERS_PUBLIC_ERROR_CODES = [
|
|
|
703
726
|
"invalid_request",
|
|
704
727
|
"not_found",
|
|
705
728
|
"source_superseded",
|
|
729
|
+
"stale_reference",
|
|
706
730
|
"content_limit_exceeded",
|
|
707
731
|
"request_limit_exceeded",
|
|
708
732
|
"rate_limited",
|
|
@@ -799,9 +823,6 @@ function isTokenUuid(value) {
|
|
|
799
823
|
}
|
|
800
824
|
|
|
801
825
|
// src/response-validation.ts
|
|
802
|
-
var PUBLIC_DATA_PLANE_SCOPE_SET = new Set(
|
|
803
|
-
PAPIERS_PUBLIC_DATA_PLANE_SCOPES
|
|
804
|
-
);
|
|
805
826
|
var PUBLIC_ERROR_CODE_SET = new Set(PAPIERS_PUBLIC_ERROR_CODES);
|
|
806
827
|
var SAFE_REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/u;
|
|
807
828
|
var LIST_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -850,26 +871,6 @@ var READ_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
|
850
871
|
"items",
|
|
851
872
|
"usage"
|
|
852
873
|
]);
|
|
853
|
-
var TOKEN_LIST_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
854
|
-
"schemaVersion",
|
|
855
|
-
"requestId",
|
|
856
|
-
"tokens"
|
|
857
|
-
]);
|
|
858
|
-
var TOKEN_REVOKE_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
859
|
-
"schemaVersion",
|
|
860
|
-
"requestId",
|
|
861
|
-
"tokenId",
|
|
862
|
-
"revoked"
|
|
863
|
-
]);
|
|
864
|
-
var TOKEN_SUMMARY_KEYS = /* @__PURE__ */ new Set([
|
|
865
|
-
"tokenId",
|
|
866
|
-
"name",
|
|
867
|
-
"scopes",
|
|
868
|
-
"createdAt",
|
|
869
|
-
"expiresAt",
|
|
870
|
-
"revokedAt",
|
|
871
|
-
"lastUsedAt"
|
|
872
|
-
]);
|
|
873
874
|
var PAGE_KEYS = /* @__PURE__ */ new Set(["nextCursor", "returnedCount"]);
|
|
874
875
|
var READ_USAGE_KEYS = /* @__PURE__ */ new Set(["returnedChars", "truncatedItemCount"]);
|
|
875
876
|
var LIST_DOCUMENT_ITEM_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -949,6 +950,7 @@ var READ_SUCCESS_PASSAGE_ITEM_KEYS = /* @__PURE__ */ new Set([
|
|
|
949
950
|
"truncated",
|
|
950
951
|
"charCount",
|
|
951
952
|
"tokenEstimate",
|
|
953
|
+
"metadata",
|
|
952
954
|
"warnings"
|
|
953
955
|
]);
|
|
954
956
|
var READ_SUCCESS_HIGHLIGHT_ITEM_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -1056,6 +1058,7 @@ var CONVERSATION_READ_NOT_FOUND_MESSAGE = "Conversation is not available.";
|
|
|
1056
1058
|
var EQUATION_READ_NOT_FOUND_MESSAGE = "Equation is not available.";
|
|
1057
1059
|
var NOTE_READ_NOT_FOUND_MESSAGE = "Note is not available.";
|
|
1058
1060
|
var DOCUMENT_READ_SOURCE_SUPERSEDED_CODE = "source_superseded";
|
|
1061
|
+
var READ_STALE_REFERENCE_CODE = "stale_reference";
|
|
1059
1062
|
var CONVERSATION_READ_CONTENT_LIMIT_CODE = "content_limit_exceeded";
|
|
1060
1063
|
var CONVERSATION_READ_REQUEST_LIMIT_CODE = "request_limit_exceeded";
|
|
1061
1064
|
var DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE = "Document content is not available for the current source.";
|
|
@@ -1299,28 +1302,64 @@ function assertPapiersReadResponse(label, body, expectedIds, expected = "metadat
|
|
|
1299
1302
|
function assertPapiersReadPipelineEnvelope(label, body) {
|
|
1300
1303
|
validatePapiersReadResponse(label, body);
|
|
1301
1304
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1305
|
+
var IMPORT_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
1306
|
+
"schemaVersion",
|
|
1307
|
+
"requestId",
|
|
1308
|
+
"operationId",
|
|
1309
|
+
"status",
|
|
1310
|
+
"documentId",
|
|
1311
|
+
"error"
|
|
1312
|
+
]);
|
|
1313
|
+
var IMPORT_ERROR_KEYS = new Set(
|
|
1314
|
+
Object.keys({
|
|
1315
|
+
code: true,
|
|
1316
|
+
message: true,
|
|
1317
|
+
retryable: true,
|
|
1318
|
+
details: true
|
|
1319
|
+
})
|
|
1320
|
+
);
|
|
1321
|
+
var IMPORT_ERROR_DETAIL_KEYS = /* @__PURE__ */ new Set(["importCode"]);
|
|
1322
|
+
function assertPapiersImportResponse(label, body) {
|
|
1323
|
+
const value = assertSchema(label, body, PAPIERS_SCHEMA_VERSIONS.import);
|
|
1324
|
+
rejectUnexpectedKeys(label, value, IMPORT_RESPONSE_KEYS);
|
|
1306
1325
|
assertPublicRequestId(`${label} requestId`, value.requestId);
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1326
|
+
assertNonEmptyString(`${label} operationId`, value.operationId);
|
|
1327
|
+
assertNonEmptyString(`${label} status`, value.status);
|
|
1328
|
+
if (value.documentId !== null) {
|
|
1329
|
+
assertDocumentReadIdentifier(`${label} documentId`, value.documentId);
|
|
1330
|
+
}
|
|
1331
|
+
if (value.error !== null) {
|
|
1332
|
+
const error = assertRecord(`${label} error`, value.error);
|
|
1333
|
+
rejectUnexpectedKeys(`${label} error`, error, IMPORT_ERROR_KEYS);
|
|
1334
|
+
assertPublicErrorCode(`${label} error.code`, error.code);
|
|
1335
|
+
assertNonEmptyString(`${label} error.message`, error.message);
|
|
1336
|
+
assertBoolean(`${label} error.retryable`, error.retryable);
|
|
1337
|
+
const details = assertRecord(`${label} error.details`, error.details);
|
|
1338
|
+
rejectUnexpectedKeys(
|
|
1339
|
+
`${label} error.details`,
|
|
1340
|
+
details,
|
|
1341
|
+
IMPORT_ERROR_DETAIL_KEYS
|
|
1342
|
+
);
|
|
1343
|
+
assertNonEmptyString(
|
|
1344
|
+
`${label} error.details.importCode`,
|
|
1345
|
+
details.importCode
|
|
1346
|
+
);
|
|
1310
1347
|
}
|
|
1311
1348
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1349
|
+
var HIGHLIGHT_RESPONSE_KEYS = /* @__PURE__ */ new Set([
|
|
1350
|
+
"schemaVersion",
|
|
1351
|
+
"requestId",
|
|
1352
|
+
"highlightId"
|
|
1353
|
+
]);
|
|
1354
|
+
function assertPapiersHighlightResponse(label, body) {
|
|
1355
|
+
const value = assertSchema(label, body, PAPIERS_SCHEMA_VERSIONS.highlight);
|
|
1356
|
+
rejectUnexpectedKeys(label, value, HIGHLIGHT_RESPONSE_KEYS);
|
|
1316
1357
|
assertPublicRequestId(`${label} requestId`, value.requestId);
|
|
1317
|
-
|
|
1318
|
-
if (tokenId !== expectedTokenId) {
|
|
1358
|
+
if (typeof value.highlightId !== "string" || !value.highlightId.startsWith("papiers://highlight/")) {
|
|
1319
1359
|
throw new PapiersCliResponseValidationError(
|
|
1320
|
-
`${label}
|
|
1360
|
+
`${label} returned an invalid highlight identifier.`
|
|
1321
1361
|
);
|
|
1322
1362
|
}
|
|
1323
|
-
assertBoolean(`${label} revoked`, value.revoked);
|
|
1324
1363
|
}
|
|
1325
1364
|
function assertPapiersErrorResponse(label, body, expectedHttpStatus) {
|
|
1326
1365
|
assertNoInternalResponseKeys(label, body);
|
|
@@ -1438,28 +1477,6 @@ function validatePapiersReadResponse(label, body, options = {}) {
|
|
|
1438
1477
|
);
|
|
1439
1478
|
}
|
|
1440
1479
|
}
|
|
1441
|
-
function validateTokenSummary(label, rawToken) {
|
|
1442
|
-
const token = assertRecord(label, rawToken);
|
|
1443
|
-
rejectUnexpectedKeys(label, token, TOKEN_SUMMARY_KEYS);
|
|
1444
|
-
assertTokenId(`${label} tokenId`, token.tokenId);
|
|
1445
|
-
assertNonEmptyString(`${label} name`, token.name);
|
|
1446
|
-
const scopes = assertArray(`${label} scopes`, token.scopes);
|
|
1447
|
-
if (scopes.length === 0) {
|
|
1448
|
-
throw new PapiersCliResponseValidationError(`${label} scopes was empty.`);
|
|
1449
|
-
}
|
|
1450
|
-
for (const [index, rawScope] of scopes.entries()) {
|
|
1451
|
-
const scope = assertNonEmptyString(`${label} scope ${index}`, rawScope);
|
|
1452
|
-
if (!PUBLIC_DATA_PLANE_SCOPE_SET.has(scope)) {
|
|
1453
|
-
throw new PapiersCliResponseValidationError(
|
|
1454
|
-
`${label} scope ${index} was not a public data-plane scope.`
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
assertTimestampString(`${label} createdAt`, token.createdAt);
|
|
1459
|
-
validateOptionalTimestamp(`${label} expiresAt`, token.expiresAt);
|
|
1460
|
-
validateOptionalTimestamp(`${label} revokedAt`, token.revokedAt);
|
|
1461
|
-
validateOptionalTimestamp(`${label} lastUsedAt`, token.lastUsedAt);
|
|
1462
|
-
}
|
|
1463
1480
|
function validateSearchResults(label, results, options = {}) {
|
|
1464
1481
|
const values = assertArray(`${label} results`, results);
|
|
1465
1482
|
for (const [index, rawResult] of values.entries()) {
|
|
@@ -1807,7 +1824,7 @@ function validateOptionalListSearchMetadata(label, value, kind) {
|
|
|
1807
1824
|
);
|
|
1808
1825
|
}
|
|
1809
1826
|
if (kind === "passage") {
|
|
1810
|
-
return
|
|
1827
|
+
return validatePassageMetadata(label, value);
|
|
1811
1828
|
}
|
|
1812
1829
|
if (kind === "highlight") {
|
|
1813
1830
|
return validateHighlightMetadata(label, value, assertDocId);
|
|
@@ -1821,7 +1838,7 @@ function validateOptionalListSearchMetadata(label, value, kind) {
|
|
|
1821
1838
|
}
|
|
1822
1839
|
return validateEquationReadMetadata(label, value);
|
|
1823
1840
|
}
|
|
1824
|
-
function
|
|
1841
|
+
function validatePassageMetadata(label, value) {
|
|
1825
1842
|
const metadata = assertRecord(label, value);
|
|
1826
1843
|
rejectUnexpectedKeys(label, metadata, PASSAGE_METADATA_KEYS);
|
|
1827
1844
|
const documentId = assertDocId(`${label}.documentId`, metadata.documentId);
|
|
@@ -1951,6 +1968,19 @@ function validatePassageReadSuccessItem(label, item, options) {
|
|
|
1951
1968
|
const contentResult = validateReadContent(label, item, itemFormat, {
|
|
1952
1969
|
expectedMaxCharsPerItem: options.expectedMaxCharsPerItem
|
|
1953
1970
|
});
|
|
1971
|
+
const metadataRequested = options.expectedInclude?.has("metadata") ?? false;
|
|
1972
|
+
if (Object.prototype.hasOwnProperty.call(item, "metadata")) {
|
|
1973
|
+
if (!metadataRequested) {
|
|
1974
|
+
throw new PapiersCliResponseValidationError(
|
|
1975
|
+
`${label} returned metadata that was not requested.`
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
validatePassageMetadata(`${label} metadata`, item.metadata);
|
|
1979
|
+
} else if (metadataRequested) {
|
|
1980
|
+
throw new PapiersCliResponseValidationError(
|
|
1981
|
+
`${label} did not return requested metadata.`
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1954
1984
|
const hasReadTruncatedWarning = validateOptionalReadWarnings(
|
|
1955
1985
|
`${label} warnings`,
|
|
1956
1986
|
item.warnings,
|
|
@@ -2383,7 +2413,8 @@ function validateReadItemError(label, value, kind, expectedFormat) {
|
|
|
2383
2413
|
const sourceSupersededAllowed = error.code === DOCUMENT_READ_SOURCE_SUPERSEDED_CODE && (kind === "document" || kind === "passage" || kind === "note" || kind === "conversation");
|
|
2384
2414
|
const contentLimitAllowed = error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE && kind === "conversation";
|
|
2385
2415
|
const requestLimitAllowed = error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE && kind === "conversation";
|
|
2386
|
-
|
|
2416
|
+
const staleReferenceKind = error.code === READ_STALE_REFERENCE_CODE && (kind === "passage" || kind === "equation") ? kind : null;
|
|
2417
|
+
if (error.code !== DOCUMENT_READ_NOT_FOUND_CODE && !sourceSupersededAllowed && !contentLimitAllowed && !requestLimitAllowed && !staleReferenceKind) {
|
|
2387
2418
|
throw new PapiersCliResponseValidationError(
|
|
2388
2419
|
`${label}.code was not a supported read error code.`
|
|
2389
2420
|
);
|
|
@@ -2393,7 +2424,7 @@ function validateReadItemError(label, value, kind, expectedFormat) {
|
|
|
2393
2424
|
`${label}.code was not supported for metadata reads.`
|
|
2394
2425
|
);
|
|
2395
2426
|
}
|
|
2396
|
-
const expectedMessage = error.code === DOCUMENT_READ_NOT_FOUND_CODE ? readNotFoundMessageForKind(kind) : error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE ? CONVERSATION_READ_CONTENT_LIMIT_MESSAGE : error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE ? CONVERSATION_READ_REQUEST_LIMIT_MESSAGE : kind === "conversation" ? CONVERSATION_READ_SOURCE_SUPERSEDED_MESSAGE : kind === "passage" ? PASSAGE_READ_CONTENT_UNAVAILABLE_MESSAGE : kind === "note" ? NOTE_READ_SOURCE_SUPERSEDED_MESSAGE : DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE;
|
|
2427
|
+
const expectedMessage = staleReferenceKind ? staleReferenceMessage(staleReferenceKind) : error.code === DOCUMENT_READ_NOT_FOUND_CODE ? readNotFoundMessageForKind(kind) : error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE ? CONVERSATION_READ_CONTENT_LIMIT_MESSAGE : error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE ? CONVERSATION_READ_REQUEST_LIMIT_MESSAGE : kind === "conversation" ? CONVERSATION_READ_SOURCE_SUPERSEDED_MESSAGE : kind === "passage" ? PASSAGE_READ_CONTENT_UNAVAILABLE_MESSAGE : kind === "note" ? NOTE_READ_SOURCE_SUPERSEDED_MESSAGE : DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE;
|
|
2397
2428
|
if (error.message !== expectedMessage) {
|
|
2398
2429
|
throw new PapiersCliResponseValidationError(
|
|
2399
2430
|
`${label}.message was not a supported read error message.`
|
|
@@ -2920,14 +2951,6 @@ function assertReadIdentifier(label, value) {
|
|
|
2920
2951
|
}
|
|
2921
2952
|
return value;
|
|
2922
2953
|
}
|
|
2923
|
-
function assertTokenId(label, value) {
|
|
2924
|
-
if (typeof value !== "string" || !isTokenUuid(value)) {
|
|
2925
|
-
throw new PapiersCliResponseValidationError(
|
|
2926
|
-
`${label} was not a token UUID.`
|
|
2927
|
-
);
|
|
2928
|
-
}
|
|
2929
|
-
return value;
|
|
2930
|
-
}
|
|
2931
2954
|
function assertPublicRequestId(label, value) {
|
|
2932
2955
|
if (typeof value !== "string" || !SAFE_REQUEST_ID_PATTERN.test(value)) {
|
|
2933
2956
|
throw new PapiersCliResponseValidationError(
|
|
@@ -3015,7 +3038,7 @@ async function getCapabilities(config, fetchImpl) {
|
|
|
3015
3038
|
config,
|
|
3016
3039
|
fetchImpl,
|
|
3017
3040
|
"/api/v1/papiers/capabilities",
|
|
3018
|
-
{ method: "GET"
|
|
3041
|
+
{ method: "GET" }
|
|
3019
3042
|
);
|
|
3020
3043
|
assertPapiersCapabilitiesResponse("capabilities", body);
|
|
3021
3044
|
return body;
|
|
@@ -3026,7 +3049,7 @@ async function listDocuments(config, fetchImpl, request) {
|
|
|
3026
3049
|
config,
|
|
3027
3050
|
fetchImpl,
|
|
3028
3051
|
"/api/v1/papiers/list",
|
|
3029
|
-
{ method: "POST", body: request
|
|
3052
|
+
{ method: "POST", body: request }
|
|
3030
3053
|
);
|
|
3031
3054
|
assertPapiersListResponse("list", body, { expectedKind });
|
|
3032
3055
|
return body;
|
|
@@ -3037,7 +3060,7 @@ async function searchDocuments(config, fetchImpl, request) {
|
|
|
3037
3060
|
config,
|
|
3038
3061
|
fetchImpl,
|
|
3039
3062
|
"/api/v1/papiers/search",
|
|
3040
|
-
{ method: "POST", body: request
|
|
3063
|
+
{ method: "POST", body: request }
|
|
3041
3064
|
);
|
|
3042
3065
|
assertPapiersSearchResponse("search", body, request.query, expected);
|
|
3043
3066
|
return body;
|
|
@@ -3047,7 +3070,7 @@ async function browseSearchResults(config, fetchImpl, request) {
|
|
|
3047
3070
|
config,
|
|
3048
3071
|
fetchImpl,
|
|
3049
3072
|
"/api/v1/papiers/search/results",
|
|
3050
|
-
{ method: "POST", body: request
|
|
3073
|
+
{ method: "POST", body: request }
|
|
3051
3074
|
);
|
|
3052
3075
|
assertPapiersSearchResultsResponse("search results", body, request);
|
|
3053
3076
|
return body;
|
|
@@ -3057,7 +3080,7 @@ async function readDocuments(config, fetchImpl, request) {
|
|
|
3057
3080
|
config,
|
|
3058
3081
|
fetchImpl,
|
|
3059
3082
|
"/api/v1/papiers/read",
|
|
3060
|
-
{ method: "POST", body: request
|
|
3083
|
+
{ method: "POST", body: request }
|
|
3061
3084
|
);
|
|
3062
3085
|
const readKind = readKindForIdentifier(request.ids[0] ?? "");
|
|
3063
3086
|
assertPapiersReadResponse("read", body, request.ids, {
|
|
@@ -3070,26 +3093,6 @@ async function readDocuments(config, fetchImpl, request) {
|
|
|
3070
3093
|
});
|
|
3071
3094
|
return body;
|
|
3072
3095
|
}
|
|
3073
|
-
async function listPapiersTokens(config, fetchImpl) {
|
|
3074
|
-
const body = await requestPapiersApi(
|
|
3075
|
-
config,
|
|
3076
|
-
fetchImpl,
|
|
3077
|
-
"/api/v1/papiers/tokens",
|
|
3078
|
-
{ method: "GET", retryable: true }
|
|
3079
|
-
);
|
|
3080
|
-
assertPapiersTokenListResponse("tokens list", body);
|
|
3081
|
-
return body;
|
|
3082
|
-
}
|
|
3083
|
-
async function revokePapiersToken(config, fetchImpl, tokenId) {
|
|
3084
|
-
const body = await requestPapiersApi(
|
|
3085
|
-
config,
|
|
3086
|
-
fetchImpl,
|
|
3087
|
-
`/api/v1/papiers/tokens/${encodeURIComponent(tokenId)}/revoke`,
|
|
3088
|
-
{ method: "POST" }
|
|
3089
|
-
);
|
|
3090
|
-
assertPapiersTokenRevokeResponse("tokens revoke", body, tokenId);
|
|
3091
|
-
return body;
|
|
3092
|
-
}
|
|
3093
3096
|
function expectedKindForListSearchRequest(request) {
|
|
3094
3097
|
const kinds = request.kinds;
|
|
3095
3098
|
if (kinds === void 0) {
|
|
@@ -3135,6 +3138,38 @@ function expectedKindsForSearchRequest(request) {
|
|
|
3135
3138
|
const [onlyKind] = expectedKinds;
|
|
3136
3139
|
return expectedKinds.size === 1 ? { expectedKind: onlyKind } : { expectedKinds };
|
|
3137
3140
|
}
|
|
3141
|
+
async function importDocument(config, fetchImpl, request) {
|
|
3142
|
+
const body = await requestPapiersApi(
|
|
3143
|
+
config,
|
|
3144
|
+
fetchImpl,
|
|
3145
|
+
"/api/v1/papiers/imports",
|
|
3146
|
+
{ method: "POST", body: request }
|
|
3147
|
+
);
|
|
3148
|
+
assertPapiersImportResponse("import", body);
|
|
3149
|
+
return body;
|
|
3150
|
+
}
|
|
3151
|
+
async function getImportStatus(config, fetchImpl, operationId) {
|
|
3152
|
+
const body = await requestPapiersApi(
|
|
3153
|
+
config,
|
|
3154
|
+
fetchImpl,
|
|
3155
|
+
`/api/v1/papiers/imports/${encodeURIComponent(operationId)}`,
|
|
3156
|
+
{ method: "GET" }
|
|
3157
|
+
);
|
|
3158
|
+
assertPapiersImportResponse("import-status", body);
|
|
3159
|
+
return body;
|
|
3160
|
+
}
|
|
3161
|
+
async function highlightDocument(config, fetchImpl, request) {
|
|
3162
|
+
const body = await requestPapiersApi(
|
|
3163
|
+
config,
|
|
3164
|
+
fetchImpl,
|
|
3165
|
+
"/api/v1/papiers/highlights",
|
|
3166
|
+
// Outlasts the server's 10 s queue wait plus 20 s search, so its answer
|
|
3167
|
+
// arrives.
|
|
3168
|
+
{ method: "POST", body: request, timeoutMs: 4e4 }
|
|
3169
|
+
);
|
|
3170
|
+
assertPapiersHighlightResponse("highlight", body);
|
|
3171
|
+
return body;
|
|
3172
|
+
}
|
|
3138
3173
|
async function requestPapiersApi(config, fetchImpl, path, init) {
|
|
3139
3174
|
if (!config.token) {
|
|
3140
3175
|
throw new CliUsageError(
|
|
@@ -3164,10 +3199,13 @@ async function requestPapiersApiWithBearer(config, fetchImpl, bearerToken, path,
|
|
|
3164
3199
|
headers["content-type"] = "application/json";
|
|
3165
3200
|
requestInit.body = JSON.stringify(init.body);
|
|
3166
3201
|
}
|
|
3167
|
-
const response = await fetchWithRetry(
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3202
|
+
const { response, text } = await fetchWithRetry(
|
|
3203
|
+
fetchImpl,
|
|
3204
|
+
requestUrl,
|
|
3205
|
+
requestInit,
|
|
3206
|
+
init.timeoutMs ?? REQUEST_TIMEOUT_MS
|
|
3207
|
+
);
|
|
3208
|
+
const body = parseResponseBody(text);
|
|
3171
3209
|
if (!response.ok) {
|
|
3172
3210
|
const errorEnvelope = extractErrorEnvelope(body, response.status);
|
|
3173
3211
|
throw new PapiersCliApiError({
|
|
@@ -3179,44 +3217,45 @@ async function requestPapiersApiWithBearer(config, fetchImpl, bearerToken, path,
|
|
|
3179
3217
|
}
|
|
3180
3218
|
return body;
|
|
3181
3219
|
}
|
|
3182
|
-
async function fetchWithRetry(fetchImpl, requestUrl, requestInit,
|
|
3183
|
-
const maxAttempts = options.retryable ? MAX_REQUEST_ATTEMPTS : 1;
|
|
3220
|
+
async function fetchWithRetry(fetchImpl, requestUrl, requestInit, timeoutMs) {
|
|
3184
3221
|
let attempt = 1;
|
|
3185
3222
|
while (true) {
|
|
3186
3223
|
let response;
|
|
3224
|
+
let text;
|
|
3187
3225
|
try {
|
|
3188
3226
|
response = await fetchImpl(requestUrl, {
|
|
3189
3227
|
...requestInit,
|
|
3190
|
-
signal: AbortSignal.timeout(
|
|
3228
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
3191
3229
|
});
|
|
3230
|
+
text = await response.text();
|
|
3192
3231
|
} catch (error) {
|
|
3193
|
-
if (attempt >=
|
|
3232
|
+
if (attempt >= MAX_REQUEST_ATTEMPTS) {
|
|
3194
3233
|
throw error;
|
|
3195
3234
|
}
|
|
3196
3235
|
await sleep(RETRY_BACKOFF_MS[attempt - 1] ?? 1e3);
|
|
3197
3236
|
attempt += 1;
|
|
3198
3237
|
continue;
|
|
3199
3238
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3239
|
+
const delayMs = retryDelayMs(response, attempt);
|
|
3240
|
+
if (attempt < MAX_REQUEST_ATTEMPTS && RETRYABLE_STATUS_CODES.has(response.status) && delayMs <= MAX_RETRY_AFTER_MS) {
|
|
3241
|
+
await sleep(delayMs);
|
|
3202
3242
|
attempt += 1;
|
|
3203
3243
|
continue;
|
|
3204
3244
|
}
|
|
3205
|
-
return response;
|
|
3245
|
+
return { response, text };
|
|
3206
3246
|
}
|
|
3207
3247
|
}
|
|
3208
3248
|
function retryDelayMs(response, attempt) {
|
|
3209
3249
|
const retryAfterSeconds = Number(response.headers.get("retry-after") ?? "");
|
|
3210
3250
|
if (Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0) {
|
|
3211
|
-
return
|
|
3251
|
+
return retryAfterSeconds * 1e3;
|
|
3212
3252
|
}
|
|
3213
3253
|
return RETRY_BACKOFF_MS[attempt - 1] ?? 1e3;
|
|
3214
3254
|
}
|
|
3215
3255
|
function sleep(ms) {
|
|
3216
3256
|
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
3217
3257
|
}
|
|
3218
|
-
|
|
3219
|
-
const text = await response.text();
|
|
3258
|
+
function parseResponseBody(text) {
|
|
3220
3259
|
if (!text) {
|
|
3221
3260
|
return null;
|
|
3222
3261
|
}
|
|
@@ -3241,6 +3280,26 @@ function formatApiErrorMessage(params) {
|
|
|
3241
3280
|
return `Papiers API error ${params.status}.`;
|
|
3242
3281
|
}
|
|
3243
3282
|
|
|
3283
|
+
// ../../src/shared/highlight-colors.ts
|
|
3284
|
+
var HIGHLIGHT_HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
|
|
3285
|
+
|
|
3286
|
+
// ../../src/shared/papiers-public-api.ts
|
|
3287
|
+
var PAPIERS_PUBLIC_DATA_PLANE_SCOPES2 = [
|
|
3288
|
+
"library:metadata:read",
|
|
3289
|
+
"library:search",
|
|
3290
|
+
"content:search",
|
|
3291
|
+
"content:snippet:read",
|
|
3292
|
+
"content:read",
|
|
3293
|
+
"content:structure:read"
|
|
3294
|
+
];
|
|
3295
|
+
var PAPIERS_PUBLIC_SCOPES = [
|
|
3296
|
+
...PAPIERS_PUBLIC_DATA_PLANE_SCOPES2,
|
|
3297
|
+
"library:import",
|
|
3298
|
+
"library:highlight"
|
|
3299
|
+
];
|
|
3300
|
+
var PAPIERS_HIGHLIGHT_MAX_SCOPE_PAGES = 20;
|
|
3301
|
+
var PAPIERS_HIGHLIGHT_MAX_PAGE = 1e4;
|
|
3302
|
+
|
|
3244
3303
|
// src/filters.ts
|
|
3245
3304
|
var FILTER_OPERATORS = [
|
|
3246
3305
|
"eq",
|
|
@@ -3709,7 +3768,16 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
3709
3768
|
"fail-fast",
|
|
3710
3769
|
"version"
|
|
3711
3770
|
]);
|
|
3771
|
+
var HIGHLIGHT_VALUE_FLAGS = [
|
|
3772
|
+
"start",
|
|
3773
|
+
"end",
|
|
3774
|
+
"passage",
|
|
3775
|
+
"pages",
|
|
3776
|
+
"note",
|
|
3777
|
+
"color"
|
|
3778
|
+
];
|
|
3712
3779
|
var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
3780
|
+
...HIGHLIGHT_VALUE_FLAGS,
|
|
3713
3781
|
"api-url",
|
|
3714
3782
|
"cursor",
|
|
3715
3783
|
"document-id",
|
|
@@ -3827,8 +3895,9 @@ COMMANDS
|
|
|
3827
3895
|
list List your documents, highlights, notes, and conversations
|
|
3828
3896
|
read Print a document, highlight, note, or conversation
|
|
3829
3897
|
browse Page through an earlier search's results
|
|
3898
|
+
import Add a public PDF or webpage to your library
|
|
3899
|
+
highlight Highlight a passage in a saved document
|
|
3830
3900
|
auth Sign in, check status, or sign out
|
|
3831
|
-
tokens List or revoke your API tokens
|
|
3832
3901
|
doctor Check your setup, connection, and what's available
|
|
3833
3902
|
version Print the CLI version
|
|
3834
3903
|
|
|
@@ -3886,7 +3955,7 @@ USAGE
|
|
|
3886
3955
|
papiers list --kind highlight|conversation [--since WHEN]
|
|
3887
3956
|
|
|
3888
3957
|
OPTIONS
|
|
3889
|
-
--kind KIND document (default) \xB7
|
|
3958
|
+
--kind KIND document (default) \xB7 highlight \xB7 conversation \xB7 note.
|
|
3890
3959
|
--filter FIELD.OP=VALUE narrow document results \u2014 repeatable, up to 25. document kind only.
|
|
3891
3960
|
e.g. --filter lastEngagedAt.gte=30d --filter annotated=true. Fields/ops: help --all.
|
|
3892
3961
|
--order-by KEY addedAt:desc (default) or lastEngagedAt:desc. document kind only.
|
|
@@ -3916,7 +3985,7 @@ OPTIONS
|
|
|
3916
3985
|
highlight, conversation: metadata|plain. note: markdown|plain.
|
|
3917
3986
|
equation: metadata.
|
|
3918
3987
|
--include SECTION extra sections, comma-separated: metadata, source, locations.
|
|
3919
|
-
document only (highlight/conversation/equation/note: metadata only
|
|
3988
|
+
document only (passage/highlight/conversation/equation/note: metadata only).
|
|
3920
3989
|
--pages RANGE restrict a document read to pages, e.g. 1,3-5. document only.
|
|
3921
3990
|
--max-chars-per-item N truncate each item's content to N chars, 1\u2013200000.
|
|
3922
3991
|
--cursor CURSOR continue one long document or note \u2014 use items[].nextCursor from the
|
|
@@ -3959,13 +4028,40 @@ OPTIONS
|
|
|
3959
4028
|
--force when signing in, replace an already-saved token.
|
|
3960
4029
|
|
|
3961
4030
|
Signing in saves your token on this machine. For scripts or an AI agent, set PAPIERS_TOKEN instead.`,
|
|
3962
|
-
|
|
4031
|
+
import: `papiers import \u2014 add a public PDF or webpage to your library.
|
|
3963
4032
|
|
|
3964
4033
|
USAGE
|
|
3965
|
-
papiers
|
|
3966
|
-
papiers
|
|
4034
|
+
papiers import URL [--json]
|
|
4035
|
+
papiers import-status OPERATION_ID [--json]
|
|
4036
|
+
|
|
4037
|
+
Needs a token created with "Allow document imports" (Settings -> API tokens).
|
|
4038
|
+
Import only sources the user approved. An import is queued, not finished:
|
|
4039
|
+
check import-status until it is ready or failed; any other status means it
|
|
4040
|
+
is still in progress. Ready means papiers read works on its documentId now.
|
|
4041
|
+
A status is as of that check: reprocessing or trashing the document can
|
|
4042
|
+
change it later.`,
|
|
4043
|
+
highlight: `papiers highlight \u2014 highlight a passage in a saved PDF or webpage.
|
|
3967
4044
|
|
|
3968
|
-
|
|
4045
|
+
USAGE
|
|
4046
|
+
papiers highlight DOCUMENT_ID --start TEXT [--end TEXT]
|
|
4047
|
+
[--passage PASSAGE_REF | --pages N[-M]]
|
|
4048
|
+
[--note TEXT] [--color #rrggbb] [--json]
|
|
4049
|
+
|
|
4050
|
+
Needs a token created with "Allow highlights" (Settings -> API tokens).
|
|
4051
|
+
DOCUMENT_ID is a doc_ ID or papiers://item/... locator. In a PDF, name what you
|
|
4052
|
+
read: --passage is the reference of a search result in this document
|
|
4053
|
+
(doc_\u2026#p\u2026.\u2026), --pages the page or pages (3, 3-4 or 3,5) you read it on. The
|
|
4054
|
+
passage is only looked for there; a webpage is searched whole and takes neither.
|
|
4055
|
+
--start is the passage's first words (or all of a short passage); --end its
|
|
4056
|
+
last words, for a longer passage or one crossing a formula. The highlight runs
|
|
4057
|
+
from --start through --end, so formulas and footnote marks between need not be
|
|
4058
|
+
quoted; if either repeats within reach (that page and the next, a few
|
|
4059
|
+
paragraphs), add words to it. Quote both as you read them; punctuation, hyphens,
|
|
4060
|
+
spacing and formatting don't matter. Keep math out of --start and --end, even
|
|
4061
|
+
a single symbol like V or \u03B2t: what you read spells it differently from the
|
|
4062
|
+
page, so start and end on plain words. If the passage appears more than once,
|
|
4063
|
+
add words until it is unique. Highlight only passages the user asked for or
|
|
4064
|
+
approved.`,
|
|
3969
4065
|
doctor: `papiers doctor \u2014 check your setup, connection, and what's available.
|
|
3970
4066
|
|
|
3971
4067
|
USAGE
|
|
@@ -3981,10 +4077,18 @@ USAGE
|
|
|
3981
4077
|
var REFERENCE = `REFERENCE \u2014 IDs, piping, pagination, and limits.
|
|
3982
4078
|
|
|
3983
4079
|
ID PREFIXES
|
|
3984
|
-
doc_ document
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
4080
|
+
doc_ document hl_ highlight cnv_ conversation
|
|
4081
|
+
nte_ note srch_ a saved search ranking (for browse)
|
|
4082
|
+
On read, the kind \u2014 and which --format/--include apply \u2014 is read from the ID.
|
|
4083
|
+
|
|
4084
|
+
PASSAGES AND EQUATIONS
|
|
4085
|
+
A passage or equation is a place in a document plus a fingerprint of its text:
|
|
4086
|
+
doc_<id>#p12.3f9c01aa passage 12 of that document
|
|
4087
|
+
doc_<id>#enode_812.77d0e2b1 an equation in that document
|
|
4088
|
+
It keeps working while that text is unchanged. If the document is processed
|
|
4089
|
+
again and the text changed, you get stale_reference: search or read the
|
|
4090
|
+
document again. Quote references in the shell ('doc_\u2026#p12.3f9c01aa'): some
|
|
4091
|
+
shells treat # specially.
|
|
3988
4092
|
|
|
3989
4093
|
RESOURCE LOCATORS
|
|
3990
4094
|
papiers://note/<uuid> papiers://highlight/<uuid>
|
|
@@ -4041,11 +4145,12 @@ var SUGGESTIBLE_COMMANDS = [
|
|
|
4041
4145
|
"auth",
|
|
4042
4146
|
"doctor",
|
|
4043
4147
|
"capabilities",
|
|
4044
|
-
"tokens",
|
|
4045
4148
|
"list",
|
|
4046
4149
|
"search",
|
|
4047
4150
|
"browse",
|
|
4048
4151
|
"read",
|
|
4152
|
+
"import",
|
|
4153
|
+
"highlight",
|
|
4049
4154
|
"version",
|
|
4050
4155
|
"help"
|
|
4051
4156
|
];
|
|
@@ -4098,7 +4203,8 @@ function parseCliArgs(argv) {
|
|
|
4098
4203
|
if (parsed.booleanFlags.has("all")) {
|
|
4099
4204
|
return { type: "help", all: true, ...global };
|
|
4100
4205
|
}
|
|
4101
|
-
const
|
|
4206
|
+
const named = command === "help" ? rest[0] : command;
|
|
4207
|
+
const topic = named === "import-status" ? "import" : named;
|
|
4102
4208
|
if (topic && HELP_TOPIC_NAMES.includes(topic)) {
|
|
4103
4209
|
return { type: "help", topic, ...global };
|
|
4104
4210
|
}
|
|
@@ -4125,9 +4231,61 @@ function parseCliArgs(argv) {
|
|
|
4125
4231
|
);
|
|
4126
4232
|
assertNoExtraPositionals(command, rest);
|
|
4127
4233
|
return { type: "capabilities", ...global };
|
|
4128
|
-
case "
|
|
4129
|
-
|
|
4130
|
-
|
|
4234
|
+
case "import":
|
|
4235
|
+
case "import-status": {
|
|
4236
|
+
assertAllowedFlags(parsed, NO_COMMAND_VALUE_FLAGS, command);
|
|
4237
|
+
assertAllowedBooleanFlags(parsed, NO_COMMAND_BOOLEAN_FLAGS, command);
|
|
4238
|
+
const [target, ...extra] = rest;
|
|
4239
|
+
assertNoExtraPositionals(command, extra);
|
|
4240
|
+
if (!target) {
|
|
4241
|
+
throw new CliUsageError(
|
|
4242
|
+
command === "import" ? "papiers import requires a URL." : "papiers import-status requires an operation ID."
|
|
4243
|
+
);
|
|
4244
|
+
}
|
|
4245
|
+
return command === "import" ? { type: "import", ...global, url: target } : { type: "import-status", ...global, operationId: target };
|
|
4246
|
+
}
|
|
4247
|
+
case "highlight": {
|
|
4248
|
+
assertAllowedFlags(
|
|
4249
|
+
parsed,
|
|
4250
|
+
/* @__PURE__ */ new Set([...GLOBAL_VALUE_FLAGS, ...HIGHLIGHT_VALUE_FLAGS]),
|
|
4251
|
+
command
|
|
4252
|
+
);
|
|
4253
|
+
assertAllowedBooleanFlags(parsed, NO_COMMAND_BOOLEAN_FLAGS, command);
|
|
4254
|
+
const [documentId, ...extra] = rest;
|
|
4255
|
+
assertNoExtraPositionals(command, extra);
|
|
4256
|
+
const start = singleFlagValue(parsed, "start");
|
|
4257
|
+
if (!documentId || !start) {
|
|
4258
|
+
throw new CliUsageError(
|
|
4259
|
+
"papiers highlight requires a document ID and --start."
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
const passageId = singleFlagValue(parsed, "passage");
|
|
4263
|
+
const pagesFlag = singleFlagValue(parsed, "pages");
|
|
4264
|
+
if (passageId !== void 0 && pagesFlag !== void 0) {
|
|
4265
|
+
throw new CliUsageError("Give --passage or --pages, not both.");
|
|
4266
|
+
}
|
|
4267
|
+
const color = singleFlagValue(parsed, "color");
|
|
4268
|
+
if (color !== void 0 && !HIGHLIGHT_HEX_COLOR_REGEX.test(color)) {
|
|
4269
|
+
throw new CliUsageError(
|
|
4270
|
+
"--color must be a lowercase #rrggbb hex color, like #fde68a."
|
|
4271
|
+
);
|
|
4272
|
+
}
|
|
4273
|
+
const optional = (name) => {
|
|
4274
|
+
const value = singleFlagValue(parsed, name);
|
|
4275
|
+
return value === void 0 ? {} : { [name]: value };
|
|
4276
|
+
};
|
|
4277
|
+
return {
|
|
4278
|
+
type: "highlight",
|
|
4279
|
+
...global,
|
|
4280
|
+
documentId,
|
|
4281
|
+
start,
|
|
4282
|
+
...optional("end"),
|
|
4283
|
+
...passageId === void 0 ? {} : { passageId },
|
|
4284
|
+
...pagesFlag === void 0 ? {} : { pages: parsePages(pagesFlag) },
|
|
4285
|
+
...optional("note"),
|
|
4286
|
+
...optional("color")
|
|
4287
|
+
};
|
|
4288
|
+
}
|
|
4131
4289
|
case "list": {
|
|
4132
4290
|
assertAllowedFlags(parsed, LIST_VALUE_FLAGS, "list");
|
|
4133
4291
|
assertAllowedBooleanFlags(parsed, IDS_PIPELINE_BOOLEAN_FLAGS, "list");
|
|
@@ -4226,35 +4384,6 @@ function parsePrettyOutput(parsed, idsOnly) {
|
|
|
4226
4384
|
}
|
|
4227
4385
|
return true;
|
|
4228
4386
|
}
|
|
4229
|
-
function parseTokensCommand(rest, parsed, global) {
|
|
4230
|
-
const [subcommand, tokenId, ...extra] = rest;
|
|
4231
|
-
switch (subcommand) {
|
|
4232
|
-
case "list":
|
|
4233
|
-
assertAllowedBooleanFlags(
|
|
4234
|
-
parsed,
|
|
4235
|
-
NO_COMMAND_BOOLEAN_FLAGS,
|
|
4236
|
-
"tokens list"
|
|
4237
|
-
);
|
|
4238
|
-
assertNoExtraPositionals("tokens list", rest.slice(1));
|
|
4239
|
-
return { type: "tokens-list", ...global };
|
|
4240
|
-
case "revoke":
|
|
4241
|
-
assertAllowedBooleanFlags(
|
|
4242
|
-
parsed,
|
|
4243
|
-
NO_COMMAND_BOOLEAN_FLAGS,
|
|
4244
|
-
"tokens revoke"
|
|
4245
|
-
);
|
|
4246
|
-
if (!tokenId) {
|
|
4247
|
-
throw new CliUsageError("papiers tokens revoke requires a token UUID.");
|
|
4248
|
-
}
|
|
4249
|
-
assertNoExtraPositionals("tokens revoke", extra);
|
|
4250
|
-
if (!isUuid(tokenId)) {
|
|
4251
|
-
throw new CliUsageError("papiers tokens revoke requires a token UUID.");
|
|
4252
|
-
}
|
|
4253
|
-
return { type: "tokens-revoke", ...global, tokenId };
|
|
4254
|
-
default:
|
|
4255
|
-
throw new CliUsageError("Supported tokens commands: list, revoke.");
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
4387
|
function parseAuthCommand(rest, parsed, global) {
|
|
4259
4388
|
const [subcommand, ...extra] = rest;
|
|
4260
4389
|
switch (subcommand) {
|
|
@@ -4357,7 +4486,7 @@ function buildReadRequest(parsed) {
|
|
|
4357
4486
|
};
|
|
4358
4487
|
}
|
|
4359
4488
|
function buildListRequest(parsed, documentIds) {
|
|
4360
|
-
const kind =
|
|
4489
|
+
const kind = parseListKind(parsed);
|
|
4361
4490
|
const since = parseSinceFlag(parsed, kind, "list");
|
|
4362
4491
|
if (kind !== "document") {
|
|
4363
4492
|
if (documentIds?.length && kind !== "highlight") {
|
|
@@ -4649,7 +4778,13 @@ function validateReadOptionsForKind(parsed, kind, format) {
|
|
|
4649
4778
|
"Public passage read supports --format markdown or --format plain."
|
|
4650
4779
|
);
|
|
4651
4780
|
}
|
|
4652
|
-
|
|
4781
|
+
const includes = parseReadInclude(parsed);
|
|
4782
|
+
if (includes?.some((include) => include !== "metadata")) {
|
|
4783
|
+
throw new CliUsageError(
|
|
4784
|
+
"Public passage read supports only --include metadata."
|
|
4785
|
+
);
|
|
4786
|
+
}
|
|
4787
|
+
rejectReadFlagsForKind(parsed, "passage", ["pages", "cursor"]);
|
|
4653
4788
|
return;
|
|
4654
4789
|
}
|
|
4655
4790
|
if (kind === "highlight") {
|
|
@@ -4834,19 +4969,24 @@ function parseGlobalOptions(parsed) {
|
|
|
4834
4969
|
apiBaseUrl: singleFlagValue(parsed, "api-url")
|
|
4835
4970
|
};
|
|
4836
4971
|
}
|
|
4837
|
-
function
|
|
4972
|
+
function parseListKind(parsed) {
|
|
4838
4973
|
const kind = singleFlagValue(parsed, "kind") ?? "document";
|
|
4839
4974
|
const normalized = normalizeCliPublicKind(kind);
|
|
4975
|
+
if (normalized === "passage" || normalized === "equation") {
|
|
4976
|
+
throw new CliUsageError(
|
|
4977
|
+
`papiers list does not list ${normalized}s. Read a document (papiers read doc_\u2026) to page through its ${normalized}s in order, or search for them.`
|
|
4978
|
+
);
|
|
4979
|
+
}
|
|
4840
4980
|
if (normalized) {
|
|
4841
4981
|
return normalized;
|
|
4842
4982
|
}
|
|
4843
4983
|
if (kind.trim().toLowerCase() === "all") {
|
|
4844
4984
|
throw new CliUsageError(
|
|
4845
|
-
|
|
4985
|
+
"papiers list does not support --kind all here; multi-kind requests are available via papiers search --kind all."
|
|
4846
4986
|
);
|
|
4847
4987
|
}
|
|
4848
4988
|
throw new CliUsageError(
|
|
4849
|
-
`papiers
|
|
4989
|
+
`papiers list supports --kind document, highlight, conversation, or note; received "${kind}".${didYouMeanKind(kind)}`
|
|
4850
4990
|
);
|
|
4851
4991
|
}
|
|
4852
4992
|
function normalizeCliPublicKind(value) {
|
|
@@ -5025,11 +5165,6 @@ function singleFlagValue(parsed, name) {
|
|
|
5025
5165
|
}
|
|
5026
5166
|
return values[0];
|
|
5027
5167
|
}
|
|
5028
|
-
function isUuid(value) {
|
|
5029
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(
|
|
5030
|
-
value
|
|
5031
|
-
);
|
|
5032
|
-
}
|
|
5033
5168
|
function splitCommaSeparatedValues(rawValue) {
|
|
5034
5169
|
return rawValue.split(",").map((value) => value.trim()).filter(Boolean);
|
|
5035
5170
|
}
|
|
@@ -5045,6 +5180,26 @@ function pruneUndefined(value) {
|
|
|
5045
5180
|
Object.entries(value).filter(([, entry]) => entry !== void 0)
|
|
5046
5181
|
);
|
|
5047
5182
|
}
|
|
5183
|
+
function parsePages(value) {
|
|
5184
|
+
const pages = [];
|
|
5185
|
+
for (const part of value.split(",")) {
|
|
5186
|
+
const match = /^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/.exec(part);
|
|
5187
|
+
const first = match ? Number(match[1]) : Number.NaN;
|
|
5188
|
+
const last = match?.[2] ? Number(match[2]) : first;
|
|
5189
|
+
if (!(first >= 1 && first <= last && last <= PAPIERS_HIGHLIGHT_MAX_PAGE)) {
|
|
5190
|
+
throw new CliUsageError(
|
|
5191
|
+
`--pages must be a page, a range like 3-4, or a list like 3,5, of pages up to ${PAPIERS_HIGHLIGHT_MAX_PAGE}.`
|
|
5192
|
+
);
|
|
5193
|
+
}
|
|
5194
|
+
if (pages.length + (last - first + 1) > PAPIERS_HIGHLIGHT_MAX_SCOPE_PAGES) {
|
|
5195
|
+
throw new CliUsageError(
|
|
5196
|
+
`--pages can name at most ${PAPIERS_HIGHLIGHT_MAX_SCOPE_PAGES} pages.`
|
|
5197
|
+
);
|
|
5198
|
+
}
|
|
5199
|
+
for (let page = first; page <= last; page++) pages.push(page);
|
|
5200
|
+
}
|
|
5201
|
+
return pages;
|
|
5202
|
+
}
|
|
5048
5203
|
|
|
5049
5204
|
// src/capabilities.ts
|
|
5050
5205
|
var CAPABILITIES_ACCOUNT_KEYS = [
|
|
@@ -5074,11 +5229,11 @@ function assertSupportedDocumentCapabilities(value) {
|
|
|
5074
5229
|
assertNonEmptyString2("capabilities account.userId", account.userId);
|
|
5075
5230
|
assertExactString("capabilities account.profile", account.profile, "pat-v1");
|
|
5076
5231
|
const scopes = assertStringArray("capabilities scopes", capabilities.scopes);
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5232
|
+
if (!PAPIERS_PUBLIC_DATA_PLANE_SCOPES.every((s) => scopes.includes(s))) {
|
|
5233
|
+
throw new Error(
|
|
5234
|
+
`capabilities scopes was ${scopes.join(", ") || "empty"}; expected every public read scope.`
|
|
5235
|
+
);
|
|
5236
|
+
}
|
|
5082
5237
|
const searchKinds = assertStringArray(
|
|
5083
5238
|
"capabilities searchKinds",
|
|
5084
5239
|
capabilities.searchKinds
|
|
@@ -5740,22 +5895,36 @@ async function runCli(argv, dependencies = {}) {
|
|
|
5740
5895
|
);
|
|
5741
5896
|
return 0;
|
|
5742
5897
|
}
|
|
5743
|
-
case "
|
|
5744
|
-
|
|
5898
|
+
case "import":
|
|
5899
|
+
writeJson(
|
|
5900
|
+
stdout,
|
|
5901
|
+
await importDocument(config, resolveFetch(dependencies.fetch), {
|
|
5902
|
+
schemaVersion: PAPIERS_SCHEMA_VERSIONS.importRequest,
|
|
5903
|
+
url: command.url,
|
|
5904
|
+
submissionId: randomUUID2()
|
|
5905
|
+
})
|
|
5906
|
+
);
|
|
5907
|
+
return 0;
|
|
5908
|
+
case "highlight": {
|
|
5909
|
+
const { type: _, json: __, apiBaseUrl: ___, ...highlight } = command;
|
|
5910
|
+
writeJson(
|
|
5745
5911
|
stdout,
|
|
5746
|
-
await
|
|
5747
|
-
|
|
5912
|
+
await highlightDocument(config, resolveFetch(dependencies.fetch), {
|
|
5913
|
+
schemaVersion: PAPIERS_SCHEMA_VERSIONS.highlightRequest,
|
|
5914
|
+
...highlight,
|
|
5915
|
+
referenceId: randomUUID2()
|
|
5916
|
+
})
|
|
5748
5917
|
);
|
|
5749
5918
|
return 0;
|
|
5750
|
-
|
|
5751
|
-
|
|
5919
|
+
}
|
|
5920
|
+
case "import-status":
|
|
5921
|
+
writeJson(
|
|
5752
5922
|
stdout,
|
|
5753
|
-
await
|
|
5923
|
+
await getImportStatus(
|
|
5754
5924
|
config,
|
|
5755
5925
|
resolveFetch(dependencies.fetch),
|
|
5756
|
-
command.
|
|
5757
|
-
)
|
|
5758
|
-
command.json
|
|
5926
|
+
command.operationId
|
|
5927
|
+
)
|
|
5759
5928
|
);
|
|
5760
5929
|
return 0;
|
|
5761
5930
|
case "list":
|
|
@@ -6016,50 +6185,6 @@ function writeAuthInspectOutput(stdout, output, json) {
|
|
|
6016
6185
|
`
|
|
6017
6186
|
);
|
|
6018
6187
|
}
|
|
6019
|
-
function writeTokenListOutput(stdout, output, json) {
|
|
6020
|
-
if (json) {
|
|
6021
|
-
writeJson(stdout, output);
|
|
6022
|
-
return;
|
|
6023
|
-
}
|
|
6024
|
-
if (output.tokens.length === 0) {
|
|
6025
|
-
stdout.write(
|
|
6026
|
-
`No active Papiers tokens found. requestId=${output.requestId}
|
|
6027
|
-
`
|
|
6028
|
-
);
|
|
6029
|
-
return;
|
|
6030
|
-
}
|
|
6031
|
-
const lines = [
|
|
6032
|
-
`Papiers tokens requestId=${output.requestId}`,
|
|
6033
|
-
...output.tokens.map(
|
|
6034
|
-
(token) => [
|
|
6035
|
-
token.tokenId,
|
|
6036
|
-
token.name,
|
|
6037
|
-
`scopes=${token.scopes.join(",")}`,
|
|
6038
|
-
`created=${token.createdAt}`,
|
|
6039
|
-
`expires=${token.expiresAt ?? "never"}`,
|
|
6040
|
-
`lastUsed=${token.lastUsedAt ?? "never"}`
|
|
6041
|
-
].join(" | ")
|
|
6042
|
-
)
|
|
6043
|
-
];
|
|
6044
|
-
stdout.write(`${redactSensitiveText(lines.join("\n"))}
|
|
6045
|
-
`);
|
|
6046
|
-
}
|
|
6047
|
-
function writeTokenRevokeOutput(stdout, output, json) {
|
|
6048
|
-
if (json) {
|
|
6049
|
-
writeJson(stdout, output);
|
|
6050
|
-
return;
|
|
6051
|
-
}
|
|
6052
|
-
const state = output.revoked ? "revoked" : "already revoked";
|
|
6053
|
-
stdout.write(
|
|
6054
|
-
`${redactSensitiveText(
|
|
6055
|
-
[
|
|
6056
|
-
`Token ${output.tokenId} ${state}. requestId=${output.requestId}`,
|
|
6057
|
-
"Local auth files were not changed; run papiers auth logout if this was your configured token."
|
|
6058
|
-
].join("\n")
|
|
6059
|
-
)}
|
|
6060
|
-
`
|
|
6061
|
-
);
|
|
6062
|
-
}
|
|
6063
6188
|
async function runAuthInspect(params) {
|
|
6064
6189
|
const capabilities = await getCapabilities(params.config, params.fetchImpl);
|
|
6065
6190
|
assertCliSupportedCapabilities(capabilities);
|
|
@@ -6613,9 +6738,9 @@ function validateExpandedReadRequest(request, ids, requestedReadKind) {
|
|
|
6613
6738
|
"Public passage read supports --format markdown or --format plain."
|
|
6614
6739
|
);
|
|
6615
6740
|
}
|
|
6616
|
-
if (request.include?.
|
|
6741
|
+
if (request.include?.some((include) => include !== "metadata")) {
|
|
6617
6742
|
throw new CliUsageError(
|
|
6618
|
-
"Public passage read
|
|
6743
|
+
"Public passage read supports only --include metadata."
|
|
6619
6744
|
);
|
|
6620
6745
|
}
|
|
6621
6746
|
if (request.scope?.pages || request.scope?.sectionId) {
|
package/package.json
CHANGED
|
@@ -85,6 +85,9 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
|
|
|
85
85
|
|
|
86
86
|
Most highlights carry no annotation (quote-only); `annotationText` is
|
|
87
87
|
only present on reads, so read the highlights you intend to lean on.
|
|
88
|
+
It contains sanitized Markdown source, including emphasis markers, escapes,
|
|
89
|
+
and backticks; interpret those as part of the user's note. The read's
|
|
90
|
+
`format` describes the quoted content, not the annotation's syntax.
|
|
88
91
|
|
|
89
92
|
5. **Write the judgement brief.** Before generating anything, summarize what
|
|
90
93
|
you found: verbatim annotations with dates and IDs, stances, open
|
|
@@ -111,8 +114,24 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
|
|
|
111
114
|
ISO timestamps or `30d`-style shorthand.
|
|
112
115
|
- **Honor the quality signal.** `quality.signal: "empty"` or `"low"` means
|
|
113
116
|
the library has nothing — report that rather than citing weak matches.
|
|
114
|
-
- **Cite IDs.** `hl_/cnv_/
|
|
115
|
-
|
|
117
|
+
- **Cite IDs.** Include `doc_/hl_/cnv_/nte_` IDs so the user can jump to
|
|
118
|
+
sources. A passage or equation is a place in a document plus a fingerprint
|
|
119
|
+
of its text (`'doc_<id>#p12.3f9c01aa'`); if its text has changed since you got
|
|
120
|
+
it, Papiers reports `stale_reference` — search or read the document again.
|
|
121
|
+
Quote these references in the shell: some shells treat `#` specially.
|
|
122
|
+
- **Import only what the user approved.** `papiers import URL` needs a token
|
|
123
|
+
with "Allow document imports". Show the user the exact URLs first. An import
|
|
124
|
+
is queued until `papiers import-status OPERATION_ID` says `ready`, which
|
|
125
|
+
means `papiers read` works on its `documentId`; don't call it added before
|
|
126
|
+
then. On `failed`, report `error.message`; retry only if `error.retryable`.
|
|
127
|
+
- **Highlight only what the user asked for or approved.** `papiers highlight
|
|
128
|
+
DOC_ID --start "..." [--end "..."]` needs a token with "Allow highlights".
|
|
129
|
+
In a PDF, name what you read: `--passage` with the search result's passage
|
|
130
|
+
reference, or `--pages` with the pages you read; a webpage needs neither. Give the
|
|
131
|
+
passage's first words as `--start` and, for a longer passage or one crossing
|
|
132
|
+
a formula, its last words as `--end`; both as you read them, on plain words:
|
|
133
|
+
keep math, even a single symbol like V, out of both (formatting doesn't
|
|
134
|
+
matter). If it repeats, add words until unique.
|
|
116
135
|
- **Budgets are policy, not protocol.** Default to ~10–20 queries and 3–8
|
|
117
136
|
document reads per task. The user may override any policy in this skill
|
|
118
137
|
(read more, weight recency differently, skip curation); the loop's shape
|