primcli 1.48.0 → 1.50.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 +4 -0
- package/bin/claude-wake.mjs +1 -1
- package/dist/{api-command-DzQ8KxD9.js → api-command-Ci6eyQ8d.js} +142 -7
- package/dist/{connect-skill-DHPc9dVN.js → connect-skill-xtf3Q5IF.js} +4 -3
- package/dist/{machine-session-BaJ1Xmdm.js → machine-session-CY0j2r7S.js} +678 -173
- package/dist/oclif/auto-signal-worker.js +2 -2
- package/dist/oclif/auto-signals.js +1 -1
- package/dist/oclif/index.js +317 -48
- package/dist/oclif/listen-supervisor.js +1 -1
- package/dist/oclif/receiver-heal.js +2 -2
- package/dist/{scoped-chat-DhxtuNXs.js → scoped-chat-DSLrmgWQ.js} +1 -1
- package/dist/{signal-command-BbeV2Nd8.js → signal-command-D9R7d44k.js} +4 -4
- package/dist/skills/primitive-connect/SKILL.md +19 -267
- package/dist/skills/primitive-connect/references/communication.md +7 -0
- package/dist/skills/primitive-connect/references/manual-setup.md +294 -0
- package/dist/skills/primitive-connect.json +5 -4
- package/package.json +1 -1
- /package/dist/{listen-background-CyLc8VBn.js → listen-background-CMfxkeVP.js} +0 -0
- /package/dist/{repeats-D2xtLium-DdiJECLI.js → repeats-D2xtLium-B8Wq41vG.js} +0 -0
- /package/dist/{shared-mail-watch-BUfUaq6j.js → shared-mail-watch-CWhpfJ0N.js} +0 -0
package/dist/oclif/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfilesDirectory, H as requireDefaultLoginProfile, M as AgentConnectionSetupError, N as agentProfileDirectory, O as loadActiveChatState, P as agentProfileName, R as loadConnectedAgentProfile, T as saveSignupCredentials, W as PrimitiveApiClient, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_PROFILE_ENV, k as loadChatConversationByLocalId, l as saveCliConfig, m as cliAccessTokenExpiresAt, n as deleteCliConfig, o as redactCliEnvironment, p as acquireCliCredentialsLock, r as emptyCliConfig, s as removeCliEnvironment, u as upsertCliEnvironment, v as deleteCliCredentialsLock, w as saveCliCredentials, x as loadCliCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-DGrZCUfw.js";
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as replyExpectation, $n as searchMemories, $t as createWakeAuthorization, A as readThreadContext, An as listDomains, At as AUTOMATED_FILTER_UNSUPPORTED_CODE, B as cliInvocation, Bn as listWakeSchedules, Bt as queryUsesAutomated, C as queryUsesAwaiting, Cn as getThread, Ct as serializeErrorPayload, D as displayAddress, Dt as formatAlreadySentNotice, E as describeWake, En as listAgentNetworks, Et as currentMailSessionKey, F as wakeReadCommand, Fn as listRepeatingSends, Ft as automatedRejectedError, G as clearReadPendingMail, Gt as resolveCliApiRequestConfig, Hn as pullWebhookEvent, Ht as createAuthenticatedCliApiClient, I as wakeRecipientField, In as listRoutes, It as ensureSearchReportsAutomated, J as recordPendingMail, Jn as reorderRoutes, Jt as cliLogout, K as pendingMailPath, Kn as redeemCreditCode, Kt as addDefaultNetworkMember, L as wakeRelationship, Ln as listSentEmails, Lt as expectedAutomatedVerdicts, M as serverMuted, Mn as listEndpoints, Mt as AUTOMATED_VALUES, N as serverRelationship, Nn as listFunctionLogs, Nt as AutomatedFilterUnsupportedError, O as formatWakeContext, On as listDefaultNetworkAgents, Ot as writeIdempotentReplayBannerIfReplay, P as skillFirstLine, Pn as listFunctions, Pt as assertAutomatedVerdict, Q as readEmailInteraction, Qn as searchEmails, Qt as createRoute, Rn as listWakeAuthorizations, Rt as hasAutomatedVerdict, S as isAwaitingRejectedError, Sn as getSentEmail, St as sentHistoryWindowStart, T as bareAddress, Tn as listAgentContacts, Tt as thrownErrorExitCode, U as generatedAgentName, Ut as createCliApiClient, V as connectNextSteps, Vn as pollCliLogin, W as identitySuggestions, Wt as refreshStoredCliCredentials, X as interactionHumanLine, Xn as resendAgentSignupVerification, Xt as createEndpoint, Y as removePendingMail, Yn as replyToEmail, Yt as completeWebhookEvent, Z as interactionNextActions, Zn as runWakeSchedule, Zt as createFunction, _ as awaitingRejectedError, _n as getFunctionTestRunTrace, _r as updateWakeSchedule, _t as priorRepliesThatWentOut, a as extractErrorCode, an as deleteWakeAuthorization, ar as simulateRoute, at as apiResultHttpStatus, b as formatRepliesCell, bn as getOrgRoutingTopology, bt as sendOutcomeExitCode, c as readTextFileFlag, cn as getAccount, cr as startCliLogin, ct as buildThrownSendFailureEnvelope, d as writeErrorWithHints, dn as getConversation, dr as unsetFunctionRoute, dt as deriveSendIdempotencyKey, en as createWakeSchedule, er as semanticSearch, et as replyInteractionWarning, f as AWAITING_FLAG_DESCRIPTION, fn as getCreditBalance, fr as updateDefaultNetworkMember, ft as formatDeletedEarlierSendNotice, g as assertAwaitingFilterRows, gn as getFunctionRouting, gr as updateWakeAuthorization, gt as formatSendRecordFailureSummary, h as ReplyStateUnsupportedError, hn as getFunction, hr as updateRoute, ht as formatSendFailureSummary, i as createOperationCommand, in as deleteRoute, ir as setMemory, it as SEND_OUTCOME_HELP, j as sentInThread, jn as listEmails, jt as AUTOMATED_FLAG_DESCRIPTION, k as latestOwnOutbound, kn as listDefaultNetworkMembers, kt as withFlagSuggestion, l as runWithTiming, ln as getAgentContactPolicy, lr as stopRepeatFromEmail, lt as checkPriorReplies, m as REPLY_STATE_UNSUPPORTED_CODE, mn as getEmail, mr as updateRepeatingSend, mt as formatPriorRepliesWarning, n as API_ERROR_CODES, nr as setFunctionRoute, nt as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, o as extractErrorPayload, on as deleteWakeSchedule, or as startAgentClaim, ot as assertValidIdempotencyKey, p as AWAITING_VALUES, pn as getDefaultNetworkAgent, pr as updateFunction, pt as formatPriorRepliesCheckSkipped, q as readPendingMail, qn as removeDefaultNetworkMember, qt as checkDefaultNetworkContactAdmission, r as TIME_FLAG_DESCRIPTION, rn as deleteMemory, rr as setFunctionSecret, rt as MAX_SEND_WAIT_TIMEOUT_MS, s as formatErrorPayload, sr as startAgentSignup, st as buildFollowUpCommand, t as API_BASE_URL_FLAG_DESCRIPTION, tr as sendEmail, tt as wakeInteractionSentence, u as surfaceUnauthorizedHint, ur as testFunction, ut as classifySendError, v as ensureSearchReportsReplyState, vn as getInboxStatus, vr as verifyAgentClaim, vt as reportSendCommandResult, w as explainPendingMailNotFound, wn as getWakeSchedule, wt as successfulSendOutcome, x as hasReplyState, xn as getRepeatingSend, xt as sendWaitClampNotice, y as formatAwaitingCell, yn as getMemory, yr as verifyAgentSignup, yt as sendIdentityFields, z as LOAD_SKILL_LINE, zn as listWakeDispatches, zt as isAutomatedRejectedError } from "../api-command-Ci6eyQ8d.js";
|
|
3
3
|
import { S as resolveListenSubscription, _ as acquireListenLock, a as mailLockBusy, c as mailTime, d as readMailJson, f as removeMailFile, g as ListenStateError, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, o as mailObject, u as privateMailPermissions, x as normalizeListenOrigin, y as listenIdentity } from "../shared-mail-files-SPPcis9g.js";
|
|
4
|
-
import { $ as describeSessionHookItem, A as
|
|
5
|
-
import {
|
|
6
|
-
import { c as
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
import { a as
|
|
10
|
-
import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, restoreAutoWorking } from "./auto-signals.js";
|
|
4
|
+
import { $ as describeSessionHookItem, A as evaluateContactPolicy, B as agentInvitationHash, C as RECEIVER_MODES, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as ContactsApiError, G as saveConnectionName, H as readAgentInvitation, I as contactAddress, J as recordHookMailCheck, K as savedReceiver, L as contactAgentAddress, M as AddressNotesApiError, N as addressNoteTarget, O as NetworkAdmissionPendingError, P as runAddressNotesRequest, Q as describeSessionHookChange, R as runContactRequest, S as runAgentConnect, T as verificationReplySubmitted, U as receiverStatusCommand, V as connectAgent, W as refusalAfterReplacement, X as claudeConfigDir, Y as boundSessionProfiles, Z as currentCliLocation, _ as disconnectAgent, _t as withOwnerMemberAddress, a as endSession, at as readClaudeSettings, b as invitationProfileName, c as pendingSessionDisconnects, ct as writeClaudeSettings, d as registerSession, dt as jsonIndentation, et as ensureHookLauncher, f as retryPendingDisconnects, ft as readManagedFile, g as AgentDisconnectError, gt as refreshOwnerMemberAddressPeriodically, h as enrollmentResumes, ht as readHookLauncherWarning, i as connectedProfilesForSession, it as inspectClaudeHooks, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as MachineFileError, m as enrollAgent, mt as writeManagedFile, n as OMP_MAIN, nt as findOnPath, o as headlessClaudeRun, ot as repairClaudeHooks, p as runDetached, pt as upsertManagedBlock, q as installClaudeWakeHook, r as agentConnectionState, rt as hasMachineSessionHooks, st as samePath, t as MACHINE_RUNTIMES, tt as ephemeralCliReason, u as readClaudeHookInput, ut as inspectManagedBlock, v as connectWarnings, w as externalReceivingState, x as pollCheckCommand, y as defaultAgentProfileName, z as agentConnectionStatus } from "../machine-session-CY0j2r7S.js";
|
|
5
|
+
import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply, o as presenceDisposition, r as isScopedChatReply, s as readPresenceProjection, t as isConnectedChatCredential } from "../scoped-chat-DSLrmgWQ.js";
|
|
6
|
+
import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, b as NotificationOutcomeUnknownError, c as openSessionNotifications, d as isRoutineNotificationContent, f as notificationPartReader, g as defaultSessionSocket, i as installedVersion, l as notificationReceiptPage, m as SESSION_UUID$1, o as notificationScope, p as readConversationStatusContent, r as installConnectSkill, s as notificationSenders, t as connectSkillTarget, u as NotificationRetryError, v as NativeSessionError, y as NativeSessionNotLoadedError } from "../connect-skill-xtf3Q5IF.js";
|
|
7
|
+
import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-CMfxkeVP.js";
|
|
8
|
+
import { a as REPEAT_IDLE_MAX_MINUTES, d as parseRepeatTick, f as preparePresenceAliveEmail, g as isEmailReceivedEvent, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as parseWebhookEvent, y as parseFromHeader } from "../repeats-D2xtLium-B8Wq41vG.js";
|
|
9
|
+
import { a as ensureSharedMailSubscription, c as reserveSharedMailSubscription, d as otherParticipants, f as EventConnection, h as pushFile, i as sharedMailScope, l as followEmailConversation, m as pullFile, n as reconcileChatSend, o as createSharedMailWaiter, p as EventReceiverError, r as openSharedMailReceiver, s as openSharedMailStore, t as sendSignal, u as readConversationFollow } from "../signal-command-D9R7d44k.js";
|
|
10
|
+
import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, restoreAutoWorking } from "./auto-signals.js";
|
|
11
11
|
import { t as guardJsonOutputCommands } from "../json-output-Cf-GFeff.js";
|
|
12
12
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
13
13
|
import { chmodSync, closeSync, constants, cpSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -12255,6 +12255,14 @@ const openapiDocument = {
|
|
|
12255
12255
|
"user_id",
|
|
12256
12256
|
"name"
|
|
12257
12257
|
]
|
|
12258
|
+
},
|
|
12259
|
+
"fyi": {
|
|
12260
|
+
"type": "boolean",
|
|
12261
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
12262
|
+
},
|
|
12263
|
+
"interaction_hint": {
|
|
12264
|
+
"type": "string",
|
|
12265
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
12258
12266
|
}
|
|
12259
12267
|
},
|
|
12260
12268
|
"required": ["direction", "id"]
|
|
@@ -12360,6 +12368,14 @@ const openapiDocument = {
|
|
|
12360
12368
|
"user_id",
|
|
12361
12369
|
"name"
|
|
12362
12370
|
]
|
|
12371
|
+
},
|
|
12372
|
+
"fyi": {
|
|
12373
|
+
"type": "boolean",
|
|
12374
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
12375
|
+
},
|
|
12376
|
+
"interaction_hint": {
|
|
12377
|
+
"type": "string",
|
|
12378
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
12363
12379
|
}
|
|
12364
12380
|
},
|
|
12365
12381
|
"required": [
|
|
@@ -22848,6 +22864,14 @@ const operationManifest = [
|
|
|
22848
22864
|
"user_id",
|
|
22849
22865
|
"name"
|
|
22850
22866
|
]
|
|
22867
|
+
},
|
|
22868
|
+
"fyi": {
|
|
22869
|
+
"type": "boolean",
|
|
22870
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
22871
|
+
},
|
|
22872
|
+
"interaction_hint": {
|
|
22873
|
+
"type": "string",
|
|
22874
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
22851
22875
|
}
|
|
22852
22876
|
},
|
|
22853
22877
|
"required": [
|
|
@@ -34266,6 +34290,14 @@ const operationManifest = [
|
|
|
34266
34290
|
"user_id",
|
|
34267
34291
|
"name"
|
|
34268
34292
|
]
|
|
34293
|
+
},
|
|
34294
|
+
"fyi": {
|
|
34295
|
+
"type": "boolean",
|
|
34296
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
34297
|
+
},
|
|
34298
|
+
"interaction_hint": {
|
|
34299
|
+
"type": "string",
|
|
34300
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
34269
34301
|
}
|
|
34270
34302
|
},
|
|
34271
34303
|
"required": ["direction", "id"]
|
|
@@ -35252,9 +35284,25 @@ function readCursor(path) {
|
|
|
35252
35284
|
} catch {}
|
|
35253
35285
|
return null;
|
|
35254
35286
|
}
|
|
35255
|
-
/**
|
|
35256
|
-
|
|
35257
|
-
|
|
35287
|
+
/**
|
|
35288
|
+
* Mail the CLI handles itself: a presence probe whose authenticated
|
|
35289
|
+
* projection marks it as control mail (verified, or pending while its proof
|
|
35290
|
+
* settles; a rejected one is ordinary mail), and the exact setup challenge
|
|
35291
|
+
* this profile's setup recorded. Nothing is judged by sender or subject: the
|
|
35292
|
+
* owner can write from the control address, and that email is listed like
|
|
35293
|
+
* any other.
|
|
35294
|
+
*/
|
|
35295
|
+
function isControlMail(email, setupChallengeId) {
|
|
35296
|
+
return presenceDisposition(email) !== "ordinary" || setupChallengeId !== null && email.id === setupChallengeId;
|
|
35297
|
+
}
|
|
35298
|
+
/** The setup challenge's email ID saved by this profile's setup, if any. */
|
|
35299
|
+
function savedSetupChallengeId(directory) {
|
|
35300
|
+
try {
|
|
35301
|
+
const id = readMailJson(join(directory, "setup.json"))?.challenge?.id;
|
|
35302
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
35303
|
+
} catch {
|
|
35304
|
+
return null;
|
|
35305
|
+
}
|
|
35258
35306
|
}
|
|
35259
35307
|
async function checkAgentMail(options) {
|
|
35260
35308
|
const { identity } = options;
|
|
@@ -35263,9 +35311,14 @@ async function checkAgentMail(options) {
|
|
|
35263
35311
|
const release = acquireListenLock(directory, "mail-check");
|
|
35264
35312
|
const readCommand = (id) => `PRIMITIVE_AGENT_PROFILE=${identity.profileName} ${options.invocation ?? "primitive"} emails get --id ${id} --brief`;
|
|
35265
35313
|
const to = identity.agentAddress.toLowerCase();
|
|
35314
|
+
const ownerMember = (options.ownerMemberAddress ?? (() => refreshOwnerMemberAddressPeriodically({
|
|
35315
|
+
configDir: options.configDir,
|
|
35316
|
+
profileName: identity.profileName
|
|
35317
|
+
})))().catch(() => identity.ownerMemberAddress ?? null);
|
|
35266
35318
|
try {
|
|
35267
35319
|
const path = join(directory, CURSOR_FILE);
|
|
35268
35320
|
const saved = readCursor(path);
|
|
35321
|
+
const setupChallenge = savedSetupChallengeId(directory);
|
|
35269
35322
|
let cursor = saved;
|
|
35270
35323
|
const emails = [];
|
|
35271
35324
|
let controlSkipped = 0;
|
|
@@ -35285,7 +35338,7 @@ async function checkAgentMail(options) {
|
|
|
35285
35338
|
});
|
|
35286
35339
|
if (result.error || !result.data) throw new MailCheckApiError(result.error);
|
|
35287
35340
|
const rows = result.data.data ?? [];
|
|
35288
|
-
for (const row of rows) if (isControlMail(row,
|
|
35341
|
+
for (const row of rows) if (isControlMail(row, setupChallenge)) controlSkipped++;
|
|
35289
35342
|
else emails.push({
|
|
35290
35343
|
id: row.id,
|
|
35291
35344
|
received_at: row.received_at,
|
|
@@ -35300,10 +35353,12 @@ async function checkAgentMail(options) {
|
|
|
35300
35353
|
more = page === maxPages - 1;
|
|
35301
35354
|
}
|
|
35302
35355
|
const output = {
|
|
35356
|
+
...emails.length > 0 ? { guidance: LOAD_SKILL_LINE } : {},
|
|
35303
35357
|
outcome: emails.length > 0 ? "mail" : "empty",
|
|
35304
35358
|
emails,
|
|
35305
35359
|
more,
|
|
35306
35360
|
control_skipped: controlSkipped,
|
|
35361
|
+
owner_member_address: await ownerMember,
|
|
35307
35362
|
profile: identity.profileName,
|
|
35308
35363
|
to,
|
|
35309
35364
|
read_command: readCommand("<id>")
|
|
@@ -35328,9 +35383,9 @@ var AgentCheckMailCommand = class AgentCheckMailCommand extends Command {
|
|
|
35328
35383
|
static summary = "Check a connected agent's mail since its last check";
|
|
35329
35384
|
static description = `Print the mail that reached the selected connected agent profile since its previous check, oldest first, once, without waiting. This is how an agent connected with poll receiving (no local session ID or hooks, so nothing can wake it) receives mail: run it at the start of each turn and after sending, then read each email with its \`read_command\`, which selects this profile: an email is readable only under the profile that received it.
|
|
35330
35385
|
|
|
35331
|
-
Acknowledgements marked fyi and mail in muted threads are left out.
|
|
35386
|
+
Acknowledgements marked fyi and mail in muted threads are left out. Presence probes and this profile's setup challenge are handled by the CLI and only counted in \`control_skipped\`; any other email from the connection's control address, such as one the owner wrote from it, is listed like any other email. The position is saved privately with the profile and moves forward only after mail was read, so mail is reported again rather than lost when a check fails or is interrupted; deduplicate by email ID. The first check starts before the address's first email. \`more: true\` means more new mail remains: handle these and check again.
|
|
35332
35387
|
|
|
35333
|
-
Requires a connected agent profile selected with ${AGENT_PROFILE_ENV}. Output is one JSON document: \`outcome\` ("mail" | "empty"), \`emails\` (id, received_at, sender, thread_id, to, and the read_command that reads that email under this profile; never subject or body), \`more\`, \`control_skipped\`, \`profile\`, \`to\` (the address this profile receives for) and \`read_command\` (the template with <id>).`;
|
|
35388
|
+
Requires a connected agent profile selected with ${AGENT_PROFILE_ENV}. Output is one JSON document: \`guidance\` (present only with mail: one line to load the primitive-connect skill before handling it), \`outcome\` ("mail" | "empty"), \`emails\` (id, received_at, sender, thread_id, to, and the read_command that reads that email under this profile; never subject or body), \`more\`, \`control_skipped\`, \`owner_member_address\` (the owner's personal address, where reports and questions go, or null when none is known; refreshed from the server every few minutes), \`profile\`, \`to\` (the address this profile receives for) and \`read_command\` (the template with <id>).`;
|
|
35334
35389
|
static examples = [`${AGENT_PROFILE_ENV}=connection-0123456789ab <%= config.bin %> agent check-mail --json`];
|
|
35335
35390
|
static flags = { json: Flags.boolean({ description: "Print JSON (already the default)" }) };
|
|
35336
35391
|
async run() {
|
|
@@ -35489,7 +35544,7 @@ function invocation$1(entry) {
|
|
|
35489
35544
|
return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
|
|
35490
35545
|
}
|
|
35491
35546
|
var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
35492
|
-
static description = `Connect this coding session to Primitive in one call. With --session, the command checks this CLI's capabilities, installs or refreshes the matching primitive-connect skill for the detected runtime (Claude Code or Codex) from files bundled in this CLI, claims the owner's private setup invitation from piped stdin, answers one authenticated setup challenge, enables owner notifications, starts receiving, seeds a private AGENT_INFO note from --name and --info when absent, and prints one JSON result. The receiver defaults to external hooks in Claude Code and the native background listener elsewhere. Without a session (no --session, or an empty one) the receiver is poll: for runtimes with no local session ID or hooks, such as a cloud-hosted conversation whose commands run in a separate sandbox, the invitation is claimed and verified the same way, nothing is installed, and the result's receiving.checkCommand is the command the agent runs to check for new mail at the start of each turn and after it sends. Native receiving starts or reuses a supervised background listener and waits briefly for its first successful mail check; external receiving installs the exact Claude session's fail-open Stop hook and resume SessionStart hook after the verification reply; a real idle mail event must still verify wake. The profile defaults to session-<session>, or to connection-<invitation hash prefix> without a session. --resume continues saved progress without reading stdin or replaying a claim or uncertain verification send. After the reply is submitted it waits up to a minute for the server to confirm the connection and reports verification.state verified, or reply_submitted while confirmation is still pending. Verification is separate from receiver health. Both official production (https://api.primitive.dev/v1) and staging (https://api.primitive-staging-1.com/v1) invitations pin their API origin. Never pass an invitation as a command argument. One address per session: if this session already has a connected Primitive address, including one saved in the profile being connected, the command claims nothing, leaves stdin unread, and exits 3 with status already_connected. Its detail names how to continue that profile's own connection when that is the only one: --resume for a setup with saved progress, or --keep-existing to refresh a claim-only profile from the same invitation. Otherwise ask the user whether to keep the existing address or disconnect it first, then rerun with --keep-existing or --replace-existing. Never choose for the user. Select the saved identity with PRIMITIVE_AGENT_PROFILE. Use --status --json with --profile, or with PRIMITIVE_AGENT_PROFILE set, to inspect saved identity and local receiver health offline; it exits 1 when the profile is not configured.`;
|
|
35547
|
+
static description = `Connect this coding session to Primitive in one call. With --session, the command checks this CLI's capabilities, installs or refreshes the matching primitive-connect skill for the detected runtime (Claude Code or Codex) from files bundled in this CLI, claims the owner's private setup invitation from piped stdin, answers one authenticated setup challenge, enables owner notifications, starts receiving, seeds a private AGENT_INFO note from --name and --info when absent, and prints one JSON result. The receiver defaults to external hooks in Claude Code and the native background listener elsewhere. When the native default cannot work because this machine has no local session socket (for example Codex older than 0.158.0), the command connects with poll receiving instead and reports receiving.fallbackReason and receiving.fallbackDetail. Without a session (no --session, or an empty one) the receiver is poll: for runtimes with no local session ID or hooks, such as a cloud-hosted conversation whose commands run in a separate sandbox, the invitation is claimed and verified the same way, nothing is installed, and the result's receiving.checkCommand is the command the agent runs to check for new mail at the start of each turn and after it sends. Native receiving starts or reuses a supervised background listener and waits briefly for its first successful mail check; external receiving installs the exact Claude session's fail-open Stop hook and resume SessionStart hook after the verification reply; a real idle mail event must still verify wake. The profile defaults to session-<session>, or to connection-<invitation hash prefix> without a session. --resume continues saved progress without reading stdin or replaying a claim or uncertain verification send. After the reply is submitted it waits up to a minute for the server to confirm the connection and reports verification.state verified, or reply_submitted while confirmation is still pending. Verification is separate from receiver health. Both official production (https://api.primitive.dev/v1) and staging (https://api.primitive-staging-1.com/v1) invitations pin their API origin. Never pass an invitation as a command argument. One address per session: if this session already has a connected Primitive address, including one saved in the profile being connected, the command claims nothing, leaves stdin unread, and exits 3 with status already_connected. Its detail names how to continue that profile's own connection when that is the only one: --resume for a setup with saved progress, or --keep-existing to refresh a claim-only profile from the same invitation. Otherwise ask the user whether to keep the existing address or disconnect it first, then rerun with --keep-existing or --replace-existing. Never choose for the user. Select the saved identity with PRIMITIVE_AGENT_PROFILE. Use --status --json with --profile, or with PRIMITIVE_AGENT_PROFILE set, to inspect saved identity and local receiver health offline; it exits 1 when the profile is not configured. A completed connection's result carries nextSteps, in order: report to the owner in two short sentences, make one offer to rename a generated name and record where the agent runs, and load the primitive-connect skill before handling mail. When the claim reported one, the result also carries the connection's name and nameIsDefault.`;
|
|
35493
35548
|
static summary = "Connect and verify an agent address";
|
|
35494
35549
|
static examples = [
|
|
35495
35550
|
"<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
|
|
@@ -35552,6 +35607,7 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35552
35607
|
async run() {
|
|
35553
35608
|
const { flags } = await this.parse(AgentConnectCommand);
|
|
35554
35609
|
let pollProfile;
|
|
35610
|
+
let replaced = [];
|
|
35555
35611
|
try {
|
|
35556
35612
|
if (flags.status) {
|
|
35557
35613
|
const profileName = flags.profile ?? (process.env["PRIMITIVE_AGENT_PROFILE"]?.trim() || void 0);
|
|
@@ -35583,7 +35639,6 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35583
35639
|
invitationText ??= await readAgentInvitation(process.stdin, process.stdin.isTTY);
|
|
35584
35640
|
return invitationText;
|
|
35585
35641
|
};
|
|
35586
|
-
let replaced = [];
|
|
35587
35642
|
let replaceExisting;
|
|
35588
35643
|
const guardSession = flags.resume ? null : flags.session !== void 0 ? session?.toLowerCase() ?? null : guardedSession(void 0);
|
|
35589
35644
|
if (guardSession && !flags["keep-existing"]) {
|
|
@@ -35644,13 +35699,25 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35644
35699
|
});
|
|
35645
35700
|
const external = output.receiving.mode === "external";
|
|
35646
35701
|
const poll = output.receiving.mode === "poll";
|
|
35702
|
+
const identity = identitySuggestions({
|
|
35703
|
+
invocation: invocation$1(process.argv[1]),
|
|
35704
|
+
profile: output.profile,
|
|
35705
|
+
..."nameIsDefault" in output ? { nameIsDefault: output.nameIsDefault } : {},
|
|
35706
|
+
connectedNow: output.status === "connected"
|
|
35707
|
+
});
|
|
35708
|
+
const nextSteps = output.status === "connected" ? connectNextSteps({
|
|
35709
|
+
address: output.address,
|
|
35710
|
+
receiving: output.receiving,
|
|
35711
|
+
..."name" in output ? { name: output.name } : {},
|
|
35712
|
+
suggestions: identity.suggestions
|
|
35713
|
+
}) : [];
|
|
35647
35714
|
const suggested = {
|
|
35648
35715
|
...output,
|
|
35649
|
-
...
|
|
35650
|
-
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
}
|
|
35716
|
+
...identity,
|
|
35717
|
+
...nextSteps.length ? {
|
|
35718
|
+
nextSteps,
|
|
35719
|
+
guidance: `${output.guidance} Next: ${nextSteps.join(" ")}`
|
|
35720
|
+
} : {}
|
|
35654
35721
|
};
|
|
35655
35722
|
if (flags.json) this.log(JSON.stringify(replaced.length ? {
|
|
35656
35723
|
...suggested,
|
|
@@ -35659,10 +35726,18 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35659
35726
|
else {
|
|
35660
35727
|
this.log(`Agent ${output.address}: verification ${output.verification.state}; receiving ${output.receiving.state}; skill ${output.skill.state}.`);
|
|
35661
35728
|
this.log(`Select it with ${output.selectProfile}.`);
|
|
35729
|
+
for (const warning of output.warnings) this.log(warning.message);
|
|
35662
35730
|
if (output.ownerNotifications === "silenced") this.log("Owner notifications remain silenced by the existing preference or policy.");
|
|
35663
35731
|
if (external && verificationReplySubmitted(output.verification.state)) this.log(output.externalHook === "installed_unverified" ? "External receive hook installed. Idle wake still needs a live mail check." : "External receive hook is unavailable. Pairing may complete, but this session will not wake automatically.");
|
|
35732
|
+
if (poll && "fallbackDetail" in output.receiving) this.log(String(output.receiving.fallbackDetail));
|
|
35664
35733
|
if (poll && "checkCommand" in output.receiving) this.log(`Nothing wakes this session for new mail. Check it at the start of each turn and after sending with: ${output.receiving.checkCommand}`);
|
|
35665
35734
|
if (output.status !== "connected") this.log(`Resume: ${output.resumeCommand}`);
|
|
35735
|
+
if (nextSteps.length) {
|
|
35736
|
+
this.log("Next steps:");
|
|
35737
|
+
nextSteps.forEach((step, index) => {
|
|
35738
|
+
this.log(`${index + 1}. ${step}`);
|
|
35739
|
+
});
|
|
35740
|
+
}
|
|
35666
35741
|
}
|
|
35667
35742
|
if (external && output.externalHook !== "installed_unverified" || !external && !poll && output.receiving.state !== "healthy" || !verificationReplySubmitted(output.verification.state)) process.exitCode = 2;
|
|
35668
35743
|
return;
|
|
@@ -35680,16 +35755,23 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35680
35755
|
configDir: this.config.configDir,
|
|
35681
35756
|
onlyIfUnknown: claimed.status === "claimed"
|
|
35682
35757
|
});
|
|
35758
|
+
const warnings = connectWarnings(this.config.configDir, result.identity.profileName);
|
|
35683
35759
|
if (flags.json) this.log(JSON.stringify(replaced.length ? {
|
|
35684
35760
|
...result,
|
|
35761
|
+
warnings,
|
|
35685
35762
|
replacedExisting: replaced
|
|
35686
|
-
} :
|
|
35763
|
+
} : {
|
|
35764
|
+
...result,
|
|
35765
|
+
warnings
|
|
35766
|
+
}));
|
|
35687
35767
|
else {
|
|
35688
35768
|
this.log(result.status === "claimed" ? "Agent profile saved. Email readiness is not yet verified." : "Agent profile already configured; no claim was sent.");
|
|
35689
35769
|
this.log(`Select it with PRIMITIVE_AGENT_PROFILE=${result.identity.profileName}.`);
|
|
35690
35770
|
this.log(result.ownerReportGuidance);
|
|
35771
|
+
for (const warning of warnings) this.log(warning.message);
|
|
35691
35772
|
}
|
|
35692
|
-
} catch (
|
|
35773
|
+
} catch (caught) {
|
|
35774
|
+
const error = refusalAfterReplacement(caught, replaced);
|
|
35693
35775
|
throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : flags.session?.trim() ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${flags.profile ?? defaultAgentProfileName(flags.session.trim())} --session ${flags.session.trim()}${flags.receiver ? ` --receiver ${flags.receiver}` : ""} --resume --json. Do not manually resend verification or reclaim the invitation.` : pollProfile ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${pollProfile} --receiver poll --resume --json. Do not manually resend verification or reclaim the invitation.` : "Agent setup did not complete. Preserve the private profile and request a fresh owner invitation if a claim may have been submitted.", { exit: 1 });
|
|
35694
35776
|
}
|
|
35695
35777
|
}
|
|
@@ -35975,6 +36057,7 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
35975
36057
|
agentAddress: result.identity.agentAddress,
|
|
35976
36058
|
sessionId: flags.session
|
|
35977
36059
|
}) : null;
|
|
36060
|
+
const warnings = connectWarnings(this.config.configDir, result.identity.profileName);
|
|
35978
36061
|
const output = result.receiving.state === "poll" ? {
|
|
35979
36062
|
...result,
|
|
35980
36063
|
receiving: {
|
|
@@ -35982,10 +36065,20 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
35982
36065
|
mode: "poll",
|
|
35983
36066
|
checkCommand: pollCheckCommand(invocation(process.argv[1]), result.identity.profileName)
|
|
35984
36067
|
},
|
|
35985
|
-
externalHook
|
|
36068
|
+
externalHook,
|
|
36069
|
+
warnings
|
|
36070
|
+
} : flags.receiver === "external" && result.connection.status !== "owner_inactive" ? {
|
|
36071
|
+
...result,
|
|
36072
|
+
receiving: {
|
|
36073
|
+
...result.receiving,
|
|
36074
|
+
state: externalReceivingState(externalHook)
|
|
36075
|
+
},
|
|
36076
|
+
externalHook,
|
|
36077
|
+
warnings
|
|
35986
36078
|
} : {
|
|
35987
36079
|
...result,
|
|
35988
|
-
externalHook
|
|
36080
|
+
externalHook,
|
|
36081
|
+
warnings
|
|
35989
36082
|
};
|
|
35990
36083
|
const identity = identitySuggestions({
|
|
35991
36084
|
invocation: invocation(process.argv[1]),
|
|
@@ -35993,18 +36086,33 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
35993
36086
|
nameIsDefault: generatedAgentName(result.name),
|
|
35994
36087
|
connectedNow: result.connection.status === "connected"
|
|
35995
36088
|
});
|
|
35996
|
-
const
|
|
36089
|
+
const nextSteps = result.connection.status === "connected" ? connectNextSteps({
|
|
36090
|
+
address: result.identity.agentAddress,
|
|
36091
|
+
receiving: {
|
|
36092
|
+
state: output.receiving.state,
|
|
36093
|
+
mode: "mode" in output.receiving ? String(output.receiving.mode) : flags.receiver === "external" ? "external" : "native"
|
|
36094
|
+
},
|
|
36095
|
+
name: result.name,
|
|
36096
|
+
suggestions: identity.suggestions
|
|
36097
|
+
}) : [];
|
|
36098
|
+
const guided = nextSteps.length ? {
|
|
35997
36099
|
...output,
|
|
35998
36100
|
...identity,
|
|
35999
|
-
|
|
36101
|
+
nextSteps,
|
|
36102
|
+
guidance: `${output.guidance} Next: ${nextSteps.join(" ")}`
|
|
36000
36103
|
} : {
|
|
36001
36104
|
...output,
|
|
36002
36105
|
...identity
|
|
36003
36106
|
};
|
|
36107
|
+
const printed = replaced.length ? {
|
|
36108
|
+
...guided,
|
|
36109
|
+
replacedExisting: replaced
|
|
36110
|
+
} : guided;
|
|
36004
36111
|
if (flags.json) this.log(JSON.stringify(printed));
|
|
36005
36112
|
else {
|
|
36006
|
-
this.log(`Agent ${result.identity.agentAddress}: pairing ${result.connection.status}; verification ${result.verification.state}; receiving ${
|
|
36113
|
+
this.log(`Agent ${result.identity.agentAddress}: pairing ${result.connection.status}; verification ${result.verification.state}; receiving ${output.receiving.state}.`);
|
|
36007
36114
|
this.log(`Select it with PRIMITIVE_AGENT_PROFILE=${result.identity.profileName}.`);
|
|
36115
|
+
for (const warning of warnings) this.log(warning.message);
|
|
36008
36116
|
if (flags["contact-requests"]) this.log(`Contact requests: ${result.contactRequestPolicy}.`);
|
|
36009
36117
|
if (result.contactRequestPolicy === "owner_disabled") this.log("The owner disabled contact requests for this address. Review the policy in the app if you want them enabled.");
|
|
36010
36118
|
if (result.contactRequestPolicy === "unavailable") this.log("Contact-request policy could not be confirmed. Receiving can still be configured; rerun this exact enrollment after policy access is restored.");
|
|
@@ -36013,6 +36121,12 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
36013
36121
|
if (result.connection.status !== "connected") this.log("Pairing is not yet confirmed. Rerun this command with the same options to resume the saved session; do not create another address.");
|
|
36014
36122
|
else if ("checkCommand" in output.receiving) this.log(`Nothing wakes this session for new mail. Check it at the start of each turn and after sending with: ${output.receiving.checkCommand}`);
|
|
36015
36123
|
else if (result.receiving.state !== "healthy") this.log("Pairing is confirmed; receiving needs separate setup or recovery.");
|
|
36124
|
+
if (nextSteps.length) {
|
|
36125
|
+
this.log("Next steps:");
|
|
36126
|
+
nextSteps.forEach((step, index) => {
|
|
36127
|
+
this.log(`${index + 1}. ${step}`);
|
|
36128
|
+
});
|
|
36129
|
+
}
|
|
36016
36130
|
}
|
|
36017
36131
|
if (externalHook === "unavailable" || flags["contact-requests"] && result.contactRequestPolicy !== "enabled" || result.connection.status !== "connected" || flags.receiver !== "external" && flags.receiver !== "poll" && result.receiving.state !== "healthy") process.exitCode = 2;
|
|
36018
36132
|
} catch (error) {
|
|
@@ -36293,6 +36407,8 @@ var AgentRenameCommand = class AgentRenameCommand extends Command {
|
|
|
36293
36407
|
address,
|
|
36294
36408
|
name
|
|
36295
36409
|
});
|
|
36410
|
+
const own = auth.connectedAgent;
|
|
36411
|
+
if (own && own.agentAddress.toLowerCase() === connection.address.toLowerCase()) saveConnectionName(this.config.configDir, own.profileName, connection.name);
|
|
36296
36412
|
if (flags.json) this.log(JSON.stringify({ connection }, null, 2));
|
|
36297
36413
|
else this.log(`Renamed ${connection.address} to "${connection.name}". The address is unchanged.`);
|
|
36298
36414
|
} catch (error) {
|
|
@@ -36629,6 +36745,66 @@ var AgentSessionRegisterCommand = class AgentSessionRegisterCommand extends Comm
|
|
|
36629
36745
|
}
|
|
36630
36746
|
};
|
|
36631
36747
|
//#endregion
|
|
36748
|
+
//#region src/oclif/commands/agent-status.ts
|
|
36749
|
+
/**
|
|
36750
|
+
* Picks the profile `agent status` reads: an explicit --profile, then
|
|
36751
|
+
* PRIMITIVE_AGENT_PROFILE, then the one address connected for this runtime
|
|
36752
|
+
* session. Null when none applies or the session has several addresses.
|
|
36753
|
+
*/
|
|
36754
|
+
function statusProfile(params) {
|
|
36755
|
+
const env = params.env ?? process.env;
|
|
36756
|
+
const chosen = params.profile ?? (env["PRIMITIVE_AGENT_PROFILE"]?.trim() || void 0);
|
|
36757
|
+
if (chosen) return { profile: chosen };
|
|
36758
|
+
const session = guardedSession(void 0, env);
|
|
36759
|
+
if (!session) return {
|
|
36760
|
+
profile: null,
|
|
36761
|
+
bound: []
|
|
36762
|
+
};
|
|
36763
|
+
const { others } = inspectSessionAddresses({
|
|
36764
|
+
configDir: params.configDir,
|
|
36765
|
+
session
|
|
36766
|
+
});
|
|
36767
|
+
const only = others.length === 1 ? others[0] : void 0;
|
|
36768
|
+
return only ? { profile: only.profile } : {
|
|
36769
|
+
profile: null,
|
|
36770
|
+
bound: others.map((row) => row.profile)
|
|
36771
|
+
};
|
|
36772
|
+
}
|
|
36773
|
+
/** A hidden alias of `agent connect --status`, for the command agents guess. */
|
|
36774
|
+
var AgentStatusCommand = class AgentStatusCommand extends Command {
|
|
36775
|
+
static hidden = true;
|
|
36776
|
+
static summary = "Show this agent's saved connection (agent connect --status)";
|
|
36777
|
+
static description = `Same as \`agent connect --status\`: read the saved identity and local receiver health offline. The profile is --profile, else ${AGENT_PROFILE_ENV}, else the one address connected for this runtime session.`;
|
|
36778
|
+
static flags = {
|
|
36779
|
+
profile: Flags.string({ description: "Local agent profile name" }),
|
|
36780
|
+
json: Flags.boolean({ description: "Print the status as JSON" })
|
|
36781
|
+
};
|
|
36782
|
+
async run() {
|
|
36783
|
+
const { flags } = await this.parse(AgentStatusCommand);
|
|
36784
|
+
const picked = statusProfile({
|
|
36785
|
+
configDir: this.config.configDir,
|
|
36786
|
+
profile: flags.profile
|
|
36787
|
+
});
|
|
36788
|
+
if (picked.profile === null) {
|
|
36789
|
+
const detail = picked.bound.length > 1 ? `This session has several connected profiles (${picked.bound.join(", ")}). Pass --profile <name>.` : `No Primitive address is connected for this session. Pass --profile <name> or set ${AGENT_PROFILE_ENV} to inspect a saved profile.`;
|
|
36790
|
+
this.log(flags.json ? JSON.stringify({
|
|
36791
|
+
status: "not_configured",
|
|
36792
|
+
profileName: null,
|
|
36793
|
+
...picked.bound.length > 1 ? { profiles: picked.bound } : {},
|
|
36794
|
+
detail
|
|
36795
|
+
}) : detail);
|
|
36796
|
+
process.exitCode = 1;
|
|
36797
|
+
return;
|
|
36798
|
+
}
|
|
36799
|
+
await AgentConnectCommand.run([
|
|
36800
|
+
"--status",
|
|
36801
|
+
"--profile",
|
|
36802
|
+
picked.profile,
|
|
36803
|
+
...flags.json ? ["--json"] : []
|
|
36804
|
+
], this.config);
|
|
36805
|
+
}
|
|
36806
|
+
};
|
|
36807
|
+
//#endregion
|
|
36632
36808
|
//#region src/oclif/commands/agent-upgrade.ts
|
|
36633
36809
|
/**
|
|
36634
36810
|
* Interactive upgrade of an emailless agent account to a full developer
|
|
@@ -41505,7 +41681,7 @@ async function buildEmailBrief(input) {
|
|
|
41505
41681
|
connected_agent_verified: detail.sender_connected_agent_verified === true
|
|
41506
41682
|
},
|
|
41507
41683
|
thread_id: threadId,
|
|
41508
|
-
in_thread: sentInThread(thread, self) ?? null,
|
|
41684
|
+
in_thread: sentInThread(thread, self, input.isOwnSignal) ?? null,
|
|
41509
41685
|
...alsoAddressed(otherParticipants(detail, self)),
|
|
41510
41686
|
attachments: briefAttachments(detail, { hideInteractionPart: interaction?.hint === "card" || interaction?.hint === "status" }),
|
|
41511
41687
|
newer: thread?.newerInboundCount === void 0 ? null : {
|
|
@@ -41614,6 +41790,7 @@ async function runBrief(command, flags) {
|
|
|
41614
41790
|
client: apiClient.client,
|
|
41615
41791
|
detail,
|
|
41616
41792
|
connected,
|
|
41793
|
+
isOwnSignal: (sentId) => isSentSignal(command.config.configDir, sentId),
|
|
41617
41794
|
signal: AbortSignal.timeout(3e4)
|
|
41618
41795
|
});
|
|
41619
41796
|
command.log(flags.json ? JSON.stringify(brief, null, 2) : renderEmailBrief(brief));
|
|
@@ -43317,7 +43494,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
43317
43494
|
url: "https://primitive.dev"
|
|
43318
43495
|
};
|
|
43319
43496
|
const SDK_VERSION_RANGE = "^1.35.0";
|
|
43320
|
-
const CLI_VERSION_RANGE = "^1.
|
|
43497
|
+
const CLI_VERSION_RANGE = "^1.50.0";
|
|
43321
43498
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
43322
43499
|
function renderHandler() {
|
|
43323
43500
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -46848,6 +47025,7 @@ async function runSharedNotificationListen(options) {
|
|
|
46848
47025
|
contact: !authorization?.contactRequest
|
|
46849
47026
|
}),
|
|
46850
47027
|
localInThread: false,
|
|
47028
|
+
isOwnSignal: (sentId) => isSentSignal(options.configDir, sentId),
|
|
46851
47029
|
signal: nextSignal
|
|
46852
47030
|
});
|
|
46853
47031
|
} catch {
|
|
@@ -47563,6 +47741,10 @@ async function createWakeMail(options) {
|
|
|
47563
47741
|
});
|
|
47564
47742
|
const identity = auth.connectedAgent;
|
|
47565
47743
|
const recipient = identity?.agentAddress;
|
|
47744
|
+
if (identity) refreshOwnerMemberAddressPeriodically({
|
|
47745
|
+
configDir: options.configDir,
|
|
47746
|
+
profileName: identity.profileName
|
|
47747
|
+
});
|
|
47566
47748
|
const policy = recipient ? apiContactPolicy(apiClient.client, recipient, options.contactRequests) : void 0;
|
|
47567
47749
|
const notices = identity && options.contactRequests ? openContactRequestNotices(options.configDir, identity) : void 0;
|
|
47568
47750
|
const activatedAt = notices ? Date.parse(notices.activate()) : 0;
|
|
@@ -47799,6 +47981,7 @@ async function createWakeMail(options) {
|
|
|
47799
47981
|
contact: admission.kind === "allowed"
|
|
47800
47982
|
}),
|
|
47801
47983
|
localInThread: Boolean(requested || followed),
|
|
47984
|
+
isOwnSignal: (sentId) => isSentSignal(options.configDir, sentId),
|
|
47802
47985
|
signal
|
|
47803
47986
|
});
|
|
47804
47987
|
if (!await recordNotice(detail, {
|
|
@@ -48390,7 +48573,8 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
48390
48573
|
const context = wake.context?.();
|
|
48391
48574
|
const metadata = context ? ` ${formatWakeContext(context)}` : "";
|
|
48392
48575
|
const interaction = wakeInteractionSentence(context?.interaction);
|
|
48393
|
-
|
|
48576
|
+
const skillFirst = skillFirstLine(relation ?? context?.relationship);
|
|
48577
|
+
process.stderr.write(`${skillFirst ? `${skillFirst}\n` : ""}Primitive mail arrived: ${wake.wakeId()}${recipient}${metadata}. Read with ${wakeReadCommand(wake.wakeId() ?? "", hookProfileName)}.${interaction} ${authority}\n`);
|
|
48394
48578
|
process.exitCode = 2;
|
|
48395
48579
|
const auto = wake.autoSignal?.();
|
|
48396
48580
|
if (auto) dispatchAutoRead({
|
|
@@ -49848,7 +50032,7 @@ function shellQuote(value) {
|
|
|
49848
50032
|
}
|
|
49849
50033
|
const MANAGED_SUFFIX = /\bagent session-register --runtime codex --hook\s*$/;
|
|
49850
50034
|
function codexHookCommand(cli) {
|
|
49851
|
-
return `${shellQuote(cli.
|
|
50035
|
+
return `${shellQuote(cli.runtimeNode)} ${shellQuote(cli.entry)} agent session-register --runtime codex --hook`;
|
|
49852
50036
|
}
|
|
49853
50037
|
/** A SessionStart hook this CLI installed, from any CLI path or version. */
|
|
49854
50038
|
function isManaged(hook) {
|
|
@@ -50074,6 +50258,10 @@ function repairCodexHook(settings, cli) {
|
|
|
50074
50258
|
changed
|
|
50075
50259
|
};
|
|
50076
50260
|
}
|
|
50261
|
+
/** Whether Codex hooks.json carries the SessionStart hook, from any CLI path. */
|
|
50262
|
+
function hasCodexSessionHook(settings) {
|
|
50263
|
+
return managedPositions(settings).length > 0;
|
|
50264
|
+
}
|
|
50077
50265
|
function writeCodexHooks(params) {
|
|
50078
50266
|
const original = params.read.state === "present" ? params.read.text : null;
|
|
50079
50267
|
const indent = original === null ? 2 : jsonIndentation(original);
|
|
@@ -50312,6 +50500,29 @@ async function ownerConnectionStatuses(credentials, fetchImpl) {
|
|
|
50312
50500
|
}
|
|
50313
50501
|
return partial();
|
|
50314
50502
|
}
|
|
50503
|
+
/** Whether any saved agent profile still holds a credential. */
|
|
50504
|
+
function connectedProfileSaved(configDir) {
|
|
50505
|
+
let names;
|
|
50506
|
+
try {
|
|
50507
|
+
names = readdirSync(join(agentProfilesDirectory(configDir), "profiles"));
|
|
50508
|
+
} catch {
|
|
50509
|
+
return false;
|
|
50510
|
+
}
|
|
50511
|
+
return names.some((name) => {
|
|
50512
|
+
try {
|
|
50513
|
+
agentProfileName(name);
|
|
50514
|
+
return loadConnectedAgentProfile(configDir, name) !== null;
|
|
50515
|
+
} catch {
|
|
50516
|
+
return false;
|
|
50517
|
+
}
|
|
50518
|
+
});
|
|
50519
|
+
}
|
|
50520
|
+
/**
|
|
50521
|
+
* Why a machine-wide check does not apply: agents on this machine were
|
|
50522
|
+
* connected one session at a time with `agent connect`, and no member login
|
|
50523
|
+
* (which machine-wide session registration needs) is saved.
|
|
50524
|
+
*/
|
|
50525
|
+
const PER_CONNECTION_SKIP = "Not set up: agents on this machine were connected one session at a time with `primitive agent connect`, which does not need it. To register every session on this machine automatically, sign in with `primitive login`, then run `primitive machine doctor --fix`.";
|
|
50315
50526
|
function check(id, status, detail, extra = {}) {
|
|
50316
50527
|
return {
|
|
50317
50528
|
id,
|
|
@@ -50336,9 +50547,21 @@ async function runMachineDoctor(options) {
|
|
|
50336
50547
|
const selected = (runtime) => !options.runtimes || options.runtimes.has(runtime);
|
|
50337
50548
|
const present = Object.fromEntries(MACHINE_RUNTIMES.map((runtime) => [runtime, runtimePresent(runtime, paths, env)]));
|
|
50338
50549
|
const skipRuntime = (runtime) => !selected(runtime) ? "Not selected with --runtime." : !present[runtime] ? `${runtime === "claude" ? "Claude Code" : runtime === "codex" ? "Codex" : "omp"} was not found on this machine.` : null;
|
|
50339
|
-
const cli = currentCliLocation(options.cliEntry, env, options.execPath
|
|
50550
|
+
const cli = currentCliLocation(options.cliEntry, env, options.execPath, {
|
|
50551
|
+
configDir: options.configDir,
|
|
50552
|
+
platform: options.platform
|
|
50553
|
+
});
|
|
50340
50554
|
const ephemeral = cli ? ephemeralCliReason(cli.entry) : null;
|
|
50341
|
-
const
|
|
50555
|
+
const ephemeralBlocked = ephemeral ? `This CLI runs from ${ephemeral}, which can be deleted; install it with \`npm install -g primitive\` before repairing hooks.` : null;
|
|
50556
|
+
const hookBlocked = !cli ? "This CLI's hook scripts were not found next to it." : cli.launcher ? null : ephemeralBlocked;
|
|
50557
|
+
const codexBlocked = !cli ? "This CLI's hook scripts were not found next to it." : ephemeralBlocked;
|
|
50558
|
+
const perConnection = !(() => {
|
|
50559
|
+
try {
|
|
50560
|
+
return loadCliCredentials(options.configDir) !== null;
|
|
50561
|
+
} catch {
|
|
50562
|
+
return true;
|
|
50563
|
+
}
|
|
50564
|
+
})() && connectedProfileSaved(options.configDir);
|
|
50342
50565
|
const skillEnv = {
|
|
50343
50566
|
...env,
|
|
50344
50567
|
CLAUDE_CONFIG_DIR: paths.claudeDir,
|
|
@@ -50365,6 +50588,7 @@ async function runMachineDoctor(options) {
|
|
|
50365
50588
|
path: target,
|
|
50366
50589
|
action: "edit_file"
|
|
50367
50590
|
});
|
|
50591
|
+
if (perConnection && (read.state === "absent" || inspectManagedBlock(read.text, body).state === "missing")) return check(id, "skip", PER_CONNECTION_SKIP, { path: target });
|
|
50368
50592
|
if (read.state === "absent") return check(id, "fail", `${target} does not exist yet.`, {
|
|
50369
50593
|
path: target,
|
|
50370
50594
|
fixable: true
|
|
@@ -50409,6 +50633,7 @@ async function runMachineDoctor(options) {
|
|
|
50409
50633
|
} catch {
|
|
50410
50634
|
info = null;
|
|
50411
50635
|
}
|
|
50636
|
+
if (!info && perConnection) return check(id, "skip", PER_CONNECTION_SKIP, { path: target });
|
|
50412
50637
|
if (!info) return check(id, "fail", `Not installed at ${target}.`, {
|
|
50413
50638
|
path: target,
|
|
50414
50639
|
fixable: true
|
|
@@ -50467,12 +50692,20 @@ async function runMachineDoctor(options) {
|
|
|
50467
50692
|
},
|
|
50468
50693
|
restoreProfiles: options.profiles
|
|
50469
50694
|
};
|
|
50695
|
+
const launcherMissing = () => !!cli?.launcher && settingsRead.ok && !existsSync(cli.launcher.path) && JSON.stringify(settingsRead.settings).includes(JSON.stringify(cli.launcher.path));
|
|
50470
50696
|
const hookCheck = (id) => ({
|
|
50471
50697
|
inspect: () => {
|
|
50472
50698
|
const skip = skipRuntime("claude");
|
|
50473
50699
|
if (skip) return check(id, "skip", skip);
|
|
50474
50700
|
if (!settingsRead.ok) return check(id, "fail", "Claude settings.json is invalid; repair it first.", { path: join(paths.claudeDir, "settings.json") });
|
|
50475
|
-
|
|
50701
|
+
if (perConnection && id !== "claude.hook.stop" && !hasMachineSessionHooks(settingsRead.settings)) return check(id, "skip", PER_CONNECTION_SKIP, { path: join(paths.claudeDir, "settings.json") });
|
|
50702
|
+
const inspected = inspectClaudeHooks(settingsRead.settings, hookContext)[id];
|
|
50703
|
+
const finding = inspected.status === "fail" && launcherMissing() ? {
|
|
50704
|
+
...inspected,
|
|
50705
|
+
detail: `${inspected.detail} The hook launcher ${cli?.launcher?.path} is missing; a repair writes it again.`
|
|
50706
|
+
} : inspected;
|
|
50707
|
+
const launcherWarning = id === "claude.hook.stop" && cli?.launcher ? readHookLauncherWarning(options.configDir) : null;
|
|
50708
|
+
if (launcherWarning && finding.status === "ok") return check(id, "warn", `${finding.detail} The last hook run reported: ${launcherWarning}`, { path: join(paths.claudeDir, "settings.json") });
|
|
50476
50709
|
return check(id, finding.status, finding.detail, {
|
|
50477
50710
|
fixable: finding.fixable,
|
|
50478
50711
|
path: join(paths.claudeDir, "settings.json"),
|
|
@@ -50487,9 +50720,17 @@ async function runMachineDoctor(options) {
|
|
|
50487
50720
|
repair: () => {
|
|
50488
50721
|
if (!settingsRead.ok) return false;
|
|
50489
50722
|
const findings = inspectClaudeHooks(settingsRead.settings, hookContext);
|
|
50490
|
-
const
|
|
50723
|
+
const machineHooksSkipped = perConnection && !hasMachineSessionHooks(settingsRead.settings);
|
|
50724
|
+
const ids = HOOK_CHECK_IDS.filter((hook) => (hook === "claude.hook.stop" || !machineHooksSkipped) && (hook === id || !options.only || options.only.has(hook)) && findings[hook].status === "fail" && findings[hook].fixable);
|
|
50491
50725
|
const repaired = repairClaudeHooks(settingsRead.settings, hookContext, new Set(ids), { profiles: options.profiles });
|
|
50492
|
-
|
|
50726
|
+
const restoreLauncher = launcherMissing();
|
|
50727
|
+
if (!repaired.changed.size && !restoreLauncher) return false;
|
|
50728
|
+
if (cli && hookBlocked === null) try {
|
|
50729
|
+
ensureHookLauncher(cli);
|
|
50730
|
+
} catch {
|
|
50731
|
+
throw new MachineFileError(`The hook launcher ${cli.launcher?.path ?? ""} could not be written.`);
|
|
50732
|
+
}
|
|
50733
|
+
if (!repaired.changed.size) return id === "claude.hook.stop";
|
|
50493
50734
|
writeClaudeSettings({
|
|
50494
50735
|
claudeDir: paths.claudeDir,
|
|
50495
50736
|
read: settingsRead.read,
|
|
@@ -50512,17 +50753,21 @@ async function runMachineDoctor(options) {
|
|
|
50512
50753
|
path: read.path,
|
|
50513
50754
|
action: "edit_file"
|
|
50514
50755
|
});
|
|
50756
|
+
if (perConnection && !hasCodexSessionHook(read.settings)) return check(id, "skip", PER_CONNECTION_SKIP, { path: read.path });
|
|
50515
50757
|
const configToml = readManagedFile(join(paths.codexHome, "config.toml"));
|
|
50516
|
-
const finding = inspectCodexHook(read,
|
|
50758
|
+
const finding = inspectCodexHook(read, {
|
|
50759
|
+
cli,
|
|
50760
|
+
blocked: codexBlocked
|
|
50761
|
+
}, configToml.state === "present" ? configToml.text : null);
|
|
50517
50762
|
return check(id, finding.status, finding.detail, {
|
|
50518
50763
|
fixable: finding.fixable,
|
|
50519
50764
|
path: read.path,
|
|
50520
|
-
...finding.status === "fail" && !finding.fixable &&
|
|
50765
|
+
...finding.status === "fail" && !finding.fixable && codexBlocked ? { action: "install_cli" } : {}
|
|
50521
50766
|
});
|
|
50522
50767
|
},
|
|
50523
50768
|
repair: () => {
|
|
50524
50769
|
const read = readCodexHooks(paths.codexHome);
|
|
50525
|
-
if (!read.ok || !cli ||
|
|
50770
|
+
if (!read.ok || !cli || codexBlocked) return false;
|
|
50526
50771
|
const repaired = repairCodexHook(read.settings, cli);
|
|
50527
50772
|
if (!repaired.changed) return false;
|
|
50528
50773
|
writeCodexHooks({
|
|
@@ -50635,10 +50880,18 @@ async function runMachineDoctor(options) {
|
|
|
50635
50880
|
} },
|
|
50636
50881
|
"cli.path_stable": { inspect: () => {
|
|
50637
50882
|
if (!cli) return check("cli.path_stable", "fail", "The running CLI's files could not be located.", { action: "install_cli" });
|
|
50638
|
-
if (ephemeral) return check("cli.path_stable", "fail", `This CLI runs from ${ephemeral}; hooks pointing at it would break. Install it globally with \`npm install -g primitive\`.`, {
|
|
50883
|
+
if (ephemeral && !cli.launcher) return check("cli.path_stable", "fail", `This CLI runs from ${ephemeral}; hooks pointing at it would break. Install it globally with \`npm install -g primitive\`.`, {
|
|
50639
50884
|
action: "install_cli",
|
|
50640
50885
|
path: cli.entry
|
|
50641
50886
|
});
|
|
50887
|
+
if (ephemeral) {
|
|
50888
|
+
const launcherNote = `This CLI runs from ${ephemeral}. Claude Code hooks run through ${cli.launcher?.path}, which uses the \`primitive\` on PATH or fetches this version again with npx if the cache is removed.`;
|
|
50889
|
+
const codexRead = readCodexHooks(paths.codexHome);
|
|
50890
|
+
return selected("codex") && present.codex && !(perConnection && codexRead.ok && !hasCodexSessionHook(codexRead.settings)) ? check("cli.path_stable", "warn", `${launcherNote} The Codex SessionStart hook names the CLI directly; install it globally with \`npm install -g primitive\` so that hook keeps working.`, {
|
|
50891
|
+
action: "install_cli",
|
|
50892
|
+
path: cli.entry
|
|
50893
|
+
}) : check("cli.path_stable", "ok", launcherNote, { path: cli.entry });
|
|
50894
|
+
}
|
|
50642
50895
|
const onPath = findOnPath("primitive", env);
|
|
50643
50896
|
if (onPath && !samePath(onPath, cli.entry)) return check("cli.path_stable", "warn", `\`primitive\` on PATH is ${onPath}, but this run is ${cli.entry}; hooks follow the copy that runs the doctor.`, { path: cli.entry });
|
|
50644
50897
|
return check("cli.path_stable", "ok", `Hooks use ${cli.node} and ${cli.entry}.`, { path: cli.entry });
|
|
@@ -50650,7 +50903,7 @@ async function runMachineDoctor(options) {
|
|
|
50650
50903
|
} catch {
|
|
50651
50904
|
return check("auth.member_login", "fail", "The saved login file is unreadable. Sign in again.", { action: "login" });
|
|
50652
50905
|
}
|
|
50653
|
-
if (!saved) return check("auth.member_login", "fail", "No saved member login on this machine.", { action: "login" });
|
|
50906
|
+
if (!saved) return perConnection ? check("auth.member_login", "skip", "No member login is saved. Agents on this machine were connected with `primitive agent connect` and use their own saved credentials; sign in with `primitive login` only to register every session on this machine automatically.") : check("auth.member_login", "fail", "No saved member login on this machine.", { action: "login" });
|
|
50654
50907
|
let credentials;
|
|
50655
50908
|
try {
|
|
50656
50909
|
credentials = await refreshStoredCliCredentials({
|
|
@@ -50804,7 +51057,7 @@ function renderCheck(check) {
|
|
|
50804
51057
|
}
|
|
50805
51058
|
var MachineDoctorCommand = class MachineDoctorCommand extends Command {
|
|
50806
51059
|
static summary = "Check, and optionally repair, this machine's Primitive setup for coding agents";
|
|
50807
|
-
static description = "Checks everything that lets Claude Code, Codex and omp sessions on this machine register with Primitive and answer mail: the CLI install and version, the saved member login, Claude settings and Primitive's SessionStart, SessionEnd and per-session receive hooks, the managed instruction block in each runtime's global instructions file, the bundled primitive-connect skill, and saved agent profiles disconnected in Primitive. Each check reports ok, warn, fail or skip, whether it can be repaired automatically, and an action (login, install_cli, update_cli, edit_file) when only a person can fix it. --fix repairs what it safely can: it only adds, rewrites or removes Primitive-owned hooks, blocks and skill copies, backs up any existing file beside it as <file>.primitive-bak-<timestamp> before changing it, writes atomically, and changes nothing when nothing drifted, so it is safe to run on a timer. Malformed or hand-edited files it cannot parse are reported and left unchanged. Exits 0 when every check is ok or skipped, 1 otherwise.";
|
|
51060
|
+
static description = "Checks everything that lets Claude Code, Codex and omp sessions on this machine register with Primitive and answer mail: the CLI install and version, the saved member login, Claude settings and Primitive's SessionStart, SessionEnd and per-session receive hooks, the managed instruction block in each runtime's global instructions file, the bundled primitive-connect skill, and saved agent profiles disconnected in Primitive. Each check reports ok, warn, fail or skip, whether it can be repaired automatically, and an action (login, install_cli, update_cli, edit_file) when only a person can fix it. --fix repairs what it safely can: it only adds, rewrites or removes Primitive-owned hooks, blocks and skill copies, backs up any existing file beside it as <file>.primitive-bak-<timestamp> before changing it, writes atomically, and changes nothing when nothing drifted, so it is safe to run on a timer. Malformed or hand-edited files it cannot parse are reported and left unchanged. On a machine whose agents were all connected with `agent connect` and that has no saved member login, machine-wide setup it does not use (the member login, SessionStart and SessionEnd hooks, instruction blocks, skills) is reported as skip, and --fix does not add it. Exits 0 when every check is ok or skipped, 1 otherwise.";
|
|
50808
51061
|
static examples = [
|
|
50809
51062
|
"<%= config.bin %> machine doctor",
|
|
50810
51063
|
"<%= config.bin %> machine doctor --json",
|
|
@@ -54401,7 +54654,8 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
54401
54654
|
} else emailId = flags.id;
|
|
54402
54655
|
const priorRepliesCheck = await checkPriorReplies({
|
|
54403
54656
|
client: apiClient.client,
|
|
54404
|
-
emailId
|
|
54657
|
+
emailId,
|
|
54658
|
+
isLocalSignal: (sentId) => isSentSignal(this.config.configDir, sentId)
|
|
54405
54659
|
});
|
|
54406
54660
|
this.priorRepliesCheck = priorRepliesCheck;
|
|
54407
54661
|
const priorRepliesMessage = priorRepliesCheck.status === "checked" ? formatPriorRepliesWarning(priorRepliesCheck.prior) : formatPriorRepliesCheckSkipped(emailId, priorRepliesCheck.reason);
|
|
@@ -54521,7 +54775,10 @@ function priorRepliesEnvelopeFields(check) {
|
|
|
54521
54775
|
};
|
|
54522
54776
|
return check.status === "checked" ? {
|
|
54523
54777
|
prior_replies: check.prior,
|
|
54524
|
-
prior_replies_check: {
|
|
54778
|
+
prior_replies_check: {
|
|
54779
|
+
status: "checked",
|
|
54780
|
+
signals_excluded: check.signals ?? 0
|
|
54781
|
+
}
|
|
54525
54782
|
} : {
|
|
54526
54783
|
prior_replies: null,
|
|
54527
54784
|
prior_replies_check: {
|
|
@@ -55433,7 +55690,10 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55433
55690
|
]
|
|
55434
55691
|
}),
|
|
55435
55692
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the message for delivery." }),
|
|
55436
|
-
"wait-timeout-ms": Flags.integer({
|
|
55693
|
+
"wait-timeout-ms": Flags.integer({
|
|
55694
|
+
description: `Maximum time to wait for the delivery outcome when --wait is set. Defaults to 30000ms; the server waits at most ${MAX_SEND_WAIT_TIMEOUT_MS}ms, so a longer value is shortened to that with a notice. This waits for delivery, not for a reply: to wait for the recipient's answer, run \`primitive emails wait --reply-to-sent-email-id <sent id> --from <recipient> --timeout <seconds>\` after sending.`,
|
|
55695
|
+
min: 1
|
|
55696
|
+
}),
|
|
55437
55697
|
"idempotency-key": Flags.string({ description: IDEMPOTENCY_KEY_FLAG_DESCRIPTION }),
|
|
55438
55698
|
json: Flags.boolean({ description: "Emit one outcome envelope { outcome, exit_code, outcome_message, sent_email_id, idempotency_key, sent, http_status, error, follow_up_commands } on stdout for every outcome, including failures, with nothing on stderr. Without --json, stdout is the send record as before." }),
|
|
55439
55699
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
@@ -55447,6 +55707,8 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55447
55707
|
attemptStartedAtIso = null;
|
|
55448
55708
|
idempotencyKey = null;
|
|
55449
55709
|
sendRequestStarted = false;
|
|
55710
|
+
/** Set when --wait-timeout-ms was shortened to the server's limit. */
|
|
55711
|
+
waitNotice = null;
|
|
55450
55712
|
async run() {
|
|
55451
55713
|
const { flags } = await this.parse(SendCommand);
|
|
55452
55714
|
try {
|
|
@@ -55457,7 +55719,8 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55457
55719
|
error,
|
|
55458
55720
|
idempotencyKey: this.idempotencyKey,
|
|
55459
55721
|
noun: "Message",
|
|
55460
|
-
requestStarted: this.sendRequestStarted
|
|
55722
|
+
requestStarted: this.sendRequestStarted,
|
|
55723
|
+
...this.waitNotice ? { extraEnvelopeFields: { wait_notice: this.waitNotice } } : {}
|
|
55461
55724
|
}), null, 2));
|
|
55462
55725
|
if (this.sendRequestStarted) {
|
|
55463
55726
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -55512,10 +55775,14 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55512
55775
|
...flags.bcc !== void 0 ? { bcc: flags.bcc } : {},
|
|
55513
55776
|
subject
|
|
55514
55777
|
};
|
|
55778
|
+
const requestedWait = flags["wait-timeout-ms"];
|
|
55779
|
+
const waitTimeoutMs = requestedWait === void 0 ? void 0 : Math.min(requestedWait, MAX_SEND_WAIT_TIMEOUT_MS);
|
|
55780
|
+
if (flags.wait && requestedWait !== void 0 && requestedWait > 3e4) this.waitNotice = sendWaitClampNotice(requestedWait);
|
|
55781
|
+
if (this.waitNotice && !flags.json) process.stderr.write(`${this.waitNotice}\n`);
|
|
55515
55782
|
const threading = {
|
|
55516
55783
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
55517
55784
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
55518
|
-
...
|
|
55785
|
+
...waitTimeoutMs !== void 0 ? { wait_timeout_ms: waitTimeoutMs } : {},
|
|
55519
55786
|
...repeat !== void 0 ? { repeat } : {}
|
|
55520
55787
|
};
|
|
55521
55788
|
const idempotencyKey = this.idempotencyKey ?? deriveSendIdempotencyKey("send", flags.fyi ? {
|
|
@@ -55562,6 +55829,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55562
55829
|
});
|
|
55563
55830
|
const outcome = reportSendCommandResult({
|
|
55564
55831
|
attemptStartedAtIso,
|
|
55832
|
+
...this.waitNotice ? { extraEnvelopeFields: { wait_notice: this.waitNotice } } : {},
|
|
55565
55833
|
idempotencyKey,
|
|
55566
55834
|
json: flags.json,
|
|
55567
55835
|
log: (line) => this.log(line),
|
|
@@ -57434,6 +57702,7 @@ const COMMANDS = {
|
|
|
57434
57702
|
completion: CompletionCommand,
|
|
57435
57703
|
"agent:upgrade": AgentUpgradeCommand,
|
|
57436
57704
|
"agent:connect": AgentConnectCommand,
|
|
57705
|
+
"agent:status": AgentStatusCommand,
|
|
57437
57706
|
"agent:check-mail": AgentCheckMailCommand,
|
|
57438
57707
|
"agent:disconnect": AgentDisconnectCommand,
|
|
57439
57708
|
"agent:enroll": AgentEnrollCommand,
|