@paigy/mcp 0.40.10 → 0.40.11

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.
@@ -2,10 +2,10 @@ import {
2
2
  AGENT_TOOLS,
3
3
  mcpInputSchema,
4
4
  serverInstructions
5
- } from "./chunk-UFOR2QEP.js";
5
+ } from "./chunk-I5QINUN6.js";
6
6
  import {
7
7
  agentName
8
- } from "./chunk-HCCUK3ZL.js";
8
+ } from "./chunk-H5GGWCPS.js";
9
9
 
10
10
  // src/toolset.ts
11
11
  var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
@@ -1301,6 +1301,7 @@ var zodToJsonSchema = (schema, options) => {
1301
1301
  };
1302
1302
  var OPTIONS_MIN = 2;
1303
1303
  var OPTIONS_MAX = 6;
1304
+ var NIGHT = { from: 23, to: 7 };
1304
1305
  function draft2020(node) {
1305
1306
  if (Array.isArray(node)) return node.map(draft2020);
1306
1307
  if (node && typeof node === "object") {
@@ -1332,7 +1333,7 @@ function mcpInputSchema(s) {
1332
1333
  }
1333
1334
  var AskInputSchema = z.object({
1334
1335
  id: z.string().optional().describe("Optional idempotency key or client-side ID for this specific ask."),
1335
- parentId: z.string().uuid().optional().describe("Optional explicit parent Goal ID. If omitted, the platform's semantic router will auto-cluster it."),
1336
+ parentId: z.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and Paigy places the question in the tree itself."),
1336
1337
  repo: z.string().optional().describe("Optional repository context."),
1337
1338
  ask: z.string().trim().min(1).max(1e4).describe(
1338
1339
  "The question, and only what is needed to answer it. News, progress and findings are their own contact \u2014 a call contact JOINS a call already happening, so several arrive as one call. Do not bundle: a DecisionNeed is settled only by an answer in the shape this ask declares, so someone who answers the part that interested them settles nothing and is asked again."
@@ -1351,7 +1352,7 @@ var StartContactSchema = z.object({
1351
1352
  }).strict();
1352
1353
  var ContactSchema = z.union([StartContactSchema, z.object({ deliveryId: z.string().uuid() }).strict()]);
1353
1354
  var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
1354
- var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. The platform will semantically cluster your asks under the correct Goals automatically. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Never bundle multiple questions into a single 'ask' string; pass them as separate objects in the 'asks' array.";
1355
+ var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Never bundle multiple questions into a single 'ask' string; pass them as separate objects in the 'asks' array.";
1355
1356
  var CreateGoalSchema = z2.object({
1356
1357
  outcome: z2.string().trim().min(1).max(1e4),
1357
1358
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
@@ -1407,9 +1408,9 @@ var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal y
1407
1408
  var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
1408
1409
  var CheckRepliesSchema = z2.object({}).strict();
1409
1410
  var GetThreadSchema = z2.object({
1410
- parentId: z2.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
1411
+ parentId: z2.string().describe("The Thread to read \u2014 the parentId of a search_threads hit.")
1411
1412
  }).strict();
1412
- var GET_THREAD_DESCRIPTION = "Read the authorized durable Entries on one conversation Thread \u2014 what you wrote there and what was delivered to you, oldest first. Use claim_goal to find the work to resume; use this to rehydrate a Thread that a search hit or a Delivery named.";
1413
+ var GET_THREAD_DESCRIPTION = "Read the authorized durable Entries on one conversation Thread \u2014 what you wrote there and what was delivered to you, oldest first. Use claim_goal to find the work to resume and get_goal for the conversation on a Goal; use this only to rehydrate a Thread that a search hit named.";
1413
1414
  var SearchThreadsSchema = z2.object({
1414
1415
  q: z2.string().describe("What to look for \u2014 plain words or a phrase (e.g. 'the livekit timeout', 'deploy to prod').")
1415
1416
  }).strict();
@@ -1887,7 +1888,7 @@ var InboxItemSchema = z3.object({
1887
1888
  error: z3.string().optional(),
1888
1889
  clarifies: z3.string().optional(),
1889
1890
  /** The ring ladder ran out while this was still pending — we tried to reach you and
1890
- * STOPPED trying (`arbitration/arbitrate.ts` `nextRing` → `stop`). Distinct from an
1891
+ * STOPPED trying (`arbitration/arbitrate.ts` `ringAt` → null). Distinct from an
1891
1892
  * agent with nothing to say, which the roster drew identically until now: "nothing to
1892
1893
  * say" and "gave up saying it" are opposite situations wearing the same face
1893
1894
  * (navigation-design.md, gap 1). False for anything that never rang. */
@@ -1956,6 +1957,8 @@ var MissedCallSchema = z3.enum([
1956
1957
  "inbox",
1957
1958
  "dismiss"
1958
1959
  ]);
1960
+ var clock = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
1961
+ var QUIET = ` Nothing rings from ${clock(NIGHT.from)} to ${clock(NIGHT.to)} your time; the count waits for morning.`;
1959
1962
  var BrokerTuningSchema = z3.object({
1960
1963
  /** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
1961
1964
  ackVerbosity: z3.enum(["normal", "none"]).optional(),
@@ -2646,10 +2649,12 @@ async function contact(input, opts = {}) {
2646
2649
  deliveryId = deliveries[0]?.deliveryId ?? "";
2647
2650
  if (!deliveryId) throw new Error("Goal contact returned no valid Delivery identity");
2648
2651
  }
2652
+ const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId);
2649
2653
  const read = async (signal2) => {
2650
2654
  const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
2651
2655
  if (!res.ok) await fail("read_delivery", res);
2652
- return await res.json();
2656
+ const one = await res.json();
2657
+ return all.length > 1 ? { ...one, deliveries: all } : one;
2653
2658
  };
2654
2659
  const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
2655
2660
  if (opts.waits === false) return read(opts.signal);
@@ -2745,6 +2750,8 @@ function deliveryView(d) {
2745
2750
  state: d.state,
2746
2751
  goalIds: d.goalIds,
2747
2752
  conversation: conversation(d),
2753
+ // Each ask's own Delivery when a contact carried several — the only handle on the others.
2754
+ deliveries: d.deliveries,
2748
2755
  next: d.kind === "notification" ? "Answers land on the Goal: read them with claim_goal or get_goal. Do not poll this notification." : d.message
2749
2756
  });
2750
2757
  }
@@ -5,6 +5,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
5
5
  import { z } from "zod";
6
6
  var OPTIONS_MIN = 2;
7
7
  var OPTIONS_MAX = 6;
8
+ var NIGHT = { from: 23, to: 7 };
8
9
  function draft2020(node) {
9
10
  if (Array.isArray(node)) return node.map(draft2020);
10
11
  if (node && typeof node === "object") {
@@ -36,7 +37,7 @@ function mcpInputSchema(s) {
36
37
  }
37
38
  var AskInputSchema = z.object({
38
39
  id: z.string().optional().describe("Optional idempotency key or client-side ID for this specific ask."),
39
- parentId: z.string().uuid().optional().describe("Optional explicit parent Goal ID. If omitted, the platform's semantic router will auto-cluster it."),
40
+ parentId: z.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and Paigy places the question in the tree itself."),
40
41
  repo: z.string().optional().describe("Optional repository context."),
41
42
  ask: z.string().trim().min(1).max(1e4).describe(
42
43
  "The question, and only what is needed to answer it. News, progress and findings are their own contact \u2014 a call contact JOINS a call already happening, so several arrive as one call. Do not bundle: a DecisionNeed is settled only by an answer in the shape this ask declares, so someone who answers the part that interested them settles nothing and is asked again."
@@ -55,7 +56,7 @@ var StartContactSchema = z.object({
55
56
  }).strict();
56
57
  var ContactSchema = z.union([StartContactSchema, z.object({ deliveryId: z.string().uuid() }).strict()]);
57
58
  var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
58
- var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. The platform will semantically cluster your asks under the correct Goals automatically. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Never bundle multiple questions into a single 'ask' string; pass them as separate objects in the 'asks' array.";
59
+ var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Never bundle multiple questions into a single 'ask' string; pass them as separate objects in the 'asks' array.";
59
60
  var CreateGoalSchema = z2.object({
60
61
  outcome: z2.string().trim().min(1).max(1e4),
61
62
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
@@ -111,9 +112,9 @@ var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal y
111
112
  var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
112
113
  var CheckRepliesSchema = z2.object({}).strict();
113
114
  var GetThreadSchema = z2.object({
114
- parentId: z2.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
115
+ parentId: z2.string().describe("The Thread to read \u2014 the parentId of a search_threads hit.")
115
116
  }).strict();
116
- var GET_THREAD_DESCRIPTION = "Read the authorized durable Entries on one conversation Thread \u2014 what you wrote there and what was delivered to you, oldest first. Use claim_goal to find the work to resume; use this to rehydrate a Thread that a search hit or a Delivery named.";
117
+ var GET_THREAD_DESCRIPTION = "Read the authorized durable Entries on one conversation Thread \u2014 what you wrote there and what was delivered to you, oldest first. Use claim_goal to find the work to resume and get_goal for the conversation on a Goal; use this only to rehydrate a Thread that a search hit named.";
117
118
  var SearchThreadsSchema = z2.object({
118
119
  q: z2.string().describe("What to look for \u2014 plain words or a phrase (e.g. 'the livekit timeout', 'deploy to prod').")
119
120
  }).strict();
@@ -129,14 +130,15 @@ var AGENT_TOOLS = [
129
130
  { name: "update_goal", description: UPDATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(UpdateGoalToolSchema) }
130
131
  ];
131
132
  var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
132
- function serverInstructions(_opts) {
133
- return `On startup and after a wake, call check_replies for the open Deliveries addressed to you, and claim_goal for your runnable or review-pending Goal and the conversation on it. get_goal rereads it without claiming. Every read ends in \`next\`, the one step to take. Contact only for an existing Goal. Notifications return immediately: keep working and collect answers through claim_goal/get_goal. On stdio, Calls hold one bounded window; on hosted MCP, Calls return after one read; continue with contact({deliveryId}) to reread that exact Delivery. Evidence can repeat: reads do not acknowledge or hide it. Use update_goal to report progress and explicitly acknowledge review. Never infer ringing from an open Call Delivery. Soft waiting, multipart contact, and re-presentation are unsupported. Your user is remote. Always interact with the user through Paigy. For decisions, approvals, or questions, contact them with structured options. Never assume anyone is reading the terminal stdout.
133
+ function serverInstructions(opts) {
134
+ const calls = opts.waits ? "A Call holds one bounded window here; continue with contact({deliveryId}) to hold the next or reread that exact Delivery." : "A Call returns after one read; continue with contact({deliveryId}) to reread that exact Delivery.";
135
+ return `On startup and after a wake, call check_replies for the open Deliveries addressed to you, and claim_goal for your runnable or review-pending Goal and the conversation on it. get_goal rereads it without claiming. Every read ends in \`next\`, the one step to take. Notifications return immediately: keep working and collect answers through claim_goal/get_goal. ${calls} Evidence can repeat: reads do not acknowledge or hide it. Report progress and acknowledge review with update_goal, never as a contact. Never infer ringing from an open Call Delivery. Soft waiting and re-presentation are unsupported. Your user is remote. Always interact with the user through Paigy. For decisions, approvals, or questions, contact them with structured options. Never assume anyone is reading the terminal stdout.
134
136
 
135
- CRITICAL PAIGY WORKFLOW PHILOSOPHY:
136
- 1. Atomicity: Never dump a 5-part question into a single \`ask\`. Emit 5 separate objects in the \`asks\` array during a single \`contact\` call so the user can answer them independently.
137
- 2. Tree Structure: Always pass \`parentId\` to deliberately nest sub-decisions under the overarching Goal, ensuring the user workspace remains a clean tree rather than a flat mess.
138
- 3. Urgency: Use \`waiting: hard\` strictly for immediate, blocking decisions, and \`waiting: none\` for async, long-running progress updates or non-blocking questions.
139
- 4. Asynchrony: Never hold the process open with while-loops. Let the current process finish or explicitly schedule wakeups, and rely on \`check_replies\` or \`claim_goal\` on the next wake to process asynchronous answers.`;
137
+ HOW TO ASK:
138
+ 1. One question per ask. Five questions are five objects in \`asks\`, in one contact, so each can be answered on its own; one question with five parts settles nothing until all five are answered.
139
+ 2. Name the Goal you are working on as the ask's \`parentId\`: the question goes onto that Goal and its answer comes back there. Without one, Paigy places it.
140
+ 3. \`waiting: hard\` only for a decision you are blocked on; \`waiting: none\` for a question you can keep working around.
141
+ 4. Never hold the process open with while-loops. Let the current process finish or schedule a wakeup, and collect answers with \`check_replies\` or \`claim_goal\` on the next wake.`;
140
142
  }
141
143
  function entryWords(entry) {
142
144
  const content = entry.content;
@@ -600,7 +602,7 @@ var InboxItemSchema = z3.object({
600
602
  error: z3.string().optional(),
601
603
  clarifies: z3.string().optional(),
602
604
  /** The ring ladder ran out while this was still pending — we tried to reach you and
603
- * STOPPED trying (`arbitration/arbitrate.ts` `nextRing` → `stop`). Distinct from an
605
+ * STOPPED trying (`arbitration/arbitrate.ts` `ringAt` → null). Distinct from an
604
606
  * agent with nothing to say, which the roster drew identically until now: "nothing to
605
607
  * say" and "gave up saying it" are opposite situations wearing the same face
606
608
  * (navigation-design.md, gap 1). False for anything that never rang. */
@@ -669,6 +671,8 @@ var MissedCallSchema = z3.enum([
669
671
  "inbox",
670
672
  "dismiss"
671
673
  ]);
674
+ var clock = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
675
+ var QUIET = ` Nothing rings from ${clock(NIGHT.from)} to ${clock(NIGHT.to)} your time; the count waits for morning.`;
672
676
  var BrokerTuningSchema = z3.object({
673
677
  /** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
674
678
  ackVerbosity: z3.enum(["normal", "none"]).optional(),
@@ -4,7 +4,7 @@ import {
4
4
  requestCode,
5
5
  saveToken,
6
6
  sleep
7
- } from "./chunk-HCCUK3ZL.js";
7
+ } from "./chunk-H5GGWCPS.js";
8
8
 
9
9
  // src/identity.ts
10
10
  var CLIENT_LABELS = {
package/dist/enable.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools
5
- } from "./chunk-QAQET3PZ.js";
6
- import "./chunk-UFOR2QEP.js";
7
- import "./chunk-HCCUK3ZL.js";
5
+ } from "./chunk-FQVK7SO4.js";
6
+ import "./chunk-I5QINUN6.js";
7
+ import "./chunk-H5GGWCPS.js";
8
8
 
9
9
  // src/enable.ts
10
10
  function main() {
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  resolvePairing,
6
6
  startPairing,
7
7
  suggestedAgentName
8
- } from "./chunk-AK5AVPF6.js";
8
+ } from "./chunk-XIHVHYDU.js";
9
9
  import {
10
10
  ENABLE_COMMAND,
11
11
  OnboardSchema,
@@ -14,8 +14,8 @@ import {
14
14
  SERVER_INSTRUCTIONS,
15
15
  TOOLS,
16
16
  paigyToolsAllowlisted
17
- } from "./chunk-QAQET3PZ.js";
18
- import "./chunk-UFOR2QEP.js";
17
+ } from "./chunk-FQVK7SO4.js";
18
+ import "./chunk-I5QINUN6.js";
19
19
  import {
20
20
  clearSurface,
21
21
  writeSurface
@@ -35,7 +35,7 @@ import {
35
35
  setIdentity,
36
36
  slotName,
37
37
  whoAmI
38
- } from "./chunk-HCCUK3ZL.js";
38
+ } from "./chunk-H5GGWCPS.js";
39
39
 
40
40
  // src/index.ts
41
41
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/dist/listen.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  entryWords
4
- } from "./chunk-UFOR2QEP.js";
4
+ } from "./chunk-I5QINUN6.js";
5
5
  import {
6
6
  checkReplies,
7
7
  contact,
8
8
  getGoal,
9
9
  subscribeWake
10
- } from "./chunk-HCCUK3ZL.js";
10
+ } from "./chunk-H5GGWCPS.js";
11
11
 
12
12
  // src/listen.ts
13
13
  import { spawn } from "child_process";
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-AK5AVPF6.js";
6
+ } from "./chunk-XIHVHYDU.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-QAQET3PZ.js";
12
- import "./chunk-UFOR2QEP.js";
11
+ } from "./chunk-FQVK7SO4.js";
12
+ import "./chunk-I5QINUN6.js";
13
13
  import {
14
14
  TOKEN_PATH,
15
15
  agentName,
@@ -20,7 +20,7 @@ import {
20
20
  saveToken,
21
21
  setIdentity,
22
22
  whoAmI
23
- } from "./chunk-HCCUK3ZL.js";
23
+ } from "./chunk-H5GGWCPS.js";
24
24
 
25
25
  // src/onboard.ts
26
26
  function reportRegistered() {
package/dist/slot.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  agentName
4
- } from "./chunk-HCCUK3ZL.js";
4
+ } from "./chunk-H5GGWCPS.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(agentName());
@@ -7,7 +7,7 @@ import {
7
7
  readToken,
8
8
  sessionSlot,
9
9
  slotName
10
- } from "./chunk-HCCUK3ZL.js";
10
+ } from "./chunk-H5GGWCPS.js";
11
11
 
12
12
  // src/statusline.ts
13
13
  import { realpathSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.10",
3
+ "version": "0.40.11",
4
4
  "description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",