primcli 1.72.2 → 1.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +41 -10
  2. package/bin/claude-pending-mail.mjs +292 -136
  3. package/bin/claude-wake.mjs +129 -60
  4. package/dist/api-command-fKP3NYa7.js +2904 -0
  5. package/dist/{cli-config--YTRdJ3t.js → cli-config-C0Y_Ppol.js} +3 -598
  6. package/dist/client.gen-CaaMP86o.js +597 -0
  7. package/dist/{listen-background--0peztKE.js → listen-background-cD6n8VYO.js} +1 -1
  8. package/dist/{machine-session-Be6j29GE.js → machine-session-B6V5-grn.js} +907 -207
  9. package/dist/{notify-session-Byt5sPWW.js → notify-session-DJAb9SGm.js} +3 -3
  10. package/dist/oclif/auto-signal-worker.js +4 -3
  11. package/dist/oclif/auto-signals.js +2 -2
  12. package/dist/oclif/index.js +272 -200
  13. package/dist/oclif/listen-supervisor.js +1 -1
  14. package/dist/oclif/receiver-heal.js +4 -4
  15. package/dist/oclif/root-signup-hint.js +1 -1
  16. package/dist/{repeats-BL7ZC-_6-CKMKBlx7.js → repeats-BL7ZC-_6-B_ESsWTH.js} +4005 -1
  17. package/dist/{scoped-chat-DTFkP360.js → scoped-chat-DAiOb622.js} +10 -2
  18. package/dist/{shared-mail-files-MwlVE90c.js → shared-mail-files-CZfm7E2k.js} +21 -1
  19. package/dist/{shared-mail-watch-DtKpsgg8.js → shared-mail-watch-DyQB0MK0.js} +1 -1
  20. package/dist/{signal-command-DeEEZPbQ.js → signal-command-Cte4mAL3.js} +8 -7
  21. package/dist/skills/primitive-connect/references/conversations.md +3 -2
  22. package/dist/skills/primitive-connect/references/native-session.md +4 -2
  23. package/dist/skills/primitive-connect/scripts/check-cli-capabilities.mjs +14 -11
  24. package/dist/skills/primitive-connect.json +5 -5
  25. package/package.json +1 -1
  26. package/dist/api-command-BrwPbyPP.js +0 -6546
@@ -1,4 +1,4 @@
1
- import { c as classifySignalContent, y as isTrustedSender } from "./repeats-BL7ZC-_6-CKMKBlx7.js";
1
+ import { b as retryTransient, c as classifySignalContent, q as isTrustedSender } from "./repeats-BL7ZC-_6-B_ESsWTH.js";
2
2
  //#region src/oclif/presence-provenance.ts
3
3
  function readPresenceProjection(detail) {
4
4
  if (!detail || typeof detail !== "object") return null;
@@ -50,6 +50,14 @@ async function readBeforeDeadline(deadline, read) {
50
50
  clearTimeout(timer);
51
51
  }
52
52
  }
53
+ /**
54
+ * readBeforeDeadline for a generated-client read inside a wait: a rate
55
+ * limit, gateway failure or network error is retried with backoff until the
56
+ * deadline instead of being returned. Null when the deadline passes.
57
+ */
58
+ function readApiBeforeDeadline(deadline, read) {
59
+ return retryTransient(() => readBeforeDeadline(deadline, read), { deadline });
60
+ }
53
61
  function address(value) {
54
62
  return value.trim().toLowerCase();
55
63
  }
@@ -89,4 +97,4 @@ function isPlainChatReply(detail) {
89
97
  }).classification === "plain";
90
98
  }
91
99
  //#endregion
92
- export { scopedChatSenderTrust as a, readBeforeDeadline as i, isPlainChatReply as n, presenceDisposition as o, isScopedChatReply as r, readPresenceProjection as s, isConnectedChatCredential as t };
100
+ export { readBeforeDeadline as a, readPresenceProjection as c, readApiBeforeDeadline as i, isPlainChatReply as n, scopedChatSenderTrust as o, isScopedChatReply as r, presenceDisposition as s, isConnectedChatCredential as t };
@@ -125,6 +125,26 @@ function removeOwner(path, owner) {
125
125
  ].includes(error.code ?? "")) throw error;
126
126
  }
127
127
  }
128
+ /**
129
+ * True while a live process holds the lock {@link acquireListenLock} takes
130
+ * for this directory and scope. Read-only; a lock whose owner exited, or
131
+ * whose owner record cannot be read, counts as free.
132
+ */
133
+ function listenLockHeld(directory, scope) {
134
+ const key = createHash("sha256").update(scope).digest("hex");
135
+ const path = join(directory, `${key}.lock`);
136
+ try {
137
+ const entries = readdirSync(path);
138
+ const match = entries.length === 1 ? /^(\d+)-[a-f0-9-]{36}$/.exec(entries[0] ?? "") : null;
139
+ if (!match) return false;
140
+ const pid = Number(match[1]);
141
+ if (!processAlive(pid)) return false;
142
+ const saved = ownerIdentity(join(path, entries[0] ?? ""));
143
+ return compareListenProcessIdentity(saved, saved === null ? null : listenProcessIdentity(pid)) !== false;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
128
148
  function acquireListenLock(directory, scope) {
129
149
  privateDirectory(directory);
130
150
  const key = createHash("sha256").update(scope).digest("hex");
@@ -352,4 +372,4 @@ function mailTime(value) {
352
372
  return new Date(time).toISOString();
353
373
  }
354
374
  //#endregion
355
- export { resolveListenSubscription as S, acquireListenLock as _, mailLockBusy as a, listenProcessIdentity as b, mailTime as c, readMailJson as d, removeMailFile as f, ListenStateError as g, writeMailJson as h, mailId as i, privateMailDirectory as l, withMailLock as m, invalidSharedMail as n, mailObject as o, syncMailDirectory as p, mailAddress as r, mailString as s, SharedMailStateError as t, privateMailPermissions as u, compareListenProcessIdentity as v, normalizeListenOrigin as x, listenIdentity as y };
375
+ export { resolveListenSubscription as C, normalizeListenOrigin as S, acquireListenLock as _, mailLockBusy as a, listenLockHeld as b, mailTime as c, readMailJson as d, removeMailFile as f, ListenStateError as g, writeMailJson as h, mailId as i, privateMailDirectory as l, withMailLock as m, invalidSharedMail as n, mailObject as o, syncMailDirectory as p, mailAddress as r, mailString as s, SharedMailStateError as t, privateMailPermissions as u, compareListenProcessIdentity as v, listenProcessIdentity as x, listenIdentity as y };
@@ -1,4 +1,4 @@
1
- import { _ as acquireListenLock, a as mailLockBusy, b as listenProcessIdentity, c as mailTime, d as readMailJson, f as removeMailFile, h as writeMailJson, i as mailId, n as invalidSharedMail, o as mailObject, s as mailString, v as compareListenProcessIdentity } from "./shared-mail-files-MwlVE90c.js";
1
+ import { _ as acquireListenLock, a as mailLockBusy, c as mailTime, d as readMailJson, f as removeMailFile, h as writeMailJson, i as mailId, n as invalidSharedMail, o as mailObject, s as mailString, v as compareListenProcessIdentity, x as listenProcessIdentity } from "./shared-mail-files-CZfm7E2k.js";
2
2
  import { watch } from "node:fs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { join } from "node:path";
@@ -1,8 +1,8 @@
1
- import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-BL7ZC-_6-CKMKBlx7.js";
2
- import { D as bareAddress, St as successfulSendOutcome, er as listSentEmails, kn as getEmail, o as extractErrorPayload, ot as classifySendError, sn as createEndpoint, vr as sendEmail } from "./api-command-BrwPbyPP.js";
3
- import { _ as acquireListenLock, b as listenProcessIdentity, c as mailTime, d as readMailJson, f as removeMailFile, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, n as invalidSharedMail, o as mailObject, p as syncMailDirectory, r as mailAddress, s as mailString, t as SharedMailStateError, v as compareListenProcessIdentity, y as listenIdentity } from "./shared-mail-files-MwlVE90c.js";
4
- import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply } from "./scoped-chat-DTFkP360.js";
5
- import { n as tryOwnSharedMail, r as waitForSharedMailChange, t as readSharedMailOwner } from "./shared-mail-watch-DtKpsgg8.js";
1
+ import { Dn as sendEmail, Ft as getEmail, _ as describeApiFailure, cn as listSentEmails, h as sendPreparedSignal, p as prepareSignalEmail, pt as createEndpoint } from "./repeats-BL7ZC-_6-B_ESsWTH.js";
2
+ import { _ as acquireListenLock, c as mailTime, d as readMailJson, f as removeMailFile, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, n as invalidSharedMail, o as mailObject, p as syncMailDirectory, r as mailAddress, s as mailString, t as SharedMailStateError, v as compareListenProcessIdentity, x as listenProcessIdentity, y as listenIdentity } from "./shared-mail-files-CZfm7E2k.js";
3
+ import { Y as classifySendError, dt as successfulSendOutcome, h as bareAddress, o as extractErrorPayload } from "./api-command-fKP3NYa7.js";
4
+ import { i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust } from "./scoped-chat-DAiOb622.js";
5
+ import { n as tryOwnSharedMail, r as waitForSharedMailChange, t as readSharedMailOwner } from "./shared-mail-watch-DyQB0MK0.js";
6
6
  import { recordSentSignal } from "./oclif/auto-signals.js";
7
7
  import { Errors } from "@oclif/core";
8
8
  import { opendirSync } from "node:fs";
@@ -2087,7 +2087,7 @@ async function openSharedMailReceiver(options) {
2087
2087
  //#region src/oclif/reconcile-chat-send.ts
2088
2088
  /** Lookup only. An empty result never authorizes another send. */
2089
2089
  async function reconcileChatSend(params) {
2090
- const result = await readBeforeDeadline(params.deadline, (signal) => listSentEmails({
2090
+ const result = await readApiBeforeDeadline(params.deadline, (signal) => listSentEmails({
2091
2091
  client: params.apiClient.client,
2092
2092
  query: {
2093
2093
  idempotency_key: params.idempotencyKey,
@@ -2097,8 +2097,9 @@ async function reconcileChatSend(params) {
2097
2097
  responseStyle: "fields"
2098
2098
  }));
2099
2099
  if (result === null) return null;
2100
+ if (result.error) throw new Errors.CLIError(`Could not reconcile the saved send intent: ${describeApiFailure(result)}. Its outcome remains unknown; do not send again.`, { exit: 1 });
2100
2101
  const page = result.data;
2101
- if (result.error || !page || !Array.isArray(page.data) || !page.meta || page.meta.cursor !== null || page.data.length > 1) throw new Errors.CLIError("Could not reconcile the saved send intent. Its outcome remains unknown; do not send again.", { exit: 1 });
2102
+ if (!page || !Array.isArray(page.data) || !page.meta || page.meta.cursor !== null || page.data.length > 1) throw new Errors.CLIError("Could not reconcile the saved send intent. Its outcome remains unknown; do not send again.", { exit: 1 });
2102
2103
  const sent = page.data[0];
2103
2104
  if (!sent) return null;
2104
2105
  if (sent.client_idempotency_key !== params.idempotencyKey || typeof sent.from_address !== "string" || sent.from_address.trim().toLowerCase() !== params.from.trim().toLowerCase() || typeof sent.to_address !== "string" || sent.to_address.trim().toLowerCase() !== params.recipient.trim().toLowerCase() || typeof sent.id !== "string" || !sent.id) throw new Errors.CLIError("The saved send lookup returned conflicting identity metadata. Inspect the existing send before retrying.", { exit: 1 });
@@ -214,8 +214,9 @@ and sends nothing; a read that started Working ends with that exact command.
214
214
  A reply stops Working for the email it answers and for the sender's other
215
215
  mail in that thread, not for the sender's mail in other threads. Do not send Read or Working
216
216
  yourself then; still send Typing just before composing. Only an older CLI,
217
- whose `primitive signal --help` does not mention automatic signals, needs the
218
- manual Working signal above. Either way the CLI
217
+ whose full signal help does not mention automatic signals, needs the manual
218
+ Working signal above. Read the full help with `primitive signal --help-full`,
219
+ or `primitive signal --help` when the CLI rejects `--help-full`. Either way the CLI
219
220
  rejects signal/interaction parents to avoid loops. Do not hand-renew Working in
220
221
  a loop through long work; the work claim under
221
222
  [Collaborate with other agents](collaboration.md) covers that. Send Typing only while composing and stop on reply, failure or waiting.
@@ -8,8 +8,10 @@ listener while that hook is installed or after it has delivered real mail. Do
8
8
  not use this native status to diagnose Claude's hook, start a competing listener,
9
9
  or repeat a claim. See the [Claude guidance](presence-and-receiving.md#claude-code-external-receiver).
10
10
 
11
- Use this reference only when installed `primitive listen --help` describes
11
+ Use this reference only when the installed CLI's full listen help describes
12
12
  "external mail events at tool-output authority" and no synthetic user messages.
13
+ Read it with `primitive listen --help-full`, or `primitive listen --help` when
14
+ the CLI rejects `--help-full`; short `--help` leaves out these descriptions.
13
15
  The same `--notify-session` command supplies these events; no extra mode flag or
14
16
  plugin is required. Those events carry mail-arrival metadata with tool-output
15
17
  trust, like a background task completion.
@@ -86,7 +88,7 @@ does not prove Primitive attached; check the listener health below.
86
88
  Once that exact session is loaded, use the profile already verified as belonging
87
89
  to this connection and saved in this session's context. Replace the example name;
88
90
  do not adopt a generic existing profile based only on configured status. Use the
89
- already inspected `primitive listen --help` to choose the installed capability.
91
+ already inspected full listen help to choose the installed capability.
90
92
  Only when it documents external-event delivery and `--background`, let the CLI
91
93
  supervise the receiver:
92
94
 
@@ -9,16 +9,16 @@ if (receiver !== 'native' && receiver !== 'external' && receiver !== 'poll') {
9
9
  process.exitCode = 2;
10
10
  } else {
11
11
  const checks = [
12
- { args: ['agent', 'connect', '--help'], label: 'agent connect', patterns: [/--session\b/, /--receiver\b/] },
13
- { args: ['agent', 'enroll', '--help'], label: 'agent enroll', patterns: [/--session\b/, /--receiver\b/, /--name\b/] },
14
- { args: ['listen', '--help'], label: 'listen', patterns: [/external mail events at tool-output authority/i, /never synthetic user messages/i] },
15
- { args: ['network', 'peers', '--help'], label: 'network peers', patterns: [/discover listed peers/i, /--owner\b/] },
12
+ { command: ['agent', 'connect'], label: 'agent connect', patterns: [/--session\b/, /--receiver\b/] },
13
+ { command: ['agent', 'enroll'], label: 'agent enroll', patterns: [/--session\b/, /--receiver\b/, /--name\b/] },
14
+ { command: ['listen'], label: 'listen', patterns: [/external mail events at tool-output authority/i, /never synthetic user messages/i] },
15
+ { command: ['network', 'peers'], label: 'network peers', patterns: [/discover listed peers/i, /--owner\b/] },
16
16
  ];
17
17
  if (receiver === 'poll') {
18
18
  // Nothing is installed and no listener runs: the agent checks for mail.
19
19
  checks[0].patterns.push(/poll installs nothing/i);
20
20
  checks[1].patterns.push(/\bpoll\b/);
21
- checks[2] = { args: ['agent', 'check-mail', '--help'], label: 'agent check-mail', patterns: [/since its previous check/i] };
21
+ checks[2] = { command: ['agent', 'check-mail'], label: 'agent check-mail', patterns: [/since its previous check/i] };
22
22
  } else if (receiver === 'external') {
23
23
  checks[0].patterns.push(/installs the exact Claude session's fail-open Stop hook/i, /resume SessionStart hook/i);
24
24
  checks[1].patterns.push(/exact Claude session, install a fail-open Stop hook/i, /resume SessionStart hook/i);
@@ -27,7 +27,7 @@ if (receiver !== 'native' && receiver !== 'external' && receiver !== 'poll') {
27
27
  checks[2].patterns.push(/--notify-session\b/, /--background\b/);
28
28
  }
29
29
 
30
- const helpText = (args) => {
30
+ const run = (args) => {
31
31
  const result = spawnSync('primitive', args, {
32
32
  encoding: 'utf8',
33
33
  timeout: 10_000,
@@ -35,16 +35,19 @@ if (receiver !== 'native' && receiver !== 'external' && receiver !== 'poll') {
35
35
  });
36
36
  return result.error || result.status !== 0 ? null : result.stdout.replace(/\s+/g, ' ');
37
37
  };
38
+ // Newer CLIs keep --help short and print the full text, which these patterns
39
+ // match, on --help-full. Older CLIs reject --help-full and print it on --help.
40
+ const helpText = (command) => run([...command, '--help-full']) ?? run([...command, '--help']);
38
41
 
39
42
  const missing = [];
40
- for (const { args, label, patterns } of checks) {
41
- const help = helpText(args);
43
+ for (const { command, label, patterns } of checks) {
44
+ const help = helpText(command);
42
45
  if (help === null) {
43
- missing.push(`${label} --help is unavailable`);
46
+ missing.push(`${label} help is unavailable`);
44
47
  continue;
45
48
  }
46
49
  if (patterns.some((pattern) => !pattern.test(help))) {
47
- missing.push(`${label} --help lacks required setup, ${receiver} receiving, or peer-discovery support`);
50
+ missing.push(`${label} help lacks required setup, ${receiver} receiving, or peer-discovery support`);
48
51
  }
49
52
  }
50
53
 
@@ -54,7 +57,7 @@ if (receiver !== 'native' && receiver !== 'external' && receiver !== 'poll') {
54
57
  } else {
55
58
  process.stdout.write(`Primitive CLI preflight passed for ${receiver} receiving and peer discovery.\n`);
56
59
  // Advisory only: older CLIs without `primitive wait` still connect and work.
57
- const wait = helpText(['wait', '--help']);
60
+ const wait = helpText(['wait']);
58
61
  process.stdout.write(wait !== null && /--code\b/.test(wait) && /--link\b/.test(wait)
59
62
  ? 'primitive wait: available (--code, --link).\n'
60
63
  : 'primitive wait: not in this CLI; run `primitive agent check-mail` every few seconds until the email is listed, then read it with `primitive emails get --id <id> --context`.\n');
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "primitive-connect",
3
- "version": "9b2fce59986cef45",
3
+ "version": "38229bc689e6b37b",
4
4
  "source": {
5
5
  "repository": "https://github.com/primitivedotdev/skills",
6
6
  "path": "skills/primitive-connect",
7
- "commit": "3c62147a74119a9570aa8526c2c591c47830f6fc"
7
+ "commit": "a737d72b70ad813a5539a3f492546da52998ec0e"
8
8
  },
9
9
  "files": {
10
10
  "SKILL.md": "37d40b66010319c6a176bc8eb66affd93cab0968c609ad523dac087d3077d29d",
@@ -15,13 +15,13 @@
15
15
  "references/connect.md": "dc39fb8bbaffa69e30a3895f195beb12cfc95c991140ab5a7adb6e1e3293272a",
16
16
  "references/contact-requests.md": "a6afb51ec2cd0bf807095e034942c6e81c82c8b8489a3e2464cd8ce9f4c28d43",
17
17
  "references/contacts.md": "11af5638bd4799f10bfa6fdc1c1cebf90db7c8a43d4ed5498b96c14779f1a482",
18
- "references/conversations.md": "2d4ffed35e4d2402f2becb567f76aa01834b24e873f1d6074a3159a276f96cf8",
18
+ "references/conversations.md": "c8a1125db76e40e8bf2e550223e4d4e1a86536ca43ccdd835024f6d6502098a7",
19
19
  "references/external-services.md": "211e03cec546d2f83c78bd38985cc64203272df47eb52cf1bdce885bceee3089",
20
20
  "references/manual-setup.md": "433f111c425cb976d153e22c851064e2e730f45229d8747ec91827f3a76b5d0e",
21
- "references/native-session.md": "29986cc84a7cf57fe82f51e70ad3b4971a2a4d3dcf4b7b64e492e71bd58afaeb",
21
+ "references/native-session.md": "badc915ef658a8ef0ca1c6b0d5c4292f3668068754f26dc63eff99e63ac0d7f8",
22
22
  "references/presence-and-receiving.md": "449e1c3c2339c4ad0f08151f2323f3982962e1037c97b24531983567a255b702",
23
23
  "references/private-api-fallback.md": "05e5fc968c080fed2479ad380a3ccc55a897eda9a723650905fcdd9be9cc241b",
24
- "scripts/check-cli-capabilities.mjs": "593b726fdb53d95a658b5740cfb000f25ed38509f7666d99c9e1e5652d34cb0a",
24
+ "scripts/check-cli-capabilities.mjs": "82a30f44a5af119d3f681257d58aa85bd4ed67fac97da508fd2d15ebc2ddb4d3",
25
25
  "scripts/connection.mjs": "2c96eda1e9a277a241555f98dd858523b8f77a9ddd82d52a6ac34e844206c3d6",
26
26
  "scripts/mail.mjs": "bfdfb60a739f1537d0e676dc5ff44fd5a65dba4303e3781dce8754c8d671cf51"
27
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primcli",
3
- "version": "1.72.2",
3
+ "version": "1.74.0",
4
4
  "description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
5
5
  "type": "module",
6
6
  "sideEffects": false,