teamclaude-cloud 1.5.2 → 1.5.4
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/package.json +1 -1
- package/src/cloud.js +6 -3
- package/src/index.js +23 -37
package/package.json
CHANGED
package/src/cloud.js
CHANGED
|
@@ -218,8 +218,11 @@ export function buildUsageFromQuota(entry) {
|
|
|
218
218
|
* `usageByUuid` (optional Map/object accountUuid → usage from buildUsageFromQuota)
|
|
219
219
|
* rides along so the dashboard can show 5h/7d/Fable utilization.
|
|
220
220
|
*/
|
|
221
|
-
export async function cloudPush(cloud, accounts, usageByUuid = null) {
|
|
222
|
-
|
|
221
|
+
export async function cloudPush(cloud, accounts, usageByUuid = null, authKey = null) {
|
|
222
|
+
// Auth: an auth key (push to the key's OWNER tenant, no owner login) OR the owner
|
|
223
|
+
// session. The key path only needs the endpoint (requireCloud), not a session.
|
|
224
|
+
if (authKey) requireCloud(cloud); else requireSession(cloud);
|
|
225
|
+
const authHeader = authKey ? { 'x-teamclaude-key': authKey } : { Authorization: `Bearer ${cloud.session.access_token}` };
|
|
223
226
|
const usageOf = (uuid) => {
|
|
224
227
|
if (!usageByUuid || !uuid) return undefined;
|
|
225
228
|
const u = usageByUuid instanceof Map ? usageByUuid.get(uuid) : usageByUuid[uuid];
|
|
@@ -239,7 +242,7 @@ export async function cloudPush(cloud, accounts, usageByUuid = null) {
|
|
|
239
242
|
};
|
|
240
243
|
const res = await httpJson(`${fnBase(cloud)}/sync/push`, {
|
|
241
244
|
method: 'POST',
|
|
242
|
-
headers: fnHeaders(cloud,
|
|
245
|
+
headers: fnHeaders(cloud, authHeader),
|
|
243
246
|
body: payload,
|
|
244
247
|
});
|
|
245
248
|
if (!res.ok) throw new Error(`Cloud push failed (${res.status}): ${describe(res.data)}`);
|
package/src/index.js
CHANGED
|
@@ -322,18 +322,10 @@ async function serverCommand() {
|
|
|
322
322
|
return am ? { ...a, accessToken: am.credential, refreshToken: am.refreshToken, expiresAt: am.expiresAt } : a;
|
|
323
323
|
});
|
|
324
324
|
if (!accounts.length) throw new Error('No OAuth accounts to push');
|
|
325
|
-
// Prefer the AUTH KEY (push to your own tenant, no expiring owner login)
|
|
325
|
+
// Prefer the AUTH KEY (push to your own tenant, no expiring owner login) —
|
|
326
|
+
// it CREATES + updates accounts. Fall back to an owner session only if no key.
|
|
326
327
|
const fresh = (await loadConfig()) || config;
|
|
327
328
|
const key = fresh.cloud?.pullKey || config.cloud?.pullKey;
|
|
328
|
-
if (key) {
|
|
329
|
-
const r = await cloudPushViaKey(resolveCloud(fresh), key, accounts);
|
|
330
|
-
const na = r.notAllowed ? `, ${r.notAllowed} not in the key's group` : '';
|
|
331
|
-
const sk = r.skipped ? `, ${r.skipped} skipped` : '';
|
|
332
|
-
return { summary: `${r.updated} updated, ${r.unchanged} unchanged${na}${sk} (via auth key)` };
|
|
333
|
-
}
|
|
334
|
-
// No key — owner login (also creates new accounts) + usage snapshot.
|
|
335
|
-
const cloud = await ensureCloudSession(fresh);
|
|
336
|
-
if (!cloud?.session?.access_token) throw new Error('No auth key and not logged in — pull once with a key, or run `teamclaude cloud login`');
|
|
337
329
|
const usageByUuid = new Map();
|
|
338
330
|
const quotaCache = await readQuotaCache();
|
|
339
331
|
for (const entry of quotaCache?.accounts || []) {
|
|
@@ -341,8 +333,14 @@ async function serverCommand() {
|
|
|
341
333
|
const usage = buildUsageFromQuota(entry);
|
|
342
334
|
if (usage) usageByUuid.set(entry.accountUuid, usage);
|
|
343
335
|
}
|
|
344
|
-
|
|
345
|
-
|
|
336
|
+
let cloud, authKey = null;
|
|
337
|
+
if (key) { cloud = resolveCloud(fresh); authKey = key; }
|
|
338
|
+
else {
|
|
339
|
+
cloud = await ensureCloudSession(fresh);
|
|
340
|
+
if (!cloud?.session?.access_token) throw new Error('No auth key and not logged in — pull once with a key, or run `teamclaude cloud login`');
|
|
341
|
+
}
|
|
342
|
+
const r = await cloudPush(cloud, accounts, usageByUuid, authKey);
|
|
343
|
+
return { summary: `${r.pushed} new, ${r.updated} updated, ${r.skipped} skipped${usageByUuid.size ? `, usage ${r.usage ?? 0}` : ''}${authKey ? ' (via auth key)' : ''}` };
|
|
346
344
|
}) : undefined,
|
|
347
345
|
});
|
|
348
346
|
hooks = {
|
|
@@ -905,7 +903,7 @@ async function accountsCommand() {
|
|
|
905
903
|
a.refreshToken = newTokens.refreshToken;
|
|
906
904
|
a.expiresAt = newTokens.expiresAt;
|
|
907
905
|
configDirty = true;
|
|
908
|
-
} catch
|
|
906
|
+
} catch {
|
|
909
907
|
// refresh failed — fetchProfile will report the specific error
|
|
910
908
|
}
|
|
911
909
|
}));
|
|
@@ -1727,36 +1725,24 @@ async function cloudLoginCommand(config) {
|
|
|
1727
1725
|
console.log(`Logged in to cloud as ${email}.`);
|
|
1728
1726
|
}
|
|
1729
1727
|
|
|
1730
|
-
// Push account tokens to the cloud using an AUTH KEY — no owner login needed. Reuses
|
|
1731
|
-
// the key-authenticated, group-scoped, freshest-wins /sync/report-token path, so the
|
|
1732
|
-
// tokens land in the KEY'S OWNER tenant (your personal key → your own account). This
|
|
1733
|
-
// UPDATES accounts already registered + in the key's group; an account the key can't
|
|
1734
|
-
// pull is reported as "not in the key's group" (create it via an owner login once).
|
|
1735
|
-
async function cloudPushViaKey(cloud, key, accounts) {
|
|
1736
|
-
const r = { updated: 0, unchanged: 0, notAllowed: 0, skipped: 0 };
|
|
1737
|
-
for (const a of accounts) {
|
|
1738
|
-
if (!a.accountUuid || !a.accessToken || !a.refreshToken) { r.skipped++; continue; }
|
|
1739
|
-
try {
|
|
1740
|
-
const res = await cloudReportToken(cloud, key, a.accountUuid, { accessToken: a.accessToken, refreshToken: a.refreshToken, expiresAt: a.expiresAt });
|
|
1741
|
-
if (res.action === 'updated') r.updated++; else r.unchanged++;
|
|
1742
|
-
} catch (e) {
|
|
1743
|
-
if (/\(40[34]\)/.test(e.message)) r.notAllowed++; else r.skipped++; // 403 not in group / 404 unknown account
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
return r;
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
1728
|
async function cloudPushCommand(config) {
|
|
1750
1729
|
const accounts = (config.accounts || []).filter(a => a.type !== 'apikey');
|
|
1751
1730
|
if (!accounts.length) { console.error('No OAuth accounts to push.'); process.exit(1); }
|
|
1752
1731
|
// Prefer the AUTH KEY: pushing to your own tenant is the key's job — no separate,
|
|
1753
|
-
// short-lived owner login required (the key is the credential, same as pull).
|
|
1732
|
+
// short-lived owner login required (the key is the credential, same as pull). This
|
|
1733
|
+
// CREATES + updates accounts in the key's owner tenant (freshest-wins + plan limits).
|
|
1754
1734
|
const key = config.cloud?.pullKey || process.env.TEAMCLAUDE_AUTH_KEY;
|
|
1755
1735
|
if (key) {
|
|
1756
|
-
const
|
|
1757
|
-
const
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1736
|
+
const usageByUuid = new Map();
|
|
1737
|
+
const quotaCache = await readQuotaCache();
|
|
1738
|
+
for (const entry of quotaCache?.accounts || []) {
|
|
1739
|
+
if (!entry?.accountUuid) continue;
|
|
1740
|
+
const usage = buildUsageFromQuota(entry);
|
|
1741
|
+
if (usage) usageByUuid.set(entry.accountUuid, usage);
|
|
1742
|
+
}
|
|
1743
|
+
const r = await cloudPush(resolveCloud(config), accounts, usageByUuid, key);
|
|
1744
|
+
const usageNote = usageByUuid.size ? `, usage ${r.usage ?? 0}/${usageByUuid.size}` : '';
|
|
1745
|
+
console.log(`Pushed to your account via auth key: ${r.pushed} new, ${r.updated} updated, ${r.skipped} skipped${usageNote}.`);
|
|
1760
1746
|
return;
|
|
1761
1747
|
}
|
|
1762
1748
|
// No auth key — fall back to an owner login (which also CREATES new accounts) and
|