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.
- package/README.md +41 -10
- package/bin/claude-pending-mail.mjs +292 -136
- package/bin/claude-wake.mjs +129 -60
- package/dist/api-command-fKP3NYa7.js +2904 -0
- package/dist/{cli-config--YTRdJ3t.js → cli-config-C0Y_Ppol.js} +3 -598
- package/dist/client.gen-CaaMP86o.js +597 -0
- package/dist/{listen-background--0peztKE.js → listen-background-cD6n8VYO.js} +1 -1
- package/dist/{machine-session-Be6j29GE.js → machine-session-B6V5-grn.js} +907 -207
- package/dist/{notify-session-Byt5sPWW.js → notify-session-DJAb9SGm.js} +3 -3
- package/dist/oclif/auto-signal-worker.js +4 -3
- package/dist/oclif/auto-signals.js +2 -2
- package/dist/oclif/index.js +272 -200
- package/dist/oclif/listen-supervisor.js +1 -1
- package/dist/oclif/receiver-heal.js +4 -4
- package/dist/oclif/root-signup-hint.js +1 -1
- package/dist/{repeats-BL7ZC-_6-CKMKBlx7.js → repeats-BL7ZC-_6-B_ESsWTH.js} +4005 -1
- package/dist/{scoped-chat-DTFkP360.js → scoped-chat-DAiOb622.js} +10 -2
- package/dist/{shared-mail-files-MwlVE90c.js → shared-mail-files-CZfm7E2k.js} +21 -1
- package/dist/{shared-mail-watch-DtKpsgg8.js → shared-mail-watch-DyQB0MK0.js} +1 -1
- package/dist/{signal-command-DeEEZPbQ.js → signal-command-Cte4mAL3.js} +8 -7
- package/dist/skills/primitive-connect/references/conversations.md +3 -2
- package/dist/skills/primitive-connect/references/native-session.md +4 -2
- package/dist/skills/primitive-connect/scripts/check-cli-capabilities.mjs +14 -11
- package/dist/skills/primitive-connect.json +5 -5
- package/package.json +1 -1
- package/dist/api-command-BrwPbyPP.js +0 -6546
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as classifySignalContent,
|
|
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 {
|
|
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,
|
|
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,
|
|
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-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as tryOwnSharedMail, r as waitForSharedMailChange, t as readSharedMailOwner } from "./shared-mail-watch-
|
|
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
|
|
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 (
|
|
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
|
|
218
|
-
|
|
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
|
|
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
|
|
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
|
-
{
|
|
13
|
-
{
|
|
14
|
-
{
|
|
15
|
-
{
|
|
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] = {
|
|
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
|
|
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 {
|
|
41
|
-
const help = helpText(
|
|
43
|
+
for (const { command, label, patterns } of checks) {
|
|
44
|
+
const help = helpText(command);
|
|
42
45
|
if (help === null) {
|
|
43
|
-
missing.push(`${label}
|
|
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}
|
|
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'
|
|
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": "
|
|
3
|
+
"version": "38229bc689e6b37b",
|
|
4
4
|
"source": {
|
|
5
5
|
"repository": "https://github.com/primitivedotdev/skills",
|
|
6
6
|
"path": "skills/primitive-connect",
|
|
7
|
-
"commit": "
|
|
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": "
|
|
18
|
+
"references/conversations.md": "c8a1125db76e40e8bf2e550223e4d4e1a86536ca43ccdd835024f6d6502098a7",
|
|
19
19
|
"references/external-services.md": "211e03cec546d2f83c78bd38985cc64203272df47eb52cf1bdce885bceee3089",
|
|
20
20
|
"references/manual-setup.md": "433f111c425cb976d153e22c851064e2e730f45229d8747ec91827f3a76b5d0e",
|
|
21
|
-
"references/native-session.md": "
|
|
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": "
|
|
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.
|
|
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,
|