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
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { a as REPEAT_IDLE_MAX_MINUTES, b as
|
|
2
|
-
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfileName, G as PrimitiveApiClient, I as agentProfilesDirectory, M as AGENT_PROFILE_ENV, N as AgentConnectionSetupError, O as loadActiveChatState, P as agentProfileDirectory, T as saveSignupCredentials, U as requireDefaultLoginProfile, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_ADDRESS_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, z as loadConnectedAgentProfile } from "../cli-config
|
|
3
|
-
import {
|
|
4
|
-
import { S as
|
|
5
|
-
import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-
|
|
6
|
-
import { $ as
|
|
7
|
-
import { a as
|
|
8
|
-
import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background
|
|
9
|
-
import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-
|
|
1
|
+
import { $ as AutomatedFilterUnsupportedError, $t as listDomains, A as sleep$3, An as setMemory, At as getAgentContactPolicy, B as hasReplyState, Bn as updateRepeatingSend, Bt as getMemory, C as collectNewAcceptedEmails, Ct as deleteWakeSchedule, D as filtersFromFlags, Dn as sendEmail, Dt as downloadSentAttachmentArchiveMember, E as fetchEmailSearchPage, En as semanticSearch, F as assertAwaitingFilterRows, Fn as stopRepeatFromEmail, Ft as getEmail, Gn as verifyAgentSignup, Gt as getWakeSchedule, H as queryUsesAwaiting, Hn as updateWakeAuthorization, Ht as getRepeatingSend, I as awaitingRejectedError, In as testFunction, It as getFunction, J as parseWebhookEvent, Jt as listAgentContacts, K as isEmailReceivedEvent, L as ensureSearchReportsReplyState, Ln as unsetFunctionRoute, Lt as getFunctionRouting, M as AWAITING_VALUES, Mn as startAgentClaim, Mt as getConversation, N as REPLY_STATE_UNSUPPORTED_CODE, Nn as startAgentSignup, Nt as getCreditBalance, O as quoteDslValue, On as setFunctionRoute, Ot as downloadSentAttachmentByFilename, P as ReplyStateUnsupportedError, Pn as startCliLogin, Pt as getDefaultNetworkAgent, Q as AUTOMATED_VALUES, Qt as listDefaultNetworkMembers, R as formatAwaitingCell, Rn as updateDefaultNetworkMember, Rt as getFunctionTestRunTrace, S as transientFailure, Sn as runWakeSchedule, St as deleteWakeAuthorization, T as encodeReceivedAtSearchCursor, Tn as searchMemories, Tt as downloadEmailAttachmentByFilename, Un as updateWakeSchedule, Ut as getSentEmail, V as isAwaitingRejectedError, Vn as updateRoute, Vt as getOrgRoutingTopology, Wn as verifyAgentClaim, Wt as getThread, X as AUTOMATED_FILTER_UNSUPPORTED_CODE, Y as parseFromHeader, Yt as listAgentNetworks, Z as AUTOMATED_FLAG_DESCRIPTION, Zt as listDefaultNetworkAgents, _ as describeApiFailure, _n as redeemCreditCode, _t as createWakeSchedule, a as REPEAT_IDLE_MAX_MINUTES, an as listRepeatingSends, at as hasAutomatedVerdict, b as retryTransient, bn as replyToEmail, bt as deleteMemory, cn as listSentEmails, ct as cancelSentEmail, d as parseRepeatTick, dn as listWakeSchedules, dt as cliLogout, en as listEmailAttachmentArchiveMembers, et as assertAutomatedVerdict, f as preparePresenceAliveEmail, fn as pollCliLogin, ft as completeWebhookEvent, g as WaitDeadlineReached, gt as createWakeAuthorization, ht as createRoute, i as REPEAT_EVERY_MAX_MINUTES, in as listFunctions, j as AWAITING_FLAG_DESCRIPTION, jn as simulateRoute, k as sinceFromFlags, kn as setFunctionSecret, kt as getAccount, l as parseInteractionEnvelope$1, ln as listWakeAuthorizations, lt as cancelSentEmailByIdempotencyKey, m as repeatStopCommand, mt as createFunction, nn as listEndpoints, o as REPEAT_MAX_SENDS_MAX, on as listRoutes, ot as isAutomatedRejectedError, p as prepareSignalEmail, pn as pullWebhookEvent, pt as createEndpoint, rn as listFunctionLogs, s as buildRepeatStopBody, sn as listSentAttachmentArchiveMembers, st as addDefaultNetworkMember, tn as listEmails, tt as automatedRejectedError, u as parsePresenceEnvelope, un as listWakeDispatches, ut as checkDefaultNetworkContactAdmission, vn as removeDefaultNetworkMember, w as cursorFromAcceptedRows, wn as searchEmails, wt as downloadEmailAttachmentArchiveMember, x as retryingOperation, xn as resendAgentSignupVerification, xt as deleteRoute, y as pauseForTransientFailure, yn as reorderRoutes, z as formatRepliesCell, zn as updateFunction, zt as getInboxStatus } from "../repeats-BL7ZC-_6-B_ESsWTH.js";
|
|
2
|
+
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfileName, G as PrimitiveApiClient, I as agentProfilesDirectory, M as AGENT_PROFILE_ENV, N as AgentConnectionSetupError, O as loadActiveChatState, P as agentProfileDirectory, T as saveSignupCredentials, U as requireDefaultLoginProfile, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_ADDRESS_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, z as loadConnectedAgentProfile } from "../cli-config-C0Y_Ppol.js";
|
|
3
|
+
import { C as resolveListenSubscription, S as normalizeListenOrigin, _ 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, y as listenIdentity } from "../shared-mail-files-CZfm7E2k.js";
|
|
4
|
+
import { $ as formatDeletedEarlierSendNotice, A as clearReadPendingMail, At as createCliApiClient, C as serverRelationship, Ct as withFlagSuggestion, D as wakeRelationship, Dt as ConnectedAgentKeyRequiredError, E as wakeRecipientField, Et as withAttachmentWait, F as interactionHumanLine, G as assertValidIdempotencyKey, H as MAX_SEND_WAIT_TIMEOUT_MS, I as interactionNextActions, J as checkPriorReplies, K as buildFollowUpCommand, L as readEmailInteraction, M as readPendingMail, Mt as resolveCliApiRequestConfig, N as recordPendingMail, O as claimPendingMail, P as removePendingMail, Q as followUpCommandPrefix, R as replyExpectation, S as serverMuted, St as writeIdempotentReplayBannerIfReplay, T as wakeReadCommand, Tt as ATTACHMENT_WAIT_MAX_MS, U as SEND_OUTCOME_HELP, V as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, W as apiResultHttpStatus, X as deriveSendIdempotencyKey, Y as classifySendError, Z as excludeSignalReplies, _ as displayAddress, _t as generatedAgentName, a as extractErrorCode, at as reportSendCommandResult, b as readThreadContext, bt as currentMailSessionKey, c as hasControlCharacter$1, ct as sendWaitClampNotice, d as runWithTiming, dt as successfulSendOutcome, et as formatPriorRepliesCheckSkipped, f as surfaceUnauthorizedHint, ft as thrownErrorExitCode, g as describeWake, h as bareAddress, ht as connectNextSteps, i as createOperationCommand, it as priorRepliesThatWentOut, j as pendingMailPath, jt as refreshStoredCliCredentials, k as clearConsumedPendingMail, kt as createAuthenticatedCliApiClient, l as headerFlagName, lt as sentHistoryWindowStart, m as explainPendingMailNotFound, mt as cliInvocation, n as API_ERROR_CODES, nt as formatSendFailureSummary, o as extractErrorPayload, ot as sendIdentityFields, p as writeErrorWithHints, q as buildThrownSendFailureEnvelope, r as TIME_FLAG_DESCRIPTION, rt as formatSendRecordFailureSummary, s as formatErrorPayload, st as sendOutcomeExitCode, t as API_BASE_URL_FLAG_DESCRIPTION, tt as formatPriorRepliesWarning, u as readTextFileFlag, ut as serializeErrorPayload, v as formatMailWakeLine, vt as identitySuggestions, wt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, x as sentInThread, xt as formatAlreadySentNotice, y as latestOwnOutbound, yt as loadSkillLine, z as replyInteractionWarning } from "../api-command-fKP3NYa7.js";
|
|
5
|
+
import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-DJAb9SGm.js";
|
|
6
|
+
import { $ as saveConnectionName, A as ContactPolicyReadRetryError, B as AddressNotesApiError, C as RECEIVER_MODES, Ct as upsertManagedBlock, D as registrationSummary, Dt as refreshOwnerMemberAddressPeriodically, E as failedSessionRegistration, Et as addSessionReceiver, F as canonicalContactSelector, G as contactAgentAddress, H as runAddressNotesRequest, I as AgentDisconnectError, J as agentInvitationHash, K as runContactRequest, L as claimOverRevokedSetup, M as NetworkAdmissionPendingError, N as createNotificationContactPolicy, O as registrationSummaryLine, Ot as withOwnerMemberAddress, P as evaluateContactPolicy, Q as refusalAfterReplacement, R as clearRevokedAddresses, S as runAgentConnect, St as readManagedFile, T as verificationReplySubmitted, Tt as readHookLauncherWarning, U as ContactsApiError, V as addressNoteTarget, W as contactAddress, X as readAgentInvitation, Y as connectAgent, Z as receiverStatusCommand, _ as enrollmentResumes, _t as samePath, a as endSession, at as boundSessionProfiles, b as invitationProfileName, bt as inspectManagedBlock, c as pendingSessionDisconnects, ct as describeSessionHookChange, d as registerSession, dt as ephemeralCliReason, et as savedReceiver, f as retryPendingDisconnects, ft as findOnPath, g as enrollAgent, gt as repairClaudeHooks, h as staleSessionAgents, ht as readClaudeSettings, i as connectedProfilesForSession, it as recordHookMailCheck, j as NETWORK_ADMISSION_PENDING_RETRY_MS, k as waitForRegistrations, l as psProcessInfo, lt as describeSessionHookItem, m as runDetached, mt as inspectClaudeHooks, n as OMP_MAIN, nt as releaseRegistrationsForProfile, o as headlessClaudeRun, ot as claudeConfigDir, p as revokeStaleSessionAgents, pt as hasMachineSessionHooks, q as agentConnectionStatus, r as agentConnectionState, rt as installClaudeWakeHook, st as currentCliLocation, t as HOOK_END_GRACE_MS, tt as MACHINE_RUNTIMES, u as readClaudeHookInput, ut as ensureHookLauncher, v as connectWarnings, vt as writeClaudeSettings, w as externalReceivingState, wt as writeManagedFile, x as pollCheckCommand, xt as jsonIndentation, y as defaultAgentProfileName, yt as MachineFileError, z as disconnectAgent } from "../machine-session-B6V5-grn.js";
|
|
7
|
+
import { a as readBeforeDeadline, c as readPresenceProjection, i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust, r as isScopedChatReply, s as presenceDisposition, t as isConnectedChatCredential } from "../scoped-chat-DAiOb622.js";
|
|
8
|
+
import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-cD6n8VYO.js";
|
|
9
|
+
import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-Cte4mAL3.js";
|
|
10
10
|
import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
|
|
11
11
|
import { n as jsonOutputRequested, r as runStreamingJson, t as guardJsonOutputCommands } from "../json-output-Ce10AsSb.js";
|
|
12
12
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
@@ -40006,7 +40006,7 @@ const RETRYABLE_STATUS = /* @__PURE__ */ new Set([
|
|
|
40006
40006
|
503,
|
|
40007
40007
|
504
|
|
40008
40008
|
]);
|
|
40009
|
-
function sleep$
|
|
40009
|
+
function sleep$2(ms) {
|
|
40010
40010
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
40011
40011
|
}
|
|
40012
40012
|
/**
|
|
@@ -40028,7 +40028,7 @@ async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES)
|
|
|
40028
40028
|
throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
|
|
40029
40029
|
}
|
|
40030
40030
|
if (res) await res.text().catch(() => {});
|
|
40031
|
-
await sleep$
|
|
40031
|
+
await sleep$2(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
|
|
40032
40032
|
}
|
|
40033
40033
|
}
|
|
40034
40034
|
async function initiate(opts, manifest) {
|
|
@@ -40371,7 +40371,7 @@ function withClearedNote(message, cleared) {
|
|
|
40371
40371
|
return `${message} Before that, this run cleared ${cleared.map((row) => `${row.address} (profile ${row.profile})`).join(", ")} on this machine because Primitive confirmed ${cleared.length === 1 ? "its credential was" : "their credentials were"} already revoked; ${cleared.length === 1 ? "it stays" : "they stay"} disconnected.`;
|
|
40372
40372
|
}
|
|
40373
40373
|
var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
40374
|
-
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, writes a private AGENT_RESUME note holding the command that resumes the bound session ("claude --resume <session>" or "codex resume <session>") once verified (or deletes an existing one when no resumable session is bound), 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: an address whose credential Primitive confirms was already revoked (for example, revoked in the app before the owner reconnected it) no longer counts; it is disconnected locally, reported in clearedRevoked, and its saved setup is moved aside so the new invitation can be claimed into the same profile. Otherwise, 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, with PRIMITIVE_AGENT_PROFILE set, or with --session (the address connected for that session) to inspect saved identity and local receiver health offline; it exits 1 when nothing is configured. A status check before connecting is optional: connect itself claims nothing and exits 3 when the session already has an address. 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.`;
|
|
40374
|
+
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, writes a private AGENT_RESUME note holding the command that resumes the bound session ("claude --resume <session>" or "codex resume <session>") once verified (or deletes an existing one when no resumable session is bound), 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: an address whose credential Primitive confirms was already revoked (for example, revoked in the app before the owner reconnected it) no longer counts; it is disconnected locally, reported in clearedRevoked, and its saved setup is moved aside so the new invitation can be claimed into the same profile. A profile whose earlier setup or enrollment never saved a credential is not an address: a pasted invitation is claimed into it (its unfinished state is moved aside), and the result's replacedUnfinished says whether that finished the same pending address. Otherwise, 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, with PRIMITIVE_AGENT_PROFILE set, or with --session (the address connected for that session) to inspect saved identity and local receiver health offline; it exits 1 when nothing is configured, and reports the session's last registration failure as registration when one was recorded. A status check before connecting is optional: connect itself claims nothing and exits 3 when the session already has an address. 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.`;
|
|
40375
40375
|
static summary = "Connect and verify an agent address";
|
|
40376
40376
|
static examples = [
|
|
40377
40377
|
"<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
|
|
@@ -40444,12 +40444,14 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
40444
40444
|
});
|
|
40445
40445
|
if (picked.profile === null && picked.session === null) throw new AgentConnectionSetupError(`Pass --profile <name>, --session <id> or set ${AGENT_PROFILE_ENV}.`);
|
|
40446
40446
|
if (picked.profile === null) {
|
|
40447
|
-
const
|
|
40447
|
+
const registration = picked.bound.length === 0 && picked.session ? registrationSummary(this.config.configDir, defaultAgentProfileName(picked.session)) : null;
|
|
40448
|
+
const detail = registration ? `${statusProfileMissingDetail(picked.bound)} ${registrationSummaryLine(registration)}` : statusProfileMissingDetail(picked.bound);
|
|
40448
40449
|
this.log(flags.json ? JSON.stringify({
|
|
40449
40450
|
status: "not_configured",
|
|
40450
40451
|
profileName: null,
|
|
40451
40452
|
session: picked.session,
|
|
40452
40453
|
...picked.bound.length > 1 ? { profiles: picked.bound } : {},
|
|
40454
|
+
...registration ? { registration } : {},
|
|
40453
40455
|
detail
|
|
40454
40456
|
}) : detail);
|
|
40455
40457
|
process.exitCode = 1;
|
|
@@ -40457,20 +40459,25 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
40457
40459
|
}
|
|
40458
40460
|
const profileName = picked.profile;
|
|
40459
40461
|
const result = agentConnectionStatus(this.config.configDir, profileName);
|
|
40462
|
+
const registration = registrationSummary(this.config.configDir, profileName);
|
|
40460
40463
|
if (result.status !== "configured") {
|
|
40461
|
-
const detail = `Agent profile ${profileName} is not configured. Run \`primitive agent connect\` with the owner's invitation to connect it.`;
|
|
40464
|
+
const detail = registration ? `Agent profile ${profileName} is not configured. ${registrationSummaryLine(registration)}` : `Agent profile ${profileName} is not configured. Run \`primitive agent connect\` with the owner's invitation to connect it.`;
|
|
40462
40465
|
this.log(flags.json ? JSON.stringify({
|
|
40463
40466
|
...result,
|
|
40467
|
+
...registration ? { registration } : {},
|
|
40464
40468
|
detail
|
|
40465
40469
|
}) : detail);
|
|
40466
40470
|
process.exitCode = 1;
|
|
40467
40471
|
return;
|
|
40468
40472
|
}
|
|
40469
|
-
if (flags.json) this.log(JSON.stringify(
|
|
40473
|
+
if (flags.json) this.log(JSON.stringify(registration?.lastFailure ? {
|
|
40474
|
+
...result,
|
|
40475
|
+
registration
|
|
40476
|
+
} : result));
|
|
40470
40477
|
else {
|
|
40471
40478
|
const receiving = result.receiving;
|
|
40472
40479
|
const why = [receiving.reason, receiving.failureCode !== receiving.reason ? receiving.failureCode : null].filter(Boolean);
|
|
40473
|
-
this.log(`Agent profile ${profileName} is configured for ${result.identity.agentAddress}. Local receiving: ${receiving.state}${why.length ? ` (${why.join(", ")})` : ""}.${receiving.detail ? ` ${receiving.detail}` : ""}${receiving.lastFiredAt ? ` Receive hook last ran at ${receiving.lastFiredAt}.` : ""}${receiving.lastSuccessfulMailCheckAt ? ` Last completed mail check at ${receiving.lastSuccessfulMailCheckAt}.` : ""}`);
|
|
40480
|
+
this.log(`Agent profile ${profileName} is configured for ${result.identity.agentAddress}. Local receiving: ${receiving.state}${why.length ? ` (${why.join(", ")})` : ""}.${receiving.detail ? ` ${receiving.detail}` : ""}${receiving.lastFiredAt ? ` Receive hook last ran at ${receiving.lastFiredAt}.` : ""}${receiving.lastSuccessfulMailCheckAt ? ` Last completed mail check at ${receiving.lastSuccessfulMailCheckAt}.` : ""}${registration?.lastFailure ? ` ${registrationSummaryLine(registration)}` : ""}`);
|
|
40474
40481
|
}
|
|
40475
40482
|
return;
|
|
40476
40483
|
}
|
|
@@ -40855,7 +40862,7 @@ function invocation(entry) {
|
|
|
40855
40862
|
}
|
|
40856
40863
|
var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
40857
40864
|
static summary = "Give this coding session an address in your organization";
|
|
40858
|
-
static description = `On this trusted machine, use the saved member OAuth login to create one address for the exact loaded session, claim its one-use invitation privately, answer the email challenge, and poll the owner's connection list for confirmed pairing. Native receiving starts when supported. With --receiver external in the exact Claude session, install a fail-open Stop hook and resume SessionStart hook in that runtime's settings after the verification reply; idle wake remains unverified until tested with real mail. With --contact-requests, the login conditionally enables first-contact intake for this exact address after verification, preserving existing policy rules. An explicit disable or uncertain policy update is reported separately without losing pairing or receiver setup. The server allocates a readable address. An uncertain creation resumes the same saved request; recovered results do not include another invitation. Use --continue-setup once to explicitly continue a recovered pending connection. An uncertain continuation
|
|
40865
|
+
static description = `On this trusted machine, use the saved member OAuth login to create one address for the exact loaded session, claim its one-use invitation privately, answer the email challenge, and poll the owner's connection list for confirmed pairing. Native receiving starts when supported. With --receiver external in the exact Claude session, install a fail-open Stop hook and resume SessionStart hook in that runtime's settings after the verification reply; idle wake remains unverified until tested with real mail. With --contact-requests, the login conditionally enables first-contact intake for this exact address after verification, preserving existing policy rules. An explicit disable or uncertain policy update is reported separately without losing pairing or receiver setup. The server allocates a readable address. An uncertain creation resumes the same saved request; recovered results do not include another invitation. Use --continue-setup once to explicitly continue a recovered pending connection. An uncertain continuation is held for owner recovery, never repeated automatically. When a claim left no saved credential (it was never sent, was refused, or its answer was lost), a rerun signed in as the agent's owner in the same organization asks Primitive for one fresh invitation for the same address and claims it; without that login the refusal names the step that finishes it from the app. Each attempt is recorded, without secrets, in the profile's registration log, which agent connect --status reports. Requires a verified Primitive-managed domain. This command does not accept API keys, a connection profile, or an invitation argument. One address per session: if this session already has another connected Primitive address, nothing is created and the command exits 3 with status already_connected; 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.`;
|
|
40859
40866
|
static examples = ["<%= config.bin %> agent enroll --session 11111111-1111-4111-8111-111111111111 --name Research --contact-requests --json", "<%= config.bin %> agent enroll --session 11111111-1111-4111-8111-111111111111 --receiver external --name Research --json"];
|
|
40860
40867
|
static flags = {
|
|
40861
40868
|
session: Flags.string({
|
|
@@ -41002,7 +41009,7 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
41002
41009
|
}
|
|
41003
41010
|
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;
|
|
41004
41011
|
} catch (error) {
|
|
41005
|
-
throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : "Enrollment
|
|
41012
|
+
throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : "Enrollment stopped on an unexpected error. Rerun the same command to resume this session's saved enrollment; it never requests a second address.", { exit: 1 });
|
|
41006
41013
|
}
|
|
41007
41014
|
}
|
|
41008
41015
|
};
|
|
@@ -42601,7 +42608,7 @@ function searchFailure(response) {
|
|
|
42601
42608
|
}
|
|
42602
42609
|
/** Inspect an exact email ID obtained from a targeted query or event journal. */
|
|
42603
42610
|
async function inspectTargetedReply(params) {
|
|
42604
|
-
const result = await
|
|
42611
|
+
const result = await readApiBeforeDeadline(params.deadline, (signal) => getEmail({
|
|
42605
42612
|
client: params.apiClient.client,
|
|
42606
42613
|
path: { id: params.id },
|
|
42607
42614
|
signal,
|
|
@@ -42609,7 +42616,8 @@ async function inspectTargetedReply(params) {
|
|
|
42609
42616
|
}));
|
|
42610
42617
|
if (result === null) return null;
|
|
42611
42618
|
const email = result.data?.data;
|
|
42612
|
-
if (result.error
|
|
42619
|
+
if (result.error) throw invalid$2(`Could not inspect reply candidate ${params.id}: ${describeApiFailure(result)}.`);
|
|
42620
|
+
if (!email || email.id !== params.id) throw invalid$2(`Could not inspect reply candidate ${params.id}.`);
|
|
42613
42621
|
const presence = presenceDisposition(email);
|
|
42614
42622
|
if (presence !== "ordinary") return {
|
|
42615
42623
|
kind: presence === "pending" ? "pending" : "unrelated",
|
|
@@ -42685,7 +42693,7 @@ async function inspectTargetedReply(params) {
|
|
|
42685
42693
|
/** One targeted recovery page. The event owner decides when recovery is needed. */
|
|
42686
42694
|
async function readTargetedReplyPage(params) {
|
|
42687
42695
|
if (!params.sentId || !params.from || !params.recipient) throw invalid$2("Targeted reply recovery requires the exact sent email, receiving address, and peer.");
|
|
42688
|
-
const result = await
|
|
42696
|
+
const result = await readApiBeforeDeadline(params.deadline, (signal) => searchEmails({
|
|
42689
42697
|
client: params.apiClient.client,
|
|
42690
42698
|
signal,
|
|
42691
42699
|
query: {
|
|
@@ -42715,6 +42723,11 @@ async function readTargetedReplyPage(params) {
|
|
|
42715
42723
|
}
|
|
42716
42724
|
//#endregion
|
|
42717
42725
|
//#region src/oclif/connected-reply-wait.ts
|
|
42726
|
+
/**
|
|
42727
|
+
* Slack for clock differences between this machine, which records when a
|
|
42728
|
+
* wait was created, and the server, which records when mail was received.
|
|
42729
|
+
*/
|
|
42730
|
+
const REPLY_CLOCK_SKEW_MS = 6e5;
|
|
42718
42731
|
function recoveryEventId(id) {
|
|
42719
42732
|
const h = createHash("sha256").update(`reply-recovery:${id}`).digest("hex");
|
|
42720
42733
|
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
|
|
@@ -42802,6 +42815,20 @@ async function openConnectedReplyWait(options) {
|
|
|
42802
42815
|
let closed = false;
|
|
42803
42816
|
let closing;
|
|
42804
42817
|
const timedOut = () => options.deadline != null && Date.now() >= options.deadline;
|
|
42818
|
+
let earliestReply;
|
|
42819
|
+
/**
|
|
42820
|
+
* The earliest receipt time a reply to this wait's send can have: the wait
|
|
42821
|
+
* is registered before the send, so mail received well before it is
|
|
42822
|
+
* retained history, not a reply.
|
|
42823
|
+
*/
|
|
42824
|
+
async function replyFloor() {
|
|
42825
|
+
if (earliestReply !== void 0) return earliestReply;
|
|
42826
|
+
const intent = await store.readWait(requestId);
|
|
42827
|
+
const created = Date.parse(intent?.createdAt ?? "");
|
|
42828
|
+
const earlier = [options.sentAt, options.since].map((value) => Date.parse(value ?? "")).filter(Number.isFinite);
|
|
42829
|
+
earliestReply = Number.isFinite(created) ? Math.min(created, ...earlier) - REPLY_CLOCK_SKEW_MS : Number.NEGATIVE_INFINITY;
|
|
42830
|
+
return earliestReply;
|
|
42831
|
+
}
|
|
42805
42832
|
async function release() {
|
|
42806
42833
|
if (released) return;
|
|
42807
42834
|
await (await openSharedMailStore({
|
|
@@ -42892,6 +42919,7 @@ async function openConnectedReplyWait(options) {
|
|
|
42892
42919
|
requestId = bound.requestId;
|
|
42893
42920
|
sentId = bound.sentEmailId ?? void 0;
|
|
42894
42921
|
recoveryNeeded = true;
|
|
42922
|
+
earliestReply = void 0;
|
|
42895
42923
|
},
|
|
42896
42924
|
uncertain: () => store.markWaitUncertain(requestId),
|
|
42897
42925
|
async cancelBeforeSend() {
|
|
@@ -42981,7 +43009,9 @@ async function openConnectedReplyWait(options) {
|
|
|
42981
43009
|
limit: options.pageSize,
|
|
42982
43010
|
cursor: localCursor
|
|
42983
43011
|
});
|
|
43012
|
+
const floor = await replyFloor();
|
|
42984
43013
|
for (const row of local.emails) {
|
|
43014
|
+
if (!row.details && Date.parse(row.receivedAt) < floor) continue;
|
|
42985
43015
|
if (row.details && row.details.authorization !== "pending" && (row.details.peer !== options.recipient || row.details.replyToSentEmailId !== sentId)) continue;
|
|
42986
43016
|
const reply = await inspect(row.emailId);
|
|
42987
43017
|
if (reply) return reply;
|
|
@@ -43658,132 +43688,6 @@ function warnIfSharedProfile(params) {
|
|
|
43658
43688
|
const warning = sharedProfileWarning(params);
|
|
43659
43689
|
if (warning) process.stderr.write(`Warning: ${warning}\n`);
|
|
43660
43690
|
}
|
|
43661
|
-
//#endregion
|
|
43662
|
-
//#region src/oclif/commands/emails-poll.ts
|
|
43663
|
-
function quoteDslValue(value) {
|
|
43664
|
-
if (/^[^\s"]+$/.test(value)) return value;
|
|
43665
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
43666
|
-
}
|
|
43667
|
-
function combineQ(q, domain) {
|
|
43668
|
-
const parts = [q?.trim(), domain ? `domain:${quoteDslValue(domain.trim())}` : void 0].filter((part) => Boolean(part));
|
|
43669
|
-
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
43670
|
-
}
|
|
43671
|
-
function normalizeIsoDate(value, label) {
|
|
43672
|
-
const parsed = new Date(value);
|
|
43673
|
-
if (Number.isNaN(parsed.getTime())) throw new Error(`${label} must be a valid date or ISO-8601 timestamp.`);
|
|
43674
|
-
return parsed.toISOString();
|
|
43675
|
-
}
|
|
43676
|
-
function filtersFromFlags(flags) {
|
|
43677
|
-
if (flags.automated !== void 0 && !AUTOMATED_VALUES.includes(flags.automated)) throw new Error("--automated must be true or false.");
|
|
43678
|
-
return {
|
|
43679
|
-
automated: flags.automated === void 0 ? void 0 : flags.automated === "true",
|
|
43680
|
-
awaiting: flags.awaiting,
|
|
43681
|
-
body: flags.body,
|
|
43682
|
-
domain: flags.domain,
|
|
43683
|
-
domainId: flags["domain-id"],
|
|
43684
|
-
from: flags.from,
|
|
43685
|
-
hasAttachment: flags["has-attachment"],
|
|
43686
|
-
q: flags.q,
|
|
43687
|
-
replyToSentEmailId: flags["reply-to-sent-email-id"],
|
|
43688
|
-
spamScoreGte: flags["spam-score-gte"],
|
|
43689
|
-
spamScoreLt: flags["spam-score-lt"],
|
|
43690
|
-
subject: flags.subject,
|
|
43691
|
-
to: flags.to
|
|
43692
|
-
};
|
|
43693
|
-
}
|
|
43694
|
-
function sinceFromFlags(flags) {
|
|
43695
|
-
if (flags.since) return normalizeIsoDate(flags.since, "--since");
|
|
43696
|
-
return flags["include-existing"] ? void 0 : (/* @__PURE__ */ new Date()).toISOString();
|
|
43697
|
-
}
|
|
43698
|
-
function buildEmailSearchQuery(params) {
|
|
43699
|
-
const query = {
|
|
43700
|
-
include_facets: "false",
|
|
43701
|
-
limit: params.pageSize,
|
|
43702
|
-
snippet: "false",
|
|
43703
|
-
sort: "received_at_asc"
|
|
43704
|
-
};
|
|
43705
|
-
const q = combineQ(params.filters.q, params.filters.domain);
|
|
43706
|
-
if (q) query.q = q;
|
|
43707
|
-
if (params.filters.awaiting) query.awaiting = params.filters.awaiting;
|
|
43708
|
-
if (params.filters.automated !== void 0) query.automated = params.filters.automated ? "true" : "false";
|
|
43709
|
-
if (params.filters.body) query.body = params.filters.body;
|
|
43710
|
-
if (params.filters.domainId) query.domain_id = params.filters.domainId;
|
|
43711
|
-
if (params.filters.from) query.from = params.filters.from;
|
|
43712
|
-
if (params.filters.hasAttachment !== void 0) query.has_attachment = params.filters.hasAttachment ? "true" : "false";
|
|
43713
|
-
if (params.filters.spamScoreGte !== void 0) query.spam_score_gte = params.filters.spamScoreGte;
|
|
43714
|
-
if (params.filters.spamScoreLt !== void 0) query.spam_score_lt = params.filters.spamScoreLt;
|
|
43715
|
-
if (params.filters.replyToSentEmailId) query.reply_to_sent_email_id = params.filters.replyToSentEmailId;
|
|
43716
|
-
if (params.filters.subject) query.subject = params.filters.subject;
|
|
43717
|
-
if (params.filters.to) query.to = params.filters.to;
|
|
43718
|
-
if (params.since) query.date_from = params.since;
|
|
43719
|
-
if (params.cursor) query.cursor = params.cursor;
|
|
43720
|
-
return query;
|
|
43721
|
-
}
|
|
43722
|
-
function encodeReceivedAtSearchCursor(email) {
|
|
43723
|
-
const raw = `r|${new Date(email.received_at).toISOString()}|${email.id}`;
|
|
43724
|
-
return Buffer.from(raw, "utf8").toString("base64url");
|
|
43725
|
-
}
|
|
43726
|
-
function cursorFromRows(rows) {
|
|
43727
|
-
const last = rows.at(-1);
|
|
43728
|
-
return last ? encodeReceivedAtSearchCursor(last) : null;
|
|
43729
|
-
}
|
|
43730
|
-
function cursorFromAcceptedRows(rows) {
|
|
43731
|
-
for (let i = rows.length - 1; i >= 0; i--) {
|
|
43732
|
-
const row = rows[i];
|
|
43733
|
-
if (row.status === "accepted" || row.status === "completed") return encodeReceivedAtSearchCursor(row);
|
|
43734
|
-
}
|
|
43735
|
-
return null;
|
|
43736
|
-
}
|
|
43737
|
-
function collectNewAcceptedEmails(rows, seenIds) {
|
|
43738
|
-
const fresh = [];
|
|
43739
|
-
for (const row of rows) {
|
|
43740
|
-
if (row.status !== "accepted" && row.status !== "completed") continue;
|
|
43741
|
-
if (seenIds.has(row.id)) continue;
|
|
43742
|
-
seenIds.add(row.id);
|
|
43743
|
-
fresh.push(row);
|
|
43744
|
-
}
|
|
43745
|
-
return fresh;
|
|
43746
|
-
}
|
|
43747
|
-
async function fetchEmailSearchPage(params) {
|
|
43748
|
-
const result = await searchEmails({
|
|
43749
|
-
client: params.apiClient.client,
|
|
43750
|
-
query: buildEmailSearchQuery({
|
|
43751
|
-
cursor: params.cursor,
|
|
43752
|
-
filters: params.filters,
|
|
43753
|
-
pageSize: params.pageSize,
|
|
43754
|
-
since: params.since
|
|
43755
|
-
}),
|
|
43756
|
-
responseStyle: "fields"
|
|
43757
|
-
});
|
|
43758
|
-
const wantsReplyState = Boolean(params.filters.awaiting) || queryUsesAwaiting(params.filters.q);
|
|
43759
|
-
const wantsAutomated = params.filters.automated !== void 0 || queryUsesAutomated(params.filters.q);
|
|
43760
|
-
if (result.error) {
|
|
43761
|
-
if (wantsReplyState && isAwaitingRejectedError(result.error)) throw awaitingRejectedError("GET /emails/search");
|
|
43762
|
-
if (wantsAutomated && isAutomatedRejectedError(result.error)) throw automatedRejectedError("GET /emails/search");
|
|
43763
|
-
return {
|
|
43764
|
-
ok: false,
|
|
43765
|
-
error: result.error
|
|
43766
|
-
};
|
|
43767
|
-
}
|
|
43768
|
-
const envelope = result.data;
|
|
43769
|
-
const rows = envelope?.data ?? [];
|
|
43770
|
-
if (wantsReplyState) {
|
|
43771
|
-
assertAwaitingFilterRows(rows, "GET /emails/search");
|
|
43772
|
-
if (rows.length === 0) await ensureSearchReportsReplyState(params.apiClient);
|
|
43773
|
-
}
|
|
43774
|
-
if (wantsAutomated) {
|
|
43775
|
-
assertAutomatedVerdict(rows, "GET /emails/search", expectedAutomatedVerdicts(params.filters.automated, params.filters.q));
|
|
43776
|
-
if (rows.length === 0) await ensureSearchReportsAutomated(params.apiClient);
|
|
43777
|
-
}
|
|
43778
|
-
return {
|
|
43779
|
-
ok: true,
|
|
43780
|
-
cursor: envelope?.meta.cursor ?? cursorFromRows(rows),
|
|
43781
|
-
rows
|
|
43782
|
-
};
|
|
43783
|
-
}
|
|
43784
|
-
function sleep$2(ms) {
|
|
43785
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43786
|
-
}
|
|
43787
43691
|
function cliError$6(message) {
|
|
43788
43692
|
return new Errors.CLIError(message, { exit: 1 });
|
|
43789
43693
|
}
|
|
@@ -45399,10 +45303,23 @@ async function waitForReply(params) {
|
|
|
45399
45303
|
deadline: totalDeadline
|
|
45400
45304
|
});
|
|
45401
45305
|
let strictFilterUnsupported = false;
|
|
45306
|
+
let notBefore = 0;
|
|
45307
|
+
const holdRetryAfter = (failure) => {
|
|
45308
|
+
if (failure.retryAfterMs !== void 0) notBefore = Math.max(notBefore, Date.now() + failure.retryAfterMs);
|
|
45309
|
+
};
|
|
45402
45310
|
for (const phase of phases) {
|
|
45403
45311
|
if (phase.label === "strict" && strictFilterUnsupported) continue;
|
|
45312
|
+
if (notBefore > Date.now()) {
|
|
45313
|
+
const remaining = totalDeadline === null ? Number.POSITIVE_INFINITY : totalDeadline - Date.now();
|
|
45314
|
+
if (remaining <= notBefore - Date.now()) {
|
|
45315
|
+
await sleep$3(Math.max(0, remaining));
|
|
45316
|
+
return null;
|
|
45317
|
+
}
|
|
45318
|
+
await sleep$3(notBefore - Date.now());
|
|
45319
|
+
}
|
|
45404
45320
|
const seenIds = /* @__PURE__ */ new Set();
|
|
45405
45321
|
let cursor = null;
|
|
45322
|
+
let failures = 0;
|
|
45406
45323
|
while (true) {
|
|
45407
45324
|
if (phase.deadline !== null && Date.now() >= phase.deadline) break;
|
|
45408
45325
|
const page = await fetchEmailSearchPage({
|
|
@@ -45413,14 +45330,25 @@ async function waitForReply(params) {
|
|
|
45413
45330
|
since: params.sentAtIso
|
|
45414
45331
|
});
|
|
45415
45332
|
if (!page.ok) {
|
|
45333
|
+
const failure = transientFailure(page);
|
|
45334
|
+
if (failure) {
|
|
45335
|
+
if (!await pauseForTransientFailure(failure, failures, {
|
|
45336
|
+
deadline: phase.deadline,
|
|
45337
|
+
notice,
|
|
45338
|
+
onTransientFailure: holdRetryAfter
|
|
45339
|
+
})) break;
|
|
45340
|
+
failures += 1;
|
|
45341
|
+
continue;
|
|
45342
|
+
}
|
|
45416
45343
|
const payload = extractErrorPayload(page.error);
|
|
45417
|
-
writeErrorWithHints(payload);
|
|
45344
|
+
writeErrorWithHints(payload, page.response);
|
|
45418
45345
|
surfaceUnauthorizedHint({
|
|
45419
45346
|
...params.authFailureContext,
|
|
45420
45347
|
payload
|
|
45421
45348
|
});
|
|
45422
45349
|
throw new Errors.CLIError("Failed to poll for reply.", { exit: 1 });
|
|
45423
45350
|
}
|
|
45351
|
+
failures = 0;
|
|
45424
45352
|
let lastAccepted;
|
|
45425
45353
|
for (let i = page.rows.length - 1; i >= 0; i--) {
|
|
45426
45354
|
const row = page.rows[i];
|
|
@@ -45432,14 +45360,19 @@ async function waitForReply(params) {
|
|
|
45432
45360
|
if (lastAccepted) cursor = encodeReceivedAtSearchCursor(lastAccepted);
|
|
45433
45361
|
const matches = collectNewAcceptedEmails(page.rows, seenIds);
|
|
45434
45362
|
for (const match of matches) {
|
|
45435
|
-
const full = await getEmail({
|
|
45363
|
+
const full = await retryTransient(() => getEmail({
|
|
45436
45364
|
client: params.apiClient.client,
|
|
45437
45365
|
path: { id: match.id },
|
|
45438
45366
|
responseStyle: "fields"
|
|
45367
|
+
}), {
|
|
45368
|
+
deadline: phase.deadline,
|
|
45369
|
+
notice,
|
|
45370
|
+
onTransientFailure: holdRetryAfter
|
|
45439
45371
|
});
|
|
45372
|
+
if (full === null) break;
|
|
45440
45373
|
if (full.error) {
|
|
45441
45374
|
const payload = extractErrorPayload(full.error);
|
|
45442
|
-
writeErrorWithHints(payload);
|
|
45375
|
+
writeErrorWithHints(payload, full.response);
|
|
45443
45376
|
surfaceUnauthorizedHint({
|
|
45444
45377
|
...params.authFailureContext,
|
|
45445
45378
|
payload
|
|
@@ -45463,7 +45396,7 @@ async function waitForReply(params) {
|
|
|
45463
45396
|
if (lastAccepted !== void 0) continue;
|
|
45464
45397
|
if (phase.deadline !== null && Date.now() >= phase.deadline) break;
|
|
45465
45398
|
if (totalDeadline !== null && Date.now() >= totalDeadline) return null;
|
|
45466
|
-
await sleep$
|
|
45399
|
+
await sleep$3(params.interval * 1e3);
|
|
45467
45400
|
}
|
|
45468
45401
|
}
|
|
45469
45402
|
return null;
|
|
@@ -47622,7 +47555,7 @@ async function resolveScopedEmailWait(params) {
|
|
|
47622
47555
|
const requestedTo = to === void 0 ? void 0 : exactAddress(to, "--to");
|
|
47623
47556
|
if (Object.values(unsupported).some((value) => value !== void 0 && value !== false)) throw invalid$1("Connected email waits support exact --from, --to, and --reply-to-sent-email-id matching only; search, body, subject, domain, attachment, and spam filters are unavailable.");
|
|
47624
47557
|
const sentId = replyToSentEmailId.toLowerCase();
|
|
47625
|
-
const result = await
|
|
47558
|
+
const result = await readApiBeforeDeadline(params.deadline, (signal) => getSentEmail({
|
|
47626
47559
|
signal,
|
|
47627
47560
|
client: params.apiClient.client,
|
|
47628
47561
|
path: { id: sentId },
|
|
@@ -47630,7 +47563,8 @@ async function resolveScopedEmailWait(params) {
|
|
|
47630
47563
|
}));
|
|
47631
47564
|
if (result === null) return null;
|
|
47632
47565
|
const sent = result.data?.data;
|
|
47633
|
-
if (result.error
|
|
47566
|
+
if (result.error) throw invalid$1(`Could not read that sent email with the connected credential: ${describeApiFailure(result)}. Inspect the existing send before retrying; do not send it again.`);
|
|
47567
|
+
if (!sent || sent.id !== sentId) throw invalid$1("Could not read that sent email with the connected credential. Inspect the existing send before retrying; do not send it again.");
|
|
47634
47568
|
const ownAddress = exactAddress(sent.from_address, "The sent email's from_address");
|
|
47635
47569
|
if (sent.from_header != null && sent.from_header !== "") {
|
|
47636
47570
|
const header = typeof sent.from_header === "string" ? parseFromHeader(sent.from_header) : null;
|
|
@@ -47640,7 +47574,8 @@ async function resolveScopedEmailWait(params) {
|
|
|
47640
47574
|
return {
|
|
47641
47575
|
from: ownAddress,
|
|
47642
47576
|
recipient,
|
|
47643
|
-
sentId
|
|
47577
|
+
sentId,
|
|
47578
|
+
...typeof sent.created_at === "string" ? { sentAt: sent.created_at } : {}
|
|
47644
47579
|
};
|
|
47645
47580
|
}
|
|
47646
47581
|
//#endregion
|
|
@@ -47665,7 +47600,10 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
47665
47600
|
Connected waits share an address event subscription and recover replies with
|
|
47666
47601
|
exact-parent search. They never scan inbox history. Additional content
|
|
47667
47602
|
filters. The command never sends mail. On timeout it exits 1; run the same
|
|
47668
|
-
wait again to recover a reply to the existing send without resending it
|
|
47603
|
+
wait again to recover a reply to the existing send without resending it.
|
|
47604
|
+
A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a
|
|
47605
|
+
network error during the wait is retried with backoff until --timeout,
|
|
47606
|
+
with one note on stderr per retry.`;
|
|
47669
47607
|
static summary = "Wait for matching inbound emails";
|
|
47670
47608
|
static examples = [
|
|
47671
47609
|
"<%= config.bin %> emails wait --reply-to-sent-email-id <sent-id> --from peer@example.com",
|
|
@@ -47776,6 +47714,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
47776
47714
|
const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
|
|
47777
47715
|
const seenIds = /* @__PURE__ */ new Set();
|
|
47778
47716
|
let cursor = null;
|
|
47717
|
+
let failures = 0;
|
|
47779
47718
|
let matched = 0;
|
|
47780
47719
|
let headerPrinted = false;
|
|
47781
47720
|
const printEmail = (email) => {
|
|
@@ -47834,8 +47773,14 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
47834
47773
|
throw error;
|
|
47835
47774
|
}
|
|
47836
47775
|
if (!page.ok) {
|
|
47776
|
+
const failure = transientFailure(page);
|
|
47777
|
+
if (failure) {
|
|
47778
|
+
if (!await pauseForTransientFailure(failure, failures, { deadline })) break;
|
|
47779
|
+
failures += 1;
|
|
47780
|
+
continue;
|
|
47781
|
+
}
|
|
47837
47782
|
const payload = extractErrorPayload(page.error);
|
|
47838
|
-
writeErrorWithHints(payload);
|
|
47783
|
+
writeErrorWithHints(payload, page.response);
|
|
47839
47784
|
surfaceUnauthorizedHint({
|
|
47840
47785
|
auth,
|
|
47841
47786
|
baseUrlOverridden,
|
|
@@ -47845,6 +47790,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
47845
47790
|
process.exitCode = 1;
|
|
47846
47791
|
return;
|
|
47847
47792
|
}
|
|
47793
|
+
failures = 0;
|
|
47848
47794
|
const nextCursor = cursorFromAcceptedRows(page.rows);
|
|
47849
47795
|
const cursorAdvanced = Boolean(nextCursor && nextCursor !== cursor);
|
|
47850
47796
|
if (nextCursor) cursor = nextCursor;
|
|
@@ -47854,7 +47800,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
47854
47800
|
}
|
|
47855
47801
|
if (cursorAdvanced) continue;
|
|
47856
47802
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
47857
|
-
await sleep$
|
|
47803
|
+
await sleep$3(flags.interval * 1e3);
|
|
47858
47804
|
}
|
|
47859
47805
|
const message = `Timed out waiting for ${flags.number} matching email${flags.number === 1 ? "" : "s"}; received ${matched}.`;
|
|
47860
47806
|
this.timedOut = {
|
|
@@ -47948,6 +47894,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
47948
47894
|
const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
|
|
47949
47895
|
const seenIds = /* @__PURE__ */ new Set();
|
|
47950
47896
|
let cursor = null;
|
|
47897
|
+
let failures = 0;
|
|
47951
47898
|
let printed = 0;
|
|
47952
47899
|
let headerPrinted = false;
|
|
47953
47900
|
while (deadline === null || Date.now() < deadline) {
|
|
@@ -47965,8 +47912,14 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
47965
47912
|
throw error;
|
|
47966
47913
|
}
|
|
47967
47914
|
if (!page.ok) {
|
|
47915
|
+
const failure = transientFailure(page);
|
|
47916
|
+
if (failure) {
|
|
47917
|
+
if (!await pauseForTransientFailure(failure, failures, { deadline })) break;
|
|
47918
|
+
failures += 1;
|
|
47919
|
+
continue;
|
|
47920
|
+
}
|
|
47968
47921
|
const payload = extractErrorPayload(page.error);
|
|
47969
|
-
writeErrorWithHints(payload);
|
|
47922
|
+
writeErrorWithHints(payload, page.response);
|
|
47970
47923
|
surfaceUnauthorizedHint({
|
|
47971
47924
|
auth,
|
|
47972
47925
|
baseUrlOverridden,
|
|
@@ -47976,6 +47929,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
47976
47929
|
process.exitCode = 1;
|
|
47977
47930
|
return;
|
|
47978
47931
|
}
|
|
47932
|
+
failures = 0;
|
|
47979
47933
|
const nextCursor = cursorFromAcceptedRows(page.rows);
|
|
47980
47934
|
const cursorAdvanced = Boolean(nextCursor && nextCursor !== cursor);
|
|
47981
47935
|
if (nextCursor) cursor = nextCursor;
|
|
@@ -47993,7 +47947,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
47993
47947
|
}
|
|
47994
47948
|
if (cursorAdvanced) continue;
|
|
47995
47949
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
47996
|
-
await sleep$
|
|
47950
|
+
await sleep$3(flags.interval * 1e3);
|
|
47997
47951
|
}
|
|
47998
47952
|
}
|
|
47999
47953
|
};
|
|
@@ -49155,7 +49109,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
49155
49109
|
url: "https://primitive.dev"
|
|
49156
49110
|
};
|
|
49157
49111
|
const SDK_VERSION_RANGE = "^2.0.0";
|
|
49158
|
-
const CLI_VERSION_RANGE = "^1.
|
|
49112
|
+
const CLI_VERSION_RANGE = "^1.74.0";
|
|
49159
49113
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
49160
49114
|
function renderHandler() {
|
|
49161
49115
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -50020,7 +49974,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
50020
49974
|
return;
|
|
50021
49975
|
}
|
|
50022
49976
|
completedInitialFollowPoll = true;
|
|
50023
|
-
await sleep$
|
|
49977
|
+
await sleep$3(flags["poll-interval"] * 1e3);
|
|
50024
49978
|
}
|
|
50025
49979
|
});
|
|
50026
49980
|
}
|
|
@@ -50931,7 +50885,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
50931
50885
|
trace = fetched;
|
|
50932
50886
|
break;
|
|
50933
50887
|
}
|
|
50934
|
-
await sleep$
|
|
50888
|
+
await sleep$3(pollIntervalMs);
|
|
50935
50889
|
}
|
|
50936
50890
|
if (!trace) {
|
|
50937
50891
|
const finalTrace = ((await getFunctionTestRunTrace({
|
|
@@ -51096,6 +51050,32 @@ const DEFAULT_API = {
|
|
|
51096
51050
|
getEmail,
|
|
51097
51051
|
listEmails
|
|
51098
51052
|
};
|
|
51053
|
+
/**
|
|
51054
|
+
* The API for a wait: a rate limit, gateway failure or network error backs
|
|
51055
|
+
* off and reads again until the deadline (null: no deadline), then throws
|
|
51056
|
+
* WaitDeadlineReached. These are all reads, so a retry repeats nothing.
|
|
51057
|
+
*/
|
|
51058
|
+
function waitingInboxApi(deadline, api = DEFAULT_API) {
|
|
51059
|
+
const options = { deadline };
|
|
51060
|
+
return {
|
|
51061
|
+
...api,
|
|
51062
|
+
getConversation: retryingOperation(api.getConversation, options),
|
|
51063
|
+
getEmail: retryingOperation(api.getEmail, options),
|
|
51064
|
+
listEmails: retryingOperation(api.listEmails, options, (input) => {
|
|
51065
|
+
const query = input.query;
|
|
51066
|
+
if (deadline === null || query?.wait === void 0) return input;
|
|
51067
|
+
const remainingMs = deadline - Date.now();
|
|
51068
|
+
if (remainingMs <= 0) return null;
|
|
51069
|
+
return {
|
|
51070
|
+
...input,
|
|
51071
|
+
query: {
|
|
51072
|
+
...query,
|
|
51073
|
+
wait: Math.min(query.wait, Math.floor(remainingMs / 1e3))
|
|
51074
|
+
}
|
|
51075
|
+
};
|
|
51076
|
+
})
|
|
51077
|
+
};
|
|
51078
|
+
}
|
|
51099
51079
|
async function listPage(api, apiClient, query) {
|
|
51100
51080
|
let result = await api.listEmails({
|
|
51101
51081
|
client: apiClient.client,
|
|
@@ -51415,7 +51395,7 @@ var InboxNextCommand = class InboxNextCommand extends Command {
|
|
|
51415
51395
|
|
|
51416
51396
|
NOT A WORK QUEUE. Nothing is claimed or locked. Two agents running \`inbox next\` on the same inbox get the same email until one of them replies, and both may answer it. Run one agent per inbox, or coordinate outside Primitive.
|
|
51417
51397
|
|
|
51418
|
-
--wait blocks until something awaits you. It takes the inbox's newest position before checking, then long-polls from that position, re-checking reply state whenever mail arrives and at least every 30 seconds, so nothing that arrives between the check and the wait is missed.
|
|
51398
|
+
--wait blocks until something awaits you. It takes the inbox's newest position before checking, then long-polls from that position, re-checking reply state whenever mail arrives and at least every 30 seconds, so nothing that arrives between the check and the wait is missed. A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a network error during the wait is retried with backoff until --timeout, with one note on stderr per retry; if it lasts until then, the wait exits 5 as if nothing arrived.
|
|
51419
51399
|
|
|
51420
51400
|
--json prints one stable envelope (version 1): \`outcome\` ("email" | "empty" | "error"), \`email\` (id, thread_id, message_id, received_at, from, from_email, to, subject, awaiting, reply_count, last_replied_at, body_text, from_known_address, auth { spf, dmarc }), \`automated\` ({ automated, reasons[], automation_headers_known }, the server's verdict), \`conversation\` (thread_id, subject, message_count, truncated, messages[] with role user|assistant), \`reply_command\`, \`interaction\` ({ hint, kind, fyi, category, plain_reply_completes, no_reply_needed }, decided by the server from a DKIM-authenticated interaction part; null when not reported), \`next_actions\` (the commands that answer the email, best first: each has kind, command, argv, description, placeholders, requires_message; empty when no reply is needed or this CLI cannot answer the interaction), \`automated_awaiting\` ({ total, capped }: automated mail also awaiting a reply, on the empty outcome; null otherwise), and \`error\` ({ code, message }) on failure.
|
|
51421
51401
|
|
|
@@ -51480,12 +51460,17 @@ var InboxNextCommand = class InboxNextCommand extends Command {
|
|
|
51480
51460
|
process.exitCode = INBOX_NEXT_EXIT_CODES.error;
|
|
51481
51461
|
};
|
|
51482
51462
|
const deadline = flags.wait && flags.timeout > 0 ? Date.now() + flags.timeout * 1e3 : null;
|
|
51463
|
+
const api = flags.wait ? waitingInboxApi(deadline) : void 0;
|
|
51483
51464
|
try {
|
|
51484
|
-
let since = flags.wait ? await baselineCursor({
|
|
51465
|
+
let since = flags.wait ? await baselineCursor({
|
|
51466
|
+
apiClient,
|
|
51467
|
+
api
|
|
51468
|
+
}) : null;
|
|
51485
51469
|
let announcedWait = false;
|
|
51486
51470
|
for (;;) {
|
|
51487
51471
|
const result = await findNextAwaiting({
|
|
51488
51472
|
apiClient,
|
|
51473
|
+
api,
|
|
51489
51474
|
includeAutomated: flags["include-automated"]
|
|
51490
51475
|
});
|
|
51491
51476
|
if (result.outcome === "email" || !flags.wait || since === null) {
|
|
@@ -51509,12 +51494,20 @@ var InboxNextCommand = class InboxNextCommand extends Command {
|
|
|
51509
51494
|
const pollStartedAt = Date.now();
|
|
51510
51495
|
since = await waitForActivity({
|
|
51511
51496
|
apiClient,
|
|
51497
|
+
api,
|
|
51512
51498
|
since,
|
|
51513
51499
|
seconds: Math.floor(remainingMs / 1e3)
|
|
51514
51500
|
});
|
|
51515
51501
|
if (since === before && Date.now() - pollStartedAt < 1e3) await sleep$1(1e3);
|
|
51516
51502
|
}
|
|
51517
51503
|
} catch (error) {
|
|
51504
|
+
if (error instanceof WaitDeadlineReached) {
|
|
51505
|
+
await this.printResult({
|
|
51506
|
+
outcome: "empty",
|
|
51507
|
+
automated_awaiting: null
|
|
51508
|
+
}, bin, flags, apiClient);
|
|
51509
|
+
return;
|
|
51510
|
+
}
|
|
51518
51511
|
if (error instanceof ReplyStateUnsupportedError || error instanceof AutomatedFilterUnsupportedError) {
|
|
51519
51512
|
fail(error.code, error.message);
|
|
51520
51513
|
return;
|
|
@@ -52293,6 +52286,15 @@ function openContactRequestNotices(configDir, identity) {
|
|
|
52293
52286
|
}
|
|
52294
52287
|
//#endregion
|
|
52295
52288
|
//#region src/oclif/conversation-status.ts
|
|
52289
|
+
/**
|
|
52290
|
+
* An acknowledgement (an `ack` signal, which `primitive reply --fyi` sends)
|
|
52291
|
+
* exists so its receiver does not have to act. It never wakes or notifies a
|
|
52292
|
+
* session: the event is completed and the message stays readable in its
|
|
52293
|
+
* thread. Read, working and typing still reach the session as status.
|
|
52294
|
+
*/
|
|
52295
|
+
function isQuietConversationStatus(status) {
|
|
52296
|
+
return status.kind === "ack";
|
|
52297
|
+
}
|
|
52296
52298
|
/** A References-derived parent ID cannot prove which Message-ID a signal names. */
|
|
52297
52299
|
async function readBoundSentMessageId(apiClient, wait, signal) {
|
|
52298
52300
|
if (!wait.sentEmailId) return null;
|
|
@@ -53026,12 +53028,12 @@ async function runSharedNotificationListen(options) {
|
|
|
53026
53028
|
await store.skipNotification(row.emailId, sessionKey);
|
|
53027
53029
|
return true;
|
|
53028
53030
|
}
|
|
53029
|
-
if (status && !await conversationStatusDue({
|
|
53031
|
+
if (status && (isQuietConversationStatus(status) || !await conversationStatusDue({
|
|
53030
53032
|
configDir: options.configDir,
|
|
53031
53033
|
scope,
|
|
53032
53034
|
recipient,
|
|
53033
53035
|
sessionKey
|
|
53034
|
-
}, status)) {
|
|
53036
|
+
}, status))) {
|
|
53035
53037
|
await store.skipNotification(row.emailId, sessionKey);
|
|
53036
53038
|
return true;
|
|
53037
53039
|
}
|
|
@@ -53241,7 +53243,9 @@ async function runStandaloneListen(options) {
|
|
|
53241
53243
|
const random = options.random ?? Math.random;
|
|
53242
53244
|
const sleep = options.sleep ?? listenSleep;
|
|
53243
53245
|
const stderr = options.stderr ?? process.stderr;
|
|
53244
|
-
const
|
|
53246
|
+
const hard = options.signal;
|
|
53247
|
+
const receiving = options.stopReceiving ? AbortSignal.any([hard, options.stopReceiving]) : hard;
|
|
53248
|
+
let signal = receiving;
|
|
53245
53249
|
const mode = options.mode ?? "stdout";
|
|
53246
53250
|
const events = options.events;
|
|
53247
53251
|
if (options.number !== void 0 && (!Number.isSafeInteger(options.number) || options.number < 1)) throw new ListenError("--number must be a positive integer.");
|
|
@@ -53398,6 +53402,7 @@ async function runStandaloneListen(options) {
|
|
|
53398
53402
|
continue;
|
|
53399
53403
|
}
|
|
53400
53404
|
signal.throwIfAborted();
|
|
53405
|
+
signal = hard;
|
|
53401
53406
|
let handled;
|
|
53402
53407
|
try {
|
|
53403
53408
|
handled = await options.handler(delivery, signal);
|
|
@@ -53439,6 +53444,7 @@ async function runStandaloneListen(options) {
|
|
|
53439
53444
|
if (!(error instanceof RequestFailure) || error.status !== 409 || error.code !== "stale_delivery") throw error;
|
|
53440
53445
|
stderr.write("Delivery ownership expired before confirmation; it may be delivered again.\n");
|
|
53441
53446
|
}
|
|
53447
|
+
signal = receiving;
|
|
53442
53448
|
}
|
|
53443
53449
|
} catch (error) {
|
|
53444
53450
|
if (!signal.aborted) throw error;
|
|
@@ -53708,6 +53714,7 @@ async function createWakeMail(options) {
|
|
|
53708
53714
|
const sentMessageId = requested && statusContent ? await readBoundSentMessageId(apiClient, requested, signal) : null;
|
|
53709
53715
|
if (statusContent && !sentMessageId) return outcome(false);
|
|
53710
53716
|
const status = requested ? boundConversationStatus(detail, statusContent, requested, sentMessageId, options.sessionKey) : null;
|
|
53717
|
+
if (status && isQuietConversationStatus(status)) return outcome(true);
|
|
53711
53718
|
let admission;
|
|
53712
53719
|
let admissionRetry;
|
|
53713
53720
|
try {
|
|
@@ -53793,14 +53800,16 @@ async function createWakeMail(options) {
|
|
|
53793
53800
|
kind: "mail",
|
|
53794
53801
|
context
|
|
53795
53802
|
})) return outcome(false);
|
|
53796
|
-
if (admission.kind === "request")
|
|
53797
|
-
|
|
53798
|
-
|
|
53799
|
-
|
|
53800
|
-
|
|
53801
|
-
|
|
53802
|
-
|
|
53803
|
-
|
|
53803
|
+
if (admission.kind === "request") {
|
|
53804
|
+
if (pendingRequest) completePending();
|
|
53805
|
+
pendingRequest = {
|
|
53806
|
+
sender,
|
|
53807
|
+
emailId: detail.id,
|
|
53808
|
+
eventId,
|
|
53809
|
+
decidedSenders: [...policy.members()],
|
|
53810
|
+
context
|
|
53811
|
+
};
|
|
53812
|
+
} else {
|
|
53804
53813
|
const wake = {
|
|
53805
53814
|
wakeId: detail.id,
|
|
53806
53815
|
senderRelation: admission.senderRelation,
|
|
@@ -53855,6 +53864,11 @@ const RESUME_LOCK_RETRY_MS = 4e3;
|
|
|
53855
53864
|
* newer mail) waits for that newer mail before waking for the held one.
|
|
53856
53865
|
*/
|
|
53857
53866
|
const HELD_WAKE_SETTLE_MS = 5e3;
|
|
53867
|
+
/**
|
|
53868
|
+
* After a wake, how long the listener keeps taking mail that is already
|
|
53869
|
+
* queued, so mail that arrived together is announced together.
|
|
53870
|
+
*/
|
|
53871
|
+
const WAKE_DRAIN_MS = 1500;
|
|
53858
53872
|
const MAIL_CHECK_RECORD_MS = 5e3;
|
|
53859
53873
|
const RESUME_LOCK_RETRY_STEP_MS = 250;
|
|
53860
53874
|
function isListenLockContention(error) {
|
|
@@ -54245,9 +54259,12 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
54245
54259
|
});
|
|
54246
54260
|
let timedOut = false;
|
|
54247
54261
|
let lastMailCheckRecord = Number.NEGATIVE_INFINITY;
|
|
54262
|
+
const receiveWindow = new AbortController();
|
|
54263
|
+
const sharedNotify = Boolean(target && flags["notify-session"] && requestConfig);
|
|
54248
54264
|
const timer = flags.timeout === void 0 ? void 0 : setTimeout(() => {
|
|
54249
54265
|
timedOut = true;
|
|
54250
|
-
|
|
54266
|
+
receiveWindow.abort();
|
|
54267
|
+
if (sharedNotify) controller.abort();
|
|
54251
54268
|
}, flags.timeout * 1e3);
|
|
54252
54269
|
const cancel = () => controller.abort();
|
|
54253
54270
|
process.on("SIGINT", cancel);
|
|
@@ -54291,6 +54308,7 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
54291
54308
|
socketPath
|
|
54292
54309
|
} : void 0,
|
|
54293
54310
|
handler: wake?.handler ?? handler,
|
|
54311
|
+
stopReceiving: receiveWindow.signal,
|
|
54294
54312
|
stderr: flags.wake ? new Writable({ write(_chunk, _encoding, done) {
|
|
54295
54313
|
done();
|
|
54296
54314
|
} }) : void 0,
|
|
@@ -54318,7 +54336,7 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
54318
54336
|
for (;;) try {
|
|
54319
54337
|
await runListen(settle ? {
|
|
54320
54338
|
...options,
|
|
54321
|
-
|
|
54339
|
+
stopReceiving: AbortSignal.any([receiveWindow.signal, settle])
|
|
54322
54340
|
} : options);
|
|
54323
54341
|
break;
|
|
54324
54342
|
} catch (error) {
|
|
@@ -54330,7 +54348,15 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
54330
54348
|
}
|
|
54331
54349
|
wake?.completed();
|
|
54332
54350
|
if (settle?.aborted && !controller.signal.aborted) wake?.wakeHeld?.();
|
|
54333
|
-
} while (wake && !wake.wakeId() && !wake.status() && !controller.signal.aborted);
|
|
54351
|
+
} while (wake && !wake.wakeId() && !wake.status() && !controller.signal.aborted && !receiveWindow.signal.aborted);
|
|
54352
|
+
if (wake && hookSessionId && (wake.wakeId() || wake.status()) && !controller.signal.aborted) try {
|
|
54353
|
+
await runListen({
|
|
54354
|
+
...options,
|
|
54355
|
+
number: void 0,
|
|
54356
|
+
stopReceiving: AbortSignal.any([receiveWindow.signal, AbortSignal.timeout(WAKE_DRAIN_MS)])
|
|
54357
|
+
});
|
|
54358
|
+
wake.completed();
|
|
54359
|
+
} catch {}
|
|
54334
54360
|
} else {
|
|
54335
54361
|
const notify = options.notifySession;
|
|
54336
54362
|
let expectedCwd;
|
|
@@ -54410,7 +54436,7 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
54410
54436
|
configDir: this.config.configDir,
|
|
54411
54437
|
...auto
|
|
54412
54438
|
});
|
|
54413
|
-
} else if (controller.signal.aborted) process.exitCode = timedOut && flags.wake ? 0 : timedOut ? 2 : 130;
|
|
54439
|
+
} else if (controller.signal.aborted || timedOut) process.exitCode = timedOut && flags.wake ? 0 : timedOut ? 2 : 130;
|
|
54414
54440
|
}
|
|
54415
54441
|
};
|
|
54416
54442
|
//#endregion
|
|
@@ -54672,7 +54698,7 @@ var ListenInitCommand = class ListenInitCommand extends Command {
|
|
|
54672
54698
|
const UUID$3 = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
|
|
54673
54699
|
var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
54674
54700
|
static summary = "List mail a session's wake listener accepted but not yet read";
|
|
54675
|
-
static description = `Prints the durable pending notices for one Claude Code session of the selected connected profile. A notice is written before its wake event is acknowledged and is removed when the session reads that exact email with \`primitive emails get --id <id>\`. Notices carry only server-derived metadata (sender, thread, whether this profile sent in the thread, newer-mail count), never subject or body. Status notices (kind status) name a peer signal on a message this session sent and have nothing to read; clear them with --clear after delivering them.
|
|
54701
|
+
static description = `Prints the durable pending notices for one Claude Code session of the selected connected profile. A notice is written before its wake event is acknowledged and is removed when the session reads that exact email with \`primitive emails get --id <id>\`. Notices carry only server-derived metadata (sender, thread, whether this profile sent in the thread, newer-mail count), never subject or body. Status notices (kind status) name a peer signal on a message this session sent and have nothing to read; clear them with --clear after delivering them. --claim marks exact ids as announced to the session and prints the ids this call claimed as \`claimed\`; receive hooks use it so each email is announced at most once. Past 50 mail notices the oldest announced one gives way; mail the session has not been shown is never dropped, so when all 50 are unannounced the list reports full and newer mail stays queued, unacknowledged. Reading never takes the writer lock, so it works while a listener holds it.`;
|
|
54676
54702
|
static examples = ["PRIMITIVE_AGENT_PROFILE=work <%= config.bin %> listen pending --session <claude-session-id>", "PRIMITIVE_AGENT_PROFILE=work <%= config.bin %> listen pending --session <claude-session-id> --clear <email-id>"];
|
|
54677
54703
|
static flags = {
|
|
54678
54704
|
session: Flags.string({ description: "Claude Code session ID (defaults to CLAUDE_CODE_SESSION_ID in a Claude session)" }),
|
|
@@ -54680,6 +54706,11 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
|
54680
54706
|
multiple: true,
|
|
54681
54707
|
description: "Remove these exact email IDs after delivering them (repeatable)"
|
|
54682
54708
|
}),
|
|
54709
|
+
claim: Flags.string({
|
|
54710
|
+
multiple: true,
|
|
54711
|
+
exclusive: ["clear"],
|
|
54712
|
+
description: "Mark these exact email IDs as announced to the session and report which ones this call claimed (repeatable)"
|
|
54713
|
+
}),
|
|
54683
54714
|
json: Flags.boolean({ description: "Print JSON (already the default)" })
|
|
54684
54715
|
};
|
|
54685
54716
|
async run() {
|
|
@@ -54692,6 +54723,9 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
|
54692
54723
|
if (!session || !UUID$3.test(session)) this.error("Pass --session with the Claude Code session UUID.");
|
|
54693
54724
|
const clear = flags.clear ?? [];
|
|
54694
54725
|
if (clear.some((id) => !UUID$3.test(id))) this.error("--clear takes exact email UUIDs.");
|
|
54726
|
+
const claim = flags.claim ?? [];
|
|
54727
|
+
if (claim.some((id) => !UUID$3.test(id))) this.error("--claim takes exact email UUIDs.");
|
|
54728
|
+
const claimed = claim.length ? await claimPendingMail(this.config.configDir, profileName, session, claim) : void 0;
|
|
54695
54729
|
const notices = clear.length ? await removePendingMail(this.config.configDir, profileName, session, clear) : readPendingMail(this.config.configDir, profileName, session);
|
|
54696
54730
|
this.log(JSON.stringify({
|
|
54697
54731
|
session_id: session.toLowerCase(),
|
|
@@ -54700,6 +54734,7 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
|
|
|
54700
54734
|
read_command: wakeReadCommand("<id>", profileName),
|
|
54701
54735
|
path: pendingMailPath(this.config.configDir, profileName, session),
|
|
54702
54736
|
full: notices.length >= 50,
|
|
54737
|
+
...claimed ? { claimed } : {},
|
|
54703
54738
|
notices
|
|
54704
54739
|
}, null, 2));
|
|
54705
54740
|
}
|
|
@@ -54852,6 +54887,8 @@ function listenForCopyHotkey(params) {
|
|
|
54852
54887
|
//#endregion
|
|
54853
54888
|
//#region src/oclif/commands/login.ts
|
|
54854
54889
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
54890
|
+
/** How long a login waits for an in-flight agent registration on this machine. */
|
|
54891
|
+
const REGISTRATION_WAIT_MS = 15e3;
|
|
54855
54892
|
function cliError$3(message) {
|
|
54856
54893
|
return new Errors.CLIError(message, { exit: 1 });
|
|
54857
54894
|
}
|
|
@@ -54987,6 +55024,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
54987
55024
|
await this.runBrowserLogin(flags, this.retryCommand());
|
|
54988
55025
|
}
|
|
54989
55026
|
async runBrowserLogin(flags, retryCommand = this.retryCommand()) {
|
|
55027
|
+
await waitForRegistrations(this.config.configDir, REGISTRATION_WAIT_MS, { onWait: () => process.stderr.write("Waiting a few seconds for this machine's agent registration to finish before changing the login.\n") });
|
|
54990
55028
|
let releaseCredentialsLock;
|
|
54991
55029
|
try {
|
|
54992
55030
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
@@ -56274,7 +56312,7 @@ const TITLES = {
|
|
|
56274
56312
|
"claude.settings_valid": "Claude Code settings file",
|
|
56275
56313
|
"claude.hook.session_start": "Claude SessionStart hook",
|
|
56276
56314
|
"claude.hook.session_end": "Claude SessionEnd hook",
|
|
56277
|
-
"claude.hook.stop": "Claude
|
|
56315
|
+
"claude.hook.stop": "Claude receive hooks",
|
|
56278
56316
|
"claude.instructions": "Claude Code instructions",
|
|
56279
56317
|
"codex.hook.session_start": "Codex SessionStart hook",
|
|
56280
56318
|
"codex.instructions": "Codex instructions",
|
|
@@ -56710,7 +56748,11 @@ async function runMachineDoctor(options) {
|
|
|
56710
56748
|
throw new MachineFileError(`The hook launcher ${cli.launcher?.path ?? ""} could not be written.`);
|
|
56711
56749
|
}
|
|
56712
56750
|
if (!repaired.changed.size) return id === "claude.hook.stop";
|
|
56713
|
-
|
|
56751
|
+
for (const item of repaired.receivers) addSessionReceiver(item.configDir, item.session, {
|
|
56752
|
+
profile: item.profile,
|
|
56753
|
+
address: item.address
|
|
56754
|
+
});
|
|
56755
|
+
if (JSON.stringify(repaired.settings) !== JSON.stringify(settingsRead.settings)) writeClaudeSettings({
|
|
56714
56756
|
claudeDir: paths.claudeDir,
|
|
56715
56757
|
read: settingsRead.read,
|
|
56716
56758
|
settings: repaired.settings,
|
|
@@ -59592,7 +59634,7 @@ async function pollForSettlementInteraction(params) {
|
|
|
59592
59634
|
}
|
|
59593
59635
|
if (receipt) return receipt;
|
|
59594
59636
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
59595
|
-
await sleep$
|
|
59637
|
+
await sleep$3(params.intervalSeconds * 1e3);
|
|
59596
59638
|
}
|
|
59597
59639
|
return null;
|
|
59598
59640
|
}
|
|
@@ -63269,8 +63311,8 @@ function setupChallengeIds(configDir, agent) {
|
|
|
63269
63311
|
const id = savedSetupChallengeId(agentProfileDirectory(configDir, agent.profileName));
|
|
63270
63312
|
return id ? [id] : [];
|
|
63271
63313
|
}
|
|
63272
|
-
async function listRows(apiClient, query) {
|
|
63273
|
-
const result = await listEmails({
|
|
63314
|
+
async function listRows(api, apiClient, query) {
|
|
63315
|
+
const result = await api.listEmails({
|
|
63274
63316
|
client: apiClient.client,
|
|
63275
63317
|
query,
|
|
63276
63318
|
responseStyle: "fields"
|
|
@@ -63282,8 +63324,8 @@ async function listRows(apiClient, query) {
|
|
|
63282
63324
|
cursor: envelope?.meta?.cursor ?? null
|
|
63283
63325
|
};
|
|
63284
63326
|
}
|
|
63285
|
-
async function readDetail(apiClient, id) {
|
|
63286
|
-
const result = await getEmail({
|
|
63327
|
+
async function readDetail(api, apiClient, id) {
|
|
63328
|
+
const result = await api.getEmail({
|
|
63287
63329
|
client: apiClient.client,
|
|
63288
63330
|
path: { id },
|
|
63289
63331
|
responseStyle: "fields"
|
|
@@ -63296,13 +63338,13 @@ async function readDetail(apiClient, id) {
|
|
|
63296
63338
|
const detail = result.data?.data;
|
|
63297
63339
|
return detail !== null && typeof detail === "object" ? detail : null;
|
|
63298
63340
|
}
|
|
63299
|
-
async function firstMatch(apiClient, rows, filters, seen) {
|
|
63341
|
+
async function firstMatch(api, apiClient, rows, filters, seen) {
|
|
63300
63342
|
for (const row of rows) {
|
|
63301
63343
|
const id = str(row.id);
|
|
63302
63344
|
if (!id || seen.has(id)) continue;
|
|
63303
63345
|
seen.add(id);
|
|
63304
63346
|
if (!rowMayMatch(row, filters)) continue;
|
|
63305
|
-
const detail = await readDetail(apiClient, id);
|
|
63347
|
+
const detail = await readDetail(api, apiClient, id);
|
|
63306
63348
|
if (detail && detailMatches(detail, filters)) return detail;
|
|
63307
63349
|
}
|
|
63308
63350
|
return null;
|
|
@@ -63317,32 +63359,49 @@ function arrivedSince(row, threshold) {
|
|
|
63317
63359
|
* the deadline.
|
|
63318
63360
|
*/
|
|
63319
63361
|
async function waitForEmail(params) {
|
|
63362
|
+
const { deadline } = params;
|
|
63363
|
+
const api = waitingInboxApi(deadline, {
|
|
63364
|
+
getConversation,
|
|
63365
|
+
getEmail,
|
|
63366
|
+
listEmails
|
|
63367
|
+
});
|
|
63368
|
+
try {
|
|
63369
|
+
return await tailForEmail(api, params);
|
|
63370
|
+
} catch (error) {
|
|
63371
|
+
if (error instanceof WaitDeadlineReached) return null;
|
|
63372
|
+
throw error;
|
|
63373
|
+
}
|
|
63374
|
+
}
|
|
63375
|
+
async function tailForEmail(api, params) {
|
|
63320
63376
|
const { apiClient, filters, deadline } = params;
|
|
63321
63377
|
const seen = new Set(params.skipIds ?? []);
|
|
63322
|
-
let since = await baselineCursor({
|
|
63323
|
-
|
|
63324
|
-
|
|
63378
|
+
let since = await baselineCursor({
|
|
63379
|
+
apiClient,
|
|
63380
|
+
api
|
|
63381
|
+
});
|
|
63382
|
+
const recent = await listRows(api, apiClient, { limit: EXISTING_PAGE_SIZE });
|
|
63383
|
+
const early = await firstMatch(api, apiClient, recent.rows.filter((row) => arrivedSince(row, params.startedAt)).reverse(), filters, seen);
|
|
63325
63384
|
if (early) return early;
|
|
63326
63385
|
if (params.includeExisting) {
|
|
63327
|
-
const found = await firstMatch(apiClient, recent.rows, filters, seen);
|
|
63386
|
+
const found = await firstMatch(api, apiClient, recent.rows, filters, seen);
|
|
63328
63387
|
if (found) return found;
|
|
63329
63388
|
}
|
|
63330
63389
|
for (;;) {
|
|
63331
63390
|
const remainingMs = deadline === null ? MAX_LONG_POLL_SECONDS * 1e3 : deadline - Date.now();
|
|
63332
63391
|
if (remainingMs <= 0) return null;
|
|
63333
63392
|
const startedAt = Date.now();
|
|
63334
|
-
const page = await listRows(apiClient, {
|
|
63393
|
+
const page = await listRows(api, apiClient, {
|
|
63335
63394
|
limit: TAIL_PAGE_SIZE,
|
|
63336
63395
|
since,
|
|
63337
63396
|
wait: Math.max(0, Math.min(MAX_LONG_POLL_SECONDS, Math.floor(remainingMs / 1e3)))
|
|
63338
63397
|
});
|
|
63339
|
-
const found = await firstMatch(apiClient, page.rows, filters, seen);
|
|
63398
|
+
const found = await firstMatch(api, apiClient, page.rows, filters, seen);
|
|
63340
63399
|
if (found) return found;
|
|
63341
63400
|
if (page.rows.length > 0 && page.cursor) {
|
|
63342
63401
|
since = page.cursor;
|
|
63343
63402
|
continue;
|
|
63344
63403
|
}
|
|
63345
|
-
if (Date.now() - startedAt < 1e3) await sleep$
|
|
63404
|
+
if (Date.now() - startedAt < 1e3) await sleep$3(Math.max(0, Math.min(1e3, remainingMs)));
|
|
63346
63405
|
}
|
|
63347
63406
|
}
|
|
63348
63407
|
function describeFilters(filters) {
|
|
@@ -63364,6 +63423,8 @@ Only mail that arrives after the command starts matches, unless --include-existi
|
|
|
63364
63423
|
|
|
63365
63424
|
--code prints only the verification or one-time code, with spaces or dashes between its groups removed. --link prints only the most likely verification, sign-in, confirm, magic or reset link, with an obvious click-tracking wrapper removed. Without either, the email is printed: sender, subject, time, its ID and the start of its text, ready for \`primitive reply --id <id>\`. --json prints one object with id, thread_id, from, to, subject, received_at, text and, when asked, code and link (null when none was found).
|
|
63366
63425
|
|
|
63426
|
+
A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a network error during the wait is retried with backoff until --timeout, with one note on stderr per retry; if it lasts until then, the wait times out (exit 5).
|
|
63427
|
+
|
|
63367
63428
|
Exit codes:
|
|
63368
63429
|
- 0: a matching email arrived and was printed.
|
|
63369
63430
|
- 1: error (API failure or authentication).
|
|
@@ -64117,10 +64178,10 @@ var WakeSchedulesUpdateCommand = class WakeSchedulesUpdateCommand extends Comman
|
|
|
64117
64178
|
};
|
|
64118
64179
|
//#endregion
|
|
64119
64180
|
//#region src/oclif/commands/whoami.ts
|
|
64120
|
-
function formatWhoamiSummary(account, managedInboxDomain) {
|
|
64181
|
+
function formatWhoamiSummary(account, managedInboxDomain, orgName = null) {
|
|
64121
64182
|
const lines = [
|
|
64122
64183
|
`Authenticated as ${account.email}`,
|
|
64123
|
-
`
|
|
64184
|
+
orgName ? `Organization: ${orgName} (${account.id})` : `Organization id: ${account.id}`,
|
|
64124
64185
|
`Plan: ${account.plan}`
|
|
64125
64186
|
];
|
|
64126
64187
|
if (managedInboxDomain) lines.push(`Managed inbox: any-local-part@${managedInboxDomain}`);
|
|
@@ -64209,14 +64270,25 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
64209
64270
|
});
|
|
64210
64271
|
if (!domainsResult.error) managedInboxDomain = (domainsResult.data?.data ?? []).find((row) => row.verified && row.managed_zone !== null)?.domain ?? null;
|
|
64211
64272
|
} catch {}
|
|
64273
|
+
const orgName = auth.credentials?.org_id === account.id ? auth.credentials.org_name ?? null : null;
|
|
64274
|
+
const session = guardedSession(void 0, process.env);
|
|
64275
|
+
let registration = null;
|
|
64276
|
+
try {
|
|
64277
|
+
registration = session ? failedSessionRegistration(this.config.configDir, session) : null;
|
|
64278
|
+
} catch {
|
|
64279
|
+
registration = null;
|
|
64280
|
+
}
|
|
64212
64281
|
if (flags.json) {
|
|
64213
64282
|
this.log(JSON.stringify({
|
|
64214
64283
|
...account,
|
|
64215
|
-
managed_inbox_domain: managedInboxDomain
|
|
64284
|
+
managed_inbox_domain: managedInboxDomain,
|
|
64285
|
+
org_name: orgName,
|
|
64286
|
+
...registration ? { session_registration: registration } : {}
|
|
64216
64287
|
}, null, 2));
|
|
64217
64288
|
return;
|
|
64218
64289
|
}
|
|
64219
|
-
this.log(formatWhoamiSummary(account, managedInboxDomain));
|
|
64290
|
+
this.log(formatWhoamiSummary(account, managedInboxDomain, orgName));
|
|
64291
|
+
if (registration) this.log(registrationSummaryLine(registration));
|
|
64220
64292
|
});
|
|
64221
64293
|
}
|
|
64222
64294
|
};
|