primcli 1.74.0 → 1.74.1
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/dist/oclif/index.js +77 -16
- package/package.json +1 -1
package/dist/oclif/index.js
CHANGED
|
@@ -49109,7 +49109,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
49109
49109
|
url: "https://primitive.dev"
|
|
49110
49110
|
};
|
|
49111
49111
|
const SDK_VERSION_RANGE = "^2.0.0";
|
|
49112
|
-
const CLI_VERSION_RANGE = "^1.74.
|
|
49112
|
+
const CLI_VERSION_RANGE = "^1.74.1";
|
|
49113
49113
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
49114
49114
|
function renderHandler() {
|
|
49115
49115
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -56463,15 +56463,36 @@ async function memberCredentialsFor(options) {
|
|
|
56463
56463
|
return null;
|
|
56464
56464
|
}
|
|
56465
56465
|
}
|
|
56466
|
+
/**
|
|
56467
|
+
* How far the server's claim time may run ahead of the time the CLI saved
|
|
56468
|
+
* the key it returned, before the claim is taken to be a later one. It
|
|
56469
|
+
* absorbs clock skew between this machine and Primitive.
|
|
56470
|
+
*/
|
|
56471
|
+
const CLAIM_SKEW_MS = 3e5;
|
|
56472
|
+
/**
|
|
56473
|
+
* Whether the list shows a saved profile's key is still the connection's
|
|
56474
|
+
* current one, without reading the profile's own record. A connection gets a
|
|
56475
|
+
* new key only by being claimed again, which moves `claimed_at` past the
|
|
56476
|
+
* moment this profile saved its key; a reconnect in progress shows as
|
|
56477
|
+
* pending, and its old key is already revoked. Either way the profile reads
|
|
56478
|
+
* its own record, which tells a rejected key from a live one.
|
|
56479
|
+
*/
|
|
56480
|
+
function keyStillCurrent(listed, profile) {
|
|
56481
|
+
if (listed.status !== "connected" && listed.status !== "claimed") return false;
|
|
56482
|
+
const saved = Date.parse(profile.created_at);
|
|
56483
|
+
return listed.claimedAt !== null && Number.isFinite(saved) && listed.claimedAt <= saved + CLAIM_SKEW_MS;
|
|
56484
|
+
}
|
|
56466
56485
|
/** Pages to read before giving up on confirming absence (10,000 agents). */
|
|
56467
56486
|
const OWNER_LIST_MAX_PAGES = 200;
|
|
56468
56487
|
/**
|
|
56469
|
-
* The member's connection list, hidden agents included,
|
|
56470
|
-
*
|
|
56488
|
+
* The member's connection list, hidden agents included, by address. Read
|
|
56489
|
+
* once per run. `complete` is true only when every page was read and the
|
|
56471
56490
|
* last one explicitly ended the list (`meta.cursor: null`): a status found on
|
|
56472
|
-
* any page stands, but absence is proof only from a complete list.
|
|
56491
|
+
* any page stands, but absence is proof only from a complete list. Paging
|
|
56492
|
+
* stops early, with `complete` false, once every address in `wanted` has
|
|
56493
|
+
* been found, so a large organization does not cost a long read.
|
|
56473
56494
|
*/
|
|
56474
|
-
async function ownerConnectionStatuses(credentials, fetchImpl) {
|
|
56495
|
+
async function ownerConnectionStatuses(credentials, fetchImpl, wanted) {
|
|
56475
56496
|
const statuses = /* @__PURE__ */ new Map();
|
|
56476
56497
|
const partial = () => ({
|
|
56477
56498
|
statuses,
|
|
@@ -56501,12 +56522,19 @@ async function ownerConnectionStatuses(credentials, fetchImpl) {
|
|
|
56501
56522
|
if (text.length > 1048576) return partial();
|
|
56502
56523
|
const body = JSON.parse(text);
|
|
56503
56524
|
if (body.success !== true || !Array.isArray(body.data)) return partial();
|
|
56504
|
-
for (const row of body.data) if (typeof row?.address === "string" && typeof row.status === "string")
|
|
56525
|
+
for (const row of body.data) if (typeof row?.address === "string" && typeof row.status === "string") {
|
|
56526
|
+
const claimedAt = typeof row.claimed_at === "string" ? Date.parse(row.claimed_at) : NaN;
|
|
56527
|
+
statuses.set(row.address, {
|
|
56528
|
+
status: row.status,
|
|
56529
|
+
claimedAt: Number.isFinite(claimedAt) ? claimedAt : null
|
|
56530
|
+
});
|
|
56531
|
+
}
|
|
56505
56532
|
const next = body.meta?.cursor;
|
|
56506
56533
|
if (next === null) return {
|
|
56507
56534
|
statuses,
|
|
56508
56535
|
complete: true
|
|
56509
56536
|
};
|
|
56537
|
+
if (wanted && [...wanted].every((address) => statuses.has(address))) return partial();
|
|
56510
56538
|
if (typeof next !== "string" || !next || next === cursor) return partial();
|
|
56511
56539
|
cursor = next;
|
|
56512
56540
|
} catch {
|
|
@@ -56834,26 +56862,59 @@ async function runMachineDoctor(options) {
|
|
|
56834
56862
|
let ownerList;
|
|
56835
56863
|
let unknown = 0;
|
|
56836
56864
|
let connected = 0;
|
|
56865
|
+
let limited = 0;
|
|
56866
|
+
member = await memberCredentialsFor(options);
|
|
56867
|
+
const checkableBy = (credentials, profile) => !!credentials && credentials.org_id === profile.org_id && credentials.api_base_url === profile.api_base_url;
|
|
56868
|
+
let rateLimited = false;
|
|
56869
|
+
const observedFetch = (async (input, init) => {
|
|
56870
|
+
const response = await fetchImpl(input, init);
|
|
56871
|
+
if (response.status === 429) rateLimited = true;
|
|
56872
|
+
return response;
|
|
56873
|
+
});
|
|
56874
|
+
const listable = profiles.filter((entry) => checkableBy(member, entry.profile));
|
|
56875
|
+
if (listable.length && member) ownerList = await ownerConnectionStatuses(member, observedFetch, new Set(listable.map((entry) => entry.profile.agent_address)));
|
|
56876
|
+
const sharedAddresses = /* @__PURE__ */ new Set();
|
|
56877
|
+
{
|
|
56878
|
+
const seen = /* @__PURE__ */ new Set();
|
|
56879
|
+
for (const entry of profiles) {
|
|
56880
|
+
const address = entry.profile.agent_address.toLowerCase();
|
|
56881
|
+
if (seen.has(address)) sharedAddresses.add(address);
|
|
56882
|
+
seen.add(address);
|
|
56883
|
+
}
|
|
56884
|
+
}
|
|
56885
|
+
const unresolved = [];
|
|
56886
|
+
for (const entry of profiles) {
|
|
56887
|
+
const listed = ownerList && checkableBy(member, entry.profile) && !sharedAddresses.has(entry.profile.agent_address.toLowerCase()) ? ownerList.statuses.get(entry.profile.agent_address) : void 0;
|
|
56888
|
+
if (listed?.status === "revoked") orphans.push(entry);
|
|
56889
|
+
else if (listed && keyStillCurrent(listed, entry.profile)) connected++;
|
|
56890
|
+
else unresolved.push(entry);
|
|
56891
|
+
}
|
|
56837
56892
|
const states = [];
|
|
56838
|
-
for (let start = 0; start <
|
|
56839
|
-
|
|
56840
|
-
|
|
56841
|
-
|
|
56893
|
+
for (let start = 0; start < unresolved.length; start += 8) {
|
|
56894
|
+
if (rateLimited) {
|
|
56895
|
+
limited = unresolved.length - start;
|
|
56896
|
+
unknown += limited;
|
|
56897
|
+
break;
|
|
56898
|
+
}
|
|
56899
|
+
states.push(...await Promise.all(unresolved.slice(start, start + 8).map(async (entry) => ({
|
|
56900
|
+
...entry,
|
|
56901
|
+
state: await agentConnectionState(entry.profile, observedFetch)
|
|
56902
|
+
}))));
|
|
56903
|
+
}
|
|
56842
56904
|
for (const entry of states) if (entry.state === "revoked") orphans.push(entry);
|
|
56843
56905
|
else if (entry.state === "rejected") {
|
|
56844
|
-
|
|
56845
|
-
const checkable = !!member && member.org_id === entry.profile.org_id && member.api_base_url === entry.profile.api_base_url;
|
|
56906
|
+
const checkable = checkableBy(member, entry.profile);
|
|
56846
56907
|
if (member && !checkable) elsewhere++;
|
|
56847
|
-
|
|
56848
|
-
const listed = checkable && ownerList ? ownerList.statuses.get(entry.profile.agent_address) ?? (ownerList.complete ? "absent" : null) : null;
|
|
56908
|
+
const listed = checkable && ownerList ? ownerList.statuses.get(entry.profile.agent_address)?.status ?? (ownerList.complete ? "absent" : null) : null;
|
|
56849
56909
|
if (listed === "revoked" || listed === "absent") orphans.push(entry);
|
|
56850
56910
|
else unconfirmed.push(entry.profile.agent_address);
|
|
56851
56911
|
} else if (entry.state === "unavailable") unknown++;
|
|
56852
56912
|
else connected++;
|
|
56853
56913
|
if (orphans.length) return check("profiles.orphaned", "fail", `${orphans.length} saved profiles were disconnected or removed in Primitive: ${orphans.map((entry) => entry.profile.agent_address).join(", ")}. A repair moves them aside locally; nothing changes in Primitive.`, { fixable: true });
|
|
56854
56914
|
if (unconfirmed.length || unreadable) return check("profiles.orphaned", "warn", [unconfirmed.length ? `${unconfirmed.length} saved profiles have credentials Primitive no longer accepts (${unconfirmed.slice(0, 5).join(", ")}${unconfirmed.length > 5 ? `, and ${unconfirmed.length - 5} more` : ""}). They cannot send or receive. To clean them up, sign in with \`primitive login --force\` as a member of their organization and run \`primitive machine doctor --fix\`, which confirms each one is disconnected before moving it aside${elsewhere ? `. ${elsewhere} of them belong to an organization or Primitive API your current sign-in is not for, so sign in there to check them` : ""}` : "", unreadable ? `${unreadable} profiles could not be read` : ""].filter(Boolean).join("; ").concat("; they were left unchanged."));
|
|
56855
|
-
|
|
56856
|
-
return check("profiles.orphaned", "
|
|
56915
|
+
const limitedNote = limited ? ` Primitive rate limited this check, so ${limited} saved profiles were not asked about; the next run checks them.` : "";
|
|
56916
|
+
if (unknown && !connected) return check("profiles.orphaned", "skip", `Primitive could not be reached to check saved profiles.${limitedNote}`);
|
|
56917
|
+
return check("profiles.orphaned", "ok", `${connected} saved profiles are connected${unknown ? `; ${unknown} could not be checked` : ""}.${limitedNote}`);
|
|
56857
56918
|
};
|
|
56858
56919
|
const moveOrphans = async () => {
|
|
56859
56920
|
let moved = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.74.
|
|
3
|
+
"version": "1.74.1",
|
|
4
4
|
"description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|