@lanes-sh/link 0.3.2 → 0.4.1

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.
Files changed (69) hide show
  1. package/README.md +11 -3
  2. package/instructions/agents/lanes-link-scout.md +2 -2
  3. package/instructions/skills/lanes-link/SKILL.md +135 -11
  4. package/package.json +3 -1
  5. package/src/cli/argv.ts +52 -0
  6. package/src/cli/commands/connect/authorise.ts +5 -0
  7. package/src/cli/commands/connect/custom/ask.ts +167 -0
  8. package/src/cli/commands/connect/custom/credential.ts +143 -0
  9. package/src/cli/commands/connect/custom/derive.ts +229 -0
  10. package/src/cli/commands/connect/custom/index.ts +285 -0
  11. package/src/cli/commands/connect/custom/prompts.ts +160 -0
  12. package/src/cli/commands/connect/custom/spec.ts +293 -0
  13. package/src/cli/commands/connect/custom/values.ts +53 -0
  14. package/src/cli/commands/connect/custom/write.ts +166 -0
  15. package/src/cli/commands/connect/grant.ts +27 -0
  16. package/src/cli/commands/connect/index.ts +24 -27
  17. package/src/cli/commands/connect/outcome.ts +3 -1
  18. package/src/cli/commands/connect/requirements.ts +11 -1
  19. package/src/cli/commands/connect/settle.ts +17 -0
  20. package/src/cli/commands/connect/setup.ts +9 -1
  21. package/src/cli/commands/connect/strategy.ts +87 -0
  22. package/src/cli/commands/connect/unknown.ts +41 -0
  23. package/src/cli/commands/mcp/list.ts +123 -29
  24. package/src/cli/identity.ts +29 -5
  25. package/src/cli/main.ts +42 -14
  26. package/src/cli/oauth.ts +89 -36
  27. package/src/cli/runtime/open.ts +13 -1
  28. package/src/cli/runtime/registry.ts +12 -0
  29. package/src/cli/selection.ts +12 -0
  30. package/src/cli/usage.ts +9 -1
  31. package/src/connectivity/auth/README.md +8 -1
  32. package/src/connectivity/auth/strategy/index.ts +128 -4
  33. package/src/connectivity/connector.ts +11 -0
  34. package/src/connectivity/index.ts +11 -1
  35. package/src/connectivity/manifest/auth.ts +19 -0
  36. package/src/connectivity/manifest/connector.ts +21 -0
  37. package/src/connectivity/manifest/primitives.ts +5 -1
  38. package/src/connectivity/manifest/provider.ts +30 -12
  39. package/src/connectivity/provider.ts +55 -0
  40. package/src/connectivity/transports/factory.ts +1 -0
  41. package/src/connectivity/transports/http/index.ts +73 -2
  42. package/src/dispatch/dispatch.ts +44 -5
  43. package/src/providers/bunq/hints.ts +45 -0
  44. package/src/providers/bunq/index.ts +87 -0
  45. package/src/providers/bunq/redact.ts +75 -0
  46. package/src/providers/bunq/specs/bunq.v1.json +883 -0
  47. package/src/providers/bunq/specs/vendor.ts +396 -0
  48. package/src/providers/bunq/strategy/handshake.ts +211 -0
  49. package/src/providers/bunq/strategy/index.ts +298 -0
  50. package/src/providers/bunq/strategy/keys.ts +72 -0
  51. package/src/providers/custom/index.ts +1 -6
  52. package/src/providers/custom/load.ts +56 -14
  53. package/src/providers/custom/template.ts +1 -1
  54. package/src/providers/discord/hints.ts +195 -0
  55. package/src/providers/discord/index.ts +121 -0
  56. package/src/providers/discord/redact.ts +99 -0
  57. package/src/providers/discord/specs/discord.v10.json +2333 -0
  58. package/src/providers/discord/specs/vendor.ts +164 -0
  59. package/src/providers/google/specs/vendor.ts +32 -317
  60. package/src/providers/index.ts +9 -0
  61. package/src/providers/reddit/index.ts +113 -0
  62. package/src/providers/reddit/oauth.ts +77 -0
  63. package/src/providers/reddit/redact.ts +33 -0
  64. package/src/providers/reddit/scopes.ts +27 -0
  65. package/src/providers/reddit/specs/reddit.v1.json +700 -0
  66. package/src/providers/scopes.ts +2 -0
  67. package/src/providers/shared/openapi.ts +155 -0
  68. package/src/providers/shared/vendor-operations.ts +179 -0
  69. package/src/providers/shared/vendor-spec.ts +309 -0
@@ -0,0 +1,700 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "Reddit",
5
+ "version": "1.0.0",
6
+ "description": "The subset of the Reddit API this provider exposes. Hand-authored: Reddit publishes no OpenAPI document."
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://oauth.reddit.com"
11
+ }
12
+ ],
13
+ "paths": {
14
+ "/r/{subreddit}/{sort}": {
15
+ "get": {
16
+ "operationId": "list_posts",
17
+ "summary": "List posts in a subreddit",
18
+ "description": "Posts from one subreddit in a given order. Returns a Listing envelope: {kind, data:{after, children:[{kind, data}]}}.",
19
+ "tags": [
20
+ "read"
21
+ ],
22
+ "parameters": [
23
+ {
24
+ "name": "subreddit",
25
+ "in": "path",
26
+ "required": true,
27
+ "schema": {
28
+ "type": "string",
29
+ "description": "Subreddit name without the \"r/\" prefix."
30
+ }
31
+ },
32
+ {
33
+ "name": "sort",
34
+ "in": "path",
35
+ "required": true,
36
+ "schema": {
37
+ "type": "string",
38
+ "enum": [
39
+ "hot",
40
+ "new",
41
+ "top",
42
+ "rising"
43
+ ],
44
+ "description": "Ordering."
45
+ }
46
+ },
47
+ {
48
+ "name": "limit",
49
+ "in": "query",
50
+ "schema": {
51
+ "type": "integer",
52
+ "description": "How many to return, up to 100."
53
+ }
54
+ },
55
+ {
56
+ "name": "after",
57
+ "in": "query",
58
+ "schema": {
59
+ "type": "string",
60
+ "description": "Fullname to page after, from a previous \"after\"."
61
+ }
62
+ },
63
+ {
64
+ "name": "t",
65
+ "in": "query",
66
+ "schema": {
67
+ "type": "string",
68
+ "enum": [
69
+ "hour",
70
+ "day",
71
+ "week",
72
+ "month",
73
+ "year",
74
+ "all"
75
+ ],
76
+ "description": "Time window. Only meaningful when sort is \"top\"."
77
+ }
78
+ }
79
+ ],
80
+ "responses": {
81
+ "200": {
82
+ "description": "OK"
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "/comments/{article}": {
88
+ "get": {
89
+ "operationId": "get_post",
90
+ "summary": "Read a post and its comments",
91
+ "description": "One post with its comment tree. `article` is the base36 id WITHOUT the t3_ prefix. Returns a two-element array: the post, then the comments.",
92
+ "tags": [
93
+ "read"
94
+ ],
95
+ "parameters": [
96
+ {
97
+ "name": "article",
98
+ "in": "path",
99
+ "required": true,
100
+ "schema": {
101
+ "type": "string",
102
+ "description": "Post id in base36, no t3_ prefix."
103
+ }
104
+ },
105
+ {
106
+ "name": "sort",
107
+ "in": "query",
108
+ "schema": {
109
+ "type": "string",
110
+ "enum": [
111
+ "confidence",
112
+ "top",
113
+ "new",
114
+ "controversial",
115
+ "old",
116
+ "qa"
117
+ ],
118
+ "description": "Comment ordering."
119
+ }
120
+ },
121
+ {
122
+ "name": "limit",
123
+ "in": "query",
124
+ "schema": {
125
+ "type": "integer",
126
+ "description": "How many comments to return."
127
+ }
128
+ },
129
+ {
130
+ "name": "depth",
131
+ "in": "query",
132
+ "schema": {
133
+ "type": "integer",
134
+ "description": "How many levels of replies to descend."
135
+ }
136
+ }
137
+ ],
138
+ "responses": {
139
+ "200": {
140
+ "description": "OK"
141
+ }
142
+ }
143
+ }
144
+ },
145
+ "/r/{subreddit}/search": {
146
+ "get": {
147
+ "operationId": "search",
148
+ "summary": "Search within a subreddit",
149
+ "description": "Full-text search. Returns a Listing envelope: {kind, data:{after, children:[{kind, data}]}}.",
150
+ "tags": [
151
+ "read"
152
+ ],
153
+ "parameters": [
154
+ {
155
+ "name": "subreddit",
156
+ "in": "path",
157
+ "required": true,
158
+ "schema": {
159
+ "type": "string",
160
+ "description": "Subreddit name."
161
+ }
162
+ },
163
+ {
164
+ "name": "q",
165
+ "in": "query",
166
+ "required": true,
167
+ "schema": {
168
+ "type": "string",
169
+ "description": "Search query."
170
+ }
171
+ },
172
+ {
173
+ "name": "restrict_sr",
174
+ "in": "query",
175
+ "schema": {
176
+ "type": "boolean",
177
+ "description": "True to search only this subreddit rather than all of Reddit."
178
+ }
179
+ },
180
+ {
181
+ "name": "sort",
182
+ "in": "query",
183
+ "schema": {
184
+ "type": "string",
185
+ "enum": [
186
+ "relevance",
187
+ "hot",
188
+ "top",
189
+ "new",
190
+ "comments"
191
+ ],
192
+ "description": "Ordering."
193
+ }
194
+ },
195
+ {
196
+ "name": "t",
197
+ "in": "query",
198
+ "schema": {
199
+ "type": "string",
200
+ "enum": [
201
+ "hour",
202
+ "day",
203
+ "week",
204
+ "month",
205
+ "year",
206
+ "all"
207
+ ],
208
+ "description": "Time window."
209
+ }
210
+ },
211
+ {
212
+ "name": "limit",
213
+ "in": "query",
214
+ "schema": {
215
+ "type": "integer",
216
+ "description": "How many to return, up to 100."
217
+ }
218
+ }
219
+ ],
220
+ "responses": {
221
+ "200": {
222
+ "description": "OK"
223
+ }
224
+ }
225
+ }
226
+ },
227
+ "/r/{subreddit}/about": {
228
+ "get": {
229
+ "operationId": "get_subreddit",
230
+ "summary": "Read a subreddit profile",
231
+ "description": "Description, subscriber count, and whether posting requires a flair (`link_flair_enabled`, `submission_type`).",
232
+ "tags": [
233
+ "read"
234
+ ],
235
+ "parameters": [
236
+ {
237
+ "name": "subreddit",
238
+ "in": "path",
239
+ "required": true,
240
+ "schema": {
241
+ "type": "string",
242
+ "description": "Subreddit name."
243
+ }
244
+ }
245
+ ],
246
+ "responses": {
247
+ "200": {
248
+ "description": "OK"
249
+ }
250
+ }
251
+ }
252
+ },
253
+ "/r/{subreddit}/about/rules": {
254
+ "get": {
255
+ "operationId": "get_rules",
256
+ "summary": "Read a subreddit’s posting rules",
257
+ "description": "The rules a submission must satisfy. Worth reading before posting to an unfamiliar subreddit — most removals are rule violations, not API errors.",
258
+ "tags": [
259
+ "read"
260
+ ],
261
+ "parameters": [
262
+ {
263
+ "name": "subreddit",
264
+ "in": "path",
265
+ "required": true,
266
+ "schema": {
267
+ "type": "string",
268
+ "description": "Subreddit name."
269
+ }
270
+ }
271
+ ],
272
+ "responses": {
273
+ "200": {
274
+ "description": "OK"
275
+ }
276
+ }
277
+ }
278
+ },
279
+ "/api/v1/{subreddit}/link_flair_v2": {
280
+ "get": {
281
+ "operationId": "list_flairs",
282
+ "summary": "List a subreddit’s post flairs",
283
+ "description": "Available post flairs and their ids. Many subreddits reject a submission with no flair_id, so call this first when submitting somewhere new.",
284
+ "tags": [
285
+ "read"
286
+ ],
287
+ "parameters": [
288
+ {
289
+ "name": "subreddit",
290
+ "in": "path",
291
+ "required": true,
292
+ "schema": {
293
+ "type": "string",
294
+ "description": "Subreddit name."
295
+ }
296
+ }
297
+ ],
298
+ "responses": {
299
+ "200": {
300
+ "description": "OK"
301
+ }
302
+ }
303
+ }
304
+ },
305
+ "/api/v1/me": {
306
+ "get": {
307
+ "operationId": "whoami",
308
+ "summary": "Read the authenticated account",
309
+ "description": "Username, karma, and account age for the connected account.",
310
+ "tags": [
311
+ "read"
312
+ ],
313
+ "responses": {
314
+ "200": {
315
+ "description": "OK"
316
+ }
317
+ }
318
+ }
319
+ },
320
+ "/subreddits/mine/subscriber": {
321
+ "get": {
322
+ "operationId": "list_my_subreddits",
323
+ "summary": "List subscribed subreddits",
324
+ "description": "Subreddits the connected account subscribes to. Returns a Listing envelope: {kind, data:{after, children:[{kind, data}]}}.",
325
+ "tags": [
326
+ "read"
327
+ ],
328
+ "parameters": [
329
+ {
330
+ "name": "limit",
331
+ "in": "query",
332
+ "schema": {
333
+ "type": "integer",
334
+ "description": "How many to return."
335
+ }
336
+ },
337
+ {
338
+ "name": "after",
339
+ "in": "query",
340
+ "schema": {
341
+ "type": "string",
342
+ "description": "Fullname to page after."
343
+ }
344
+ }
345
+ ],
346
+ "responses": {
347
+ "200": {
348
+ "description": "OK"
349
+ }
350
+ }
351
+ }
352
+ },
353
+ "/api/submit": {
354
+ "post": {
355
+ "operationId": "submit_post",
356
+ "summary": "Submit a post to a subreddit",
357
+ "description": "Create a text or link post. kind=\"self\" needs `text`; kind=\"link\" needs `url`. Check list_flairs first — many subreddits reject a post with no flair_id.",
358
+ "tags": [
359
+ "write"
360
+ ],
361
+ "requestBody": {
362
+ "required": true,
363
+ "content": {
364
+ "application/x-www-form-urlencoded": {
365
+ "schema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "sr": {
369
+ "type": "string",
370
+ "description": "Subreddit name without the \"r/\" prefix."
371
+ },
372
+ "kind": {
373
+ "type": "string",
374
+ "enum": [
375
+ "self",
376
+ "link"
377
+ ],
378
+ "description": "\"self\" for a text post, \"link\" for a URL post."
379
+ },
380
+ "title": {
381
+ "type": "string",
382
+ "description": "Post title, up to 300 characters."
383
+ },
384
+ "text": {
385
+ "type": "string",
386
+ "description": "Body, as markdown. Only for kind=\"self\"."
387
+ },
388
+ "url": {
389
+ "type": "string",
390
+ "description": "Target URL. Only for kind=\"link\"."
391
+ },
392
+ "flair_id": {
393
+ "type": "string",
394
+ "description": "Flair template id from list_flairs."
395
+ },
396
+ "nsfw": {
397
+ "type": "boolean",
398
+ "description": "Mark as not safe for work."
399
+ },
400
+ "spoiler": {
401
+ "type": "boolean",
402
+ "description": "Mark as a spoiler."
403
+ },
404
+ "sendreplies": {
405
+ "type": "boolean",
406
+ "description": "Send reply notifications to the account inbox."
407
+ },
408
+ "api_type": {
409
+ "type": "string",
410
+ "enum": [
411
+ "json"
412
+ ],
413
+ "default": "json",
414
+ "description": "Always \"json\". Without it the response is not JSON and errors are unreadable."
415
+ }
416
+ },
417
+ "required": [
418
+ "sr",
419
+ "kind",
420
+ "title",
421
+ "api_type"
422
+ ]
423
+ }
424
+ }
425
+ }
426
+ },
427
+ "responses": {
428
+ "200": {
429
+ "description": "OK"
430
+ }
431
+ }
432
+ }
433
+ },
434
+ "/api/comment": {
435
+ "post": {
436
+ "operationId": "add_comment",
437
+ "summary": "Comment on a post or reply to a comment",
438
+ "description": "thing_id is a FULLNAME, not a bare id: t3_<id> for a post, t1_<id> for a comment. A bare id is silently rejected.",
439
+ "tags": [
440
+ "write"
441
+ ],
442
+ "requestBody": {
443
+ "required": true,
444
+ "content": {
445
+ "application/x-www-form-urlencoded": {
446
+ "schema": {
447
+ "type": "object",
448
+ "properties": {
449
+ "thing_id": {
450
+ "type": "string",
451
+ "description": "Fullname of the parent: t3_<id> for a post, t1_<id> for a comment."
452
+ },
453
+ "text": {
454
+ "type": "string",
455
+ "description": "Comment body, as markdown."
456
+ },
457
+ "api_type": {
458
+ "type": "string",
459
+ "enum": [
460
+ "json"
461
+ ],
462
+ "default": "json",
463
+ "description": "Always \"json\". Without it the response is not JSON and errors are unreadable."
464
+ }
465
+ },
466
+ "required": [
467
+ "thing_id",
468
+ "text",
469
+ "api_type"
470
+ ]
471
+ }
472
+ }
473
+ }
474
+ },
475
+ "responses": {
476
+ "200": {
477
+ "description": "OK"
478
+ }
479
+ }
480
+ }
481
+ },
482
+ "/api/editusertext": {
483
+ "post": {
484
+ "operationId": "edit_text",
485
+ "summary": "Edit your own post or comment",
486
+ "description": "Replaces the body of something this account authored. Only works on a text post or a comment.",
487
+ "tags": [
488
+ "write"
489
+ ],
490
+ "requestBody": {
491
+ "required": true,
492
+ "content": {
493
+ "application/x-www-form-urlencoded": {
494
+ "schema": {
495
+ "type": "object",
496
+ "properties": {
497
+ "thing_id": {
498
+ "type": "string",
499
+ "description": "Fullname of the item to edit: t3_<id> or t1_<id>."
500
+ },
501
+ "text": {
502
+ "type": "string",
503
+ "description": "Replacement body, as markdown."
504
+ },
505
+ "api_type": {
506
+ "type": "string",
507
+ "enum": [
508
+ "json"
509
+ ],
510
+ "default": "json",
511
+ "description": "Always \"json\". Without it the response is not JSON and errors are unreadable."
512
+ }
513
+ },
514
+ "required": [
515
+ "thing_id",
516
+ "text",
517
+ "api_type"
518
+ ]
519
+ }
520
+ }
521
+ }
522
+ },
523
+ "responses": {
524
+ "200": {
525
+ "description": "OK"
526
+ }
527
+ }
528
+ }
529
+ },
530
+ "/api/vote": {
531
+ "post": {
532
+ "operationId": "vote",
533
+ "summary": "Vote on a post or comment",
534
+ "description": "dir is 1 to upvote, -1 to downvote, 0 to clear an existing vote.",
535
+ "tags": [
536
+ "write"
537
+ ],
538
+ "requestBody": {
539
+ "required": true,
540
+ "content": {
541
+ "application/x-www-form-urlencoded": {
542
+ "schema": {
543
+ "type": "object",
544
+ "properties": {
545
+ "id": {
546
+ "type": "string",
547
+ "description": "Fullname of the target: t3_<id> or t1_<id>."
548
+ },
549
+ "dir": {
550
+ "type": "integer",
551
+ "enum": [
552
+ 1,
553
+ 0,
554
+ -1
555
+ ],
556
+ "description": "1 upvote, 0 clear, -1 downvote."
557
+ }
558
+ },
559
+ "required": [
560
+ "id",
561
+ "dir"
562
+ ]
563
+ }
564
+ }
565
+ }
566
+ },
567
+ "responses": {
568
+ "200": {
569
+ "description": "OK"
570
+ }
571
+ }
572
+ }
573
+ },
574
+ "/api/del": {
575
+ "post": {
576
+ "operationId": "delete_thing",
577
+ "summary": "Delete your own post or comment",
578
+ "description": "Permanent. Reddit has no trash for this — a deleted item cannot be restored through the API.",
579
+ "tags": [
580
+ "write"
581
+ ],
582
+ "requestBody": {
583
+ "required": true,
584
+ "content": {
585
+ "application/x-www-form-urlencoded": {
586
+ "schema": {
587
+ "type": "object",
588
+ "properties": {
589
+ "id": {
590
+ "type": "string",
591
+ "description": "Fullname to delete: t3_<id> or t1_<id>."
592
+ }
593
+ },
594
+ "required": [
595
+ "id"
596
+ ]
597
+ }
598
+ }
599
+ }
600
+ },
601
+ "responses": {
602
+ "200": {
603
+ "description": "OK"
604
+ }
605
+ }
606
+ }
607
+ },
608
+ "/api/save": {
609
+ "post": {
610
+ "operationId": "save_thing",
611
+ "summary": "Save a post or comment",
612
+ "description": "Adds to the account’s saved items.",
613
+ "tags": [
614
+ "write"
615
+ ],
616
+ "requestBody": {
617
+ "required": true,
618
+ "content": {
619
+ "application/x-www-form-urlencoded": {
620
+ "schema": {
621
+ "type": "object",
622
+ "properties": {
623
+ "id": {
624
+ "type": "string",
625
+ "description": "Fullname to save: t3_<id> or t1_<id>."
626
+ },
627
+ "category": {
628
+ "type": "string",
629
+ "description": "Optional saved-items category name."
630
+ }
631
+ },
632
+ "required": [
633
+ "id"
634
+ ]
635
+ }
636
+ }
637
+ }
638
+ },
639
+ "responses": {
640
+ "200": {
641
+ "description": "OK"
642
+ }
643
+ }
644
+ }
645
+ },
646
+ "/api/selectflair": {
647
+ "post": {
648
+ "operationId": "set_flair",
649
+ "summary": "Set the flair on your own post",
650
+ "description": "Applies a flair template from list_flairs to a post this account authored.",
651
+ "tags": [
652
+ "write"
653
+ ],
654
+ "requestBody": {
655
+ "required": true,
656
+ "content": {
657
+ "application/x-www-form-urlencoded": {
658
+ "schema": {
659
+ "type": "object",
660
+ "properties": {
661
+ "sr": {
662
+ "type": "string",
663
+ "description": "Subreddit name without the \"r/\" prefix."
664
+ },
665
+ "link": {
666
+ "type": "string",
667
+ "description": "Fullname of the post: t3_<id>."
668
+ },
669
+ "flair_template_id": {
670
+ "type": "string",
671
+ "description": "Flair template id from list_flairs."
672
+ },
673
+ "api_type": {
674
+ "type": "string",
675
+ "enum": [
676
+ "json"
677
+ ],
678
+ "default": "json",
679
+ "description": "Always \"json\". Without it the response is not JSON and errors are unreadable."
680
+ }
681
+ },
682
+ "required": [
683
+ "sr",
684
+ "link",
685
+ "flair_template_id",
686
+ "api_type"
687
+ ]
688
+ }
689
+ }
690
+ }
691
+ },
692
+ "responses": {
693
+ "200": {
694
+ "description": "OK"
695
+ }
696
+ }
697
+ }
698
+ }
699
+ }
700
+ }