replicas-cli 0.2.772 → 0.2.775
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/index.cjs +164 -145
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7558,96 +7558,6 @@ var import_chalk = __toESM(require("chalk"));
|
|
|
7558
7558
|
var import_open = __toESM(require("open"));
|
|
7559
7559
|
var import_prompts5 = __toESM(require("prompts"));
|
|
7560
7560
|
|
|
7561
|
-
// src/lib/auth.ts
|
|
7562
|
-
var import_supabase_js2 = require("@supabase/supabase-js");
|
|
7563
|
-
|
|
7564
|
-
// src/lib/supabase.ts
|
|
7565
|
-
var import_supabase_js = require("@supabase/supabase-js");
|
|
7566
|
-
var supabaseUrl = process.env.REPLICAS_SUPABASE_URL || DEFAULT_SUPABASE_URL;
|
|
7567
|
-
var supabaseAnonKey = process.env.REPLICAS_SUPABASE_ANON_KEY || DEFAULT_SUPABASE_ANON_KEY;
|
|
7568
|
-
var supabase = (0, import_supabase_js.createClient)(supabaseUrl, supabaseAnonKey, {
|
|
7569
|
-
auth: {
|
|
7570
|
-
persistSession: true,
|
|
7571
|
-
autoRefreshToken: true
|
|
7572
|
-
}
|
|
7573
|
-
});
|
|
7574
|
-
|
|
7575
|
-
// src/lib/auth.ts
|
|
7576
|
-
var AuthRefreshError = class extends Error {
|
|
7577
|
-
constructor(message = 'Failed to refresh token. Please run "replicas login" again.') {
|
|
7578
|
-
super(message);
|
|
7579
|
-
this.name = "AuthRefreshError";
|
|
7580
|
-
}
|
|
7581
|
-
};
|
|
7582
|
-
var NotAuthenticatedError = class extends Error {
|
|
7583
|
-
constructor(message = 'Not authenticated. Please run "replicas login"') {
|
|
7584
|
-
super(message);
|
|
7585
|
-
this.name = "NotAuthenticatedError";
|
|
7586
|
-
}
|
|
7587
|
-
};
|
|
7588
|
-
function isReplicasAuthError(error51) {
|
|
7589
|
-
return error51 instanceof AuthRefreshError || error51 instanceof NotAuthenticatedError;
|
|
7590
|
-
}
|
|
7591
|
-
function isTokenExpired(expiresAt) {
|
|
7592
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
7593
|
-
return expiresAt - now < 60;
|
|
7594
|
-
}
|
|
7595
|
-
async function refreshToken() {
|
|
7596
|
-
const config3 = readConfig();
|
|
7597
|
-
if (!config3) {
|
|
7598
|
-
throw new NotAuthenticatedError();
|
|
7599
|
-
}
|
|
7600
|
-
let session;
|
|
7601
|
-
try {
|
|
7602
|
-
const { data, error: error51 } = await supabase.auth.refreshSession({
|
|
7603
|
-
refresh_token: config3.refresh_token
|
|
7604
|
-
});
|
|
7605
|
-
if (error51) throw error51;
|
|
7606
|
-
if (!data.session) throw new AuthRefreshError();
|
|
7607
|
-
session = data.session;
|
|
7608
|
-
} catch (error51) {
|
|
7609
|
-
if (error51 instanceof AuthRefreshError) throw error51;
|
|
7610
|
-
if ((0, import_supabase_js2.isAuthError)(error51) && !(0, import_supabase_js2.isAuthRetryableFetchError)(error51)) {
|
|
7611
|
-
throw new AuthRefreshError();
|
|
7612
|
-
}
|
|
7613
|
-
throw error51;
|
|
7614
|
-
}
|
|
7615
|
-
writeConfig({
|
|
7616
|
-
...config3,
|
|
7617
|
-
access_token: session.access_token,
|
|
7618
|
-
refresh_token: session.refresh_token,
|
|
7619
|
-
expires_at: session.expires_at || 0
|
|
7620
|
-
});
|
|
7621
|
-
}
|
|
7622
|
-
async function getValidToken() {
|
|
7623
|
-
const config3 = readConfig();
|
|
7624
|
-
if (!config3?.access_token) {
|
|
7625
|
-
throw new NotAuthenticatedError();
|
|
7626
|
-
}
|
|
7627
|
-
if (isTokenExpired(config3.expires_at)) {
|
|
7628
|
-
await refreshToken();
|
|
7629
|
-
const newConfig = readConfig();
|
|
7630
|
-
if (!newConfig) {
|
|
7631
|
-
throw new AuthRefreshError();
|
|
7632
|
-
}
|
|
7633
|
-
return newConfig.access_token;
|
|
7634
|
-
}
|
|
7635
|
-
return config3.access_token;
|
|
7636
|
-
}
|
|
7637
|
-
async function getCurrentUser() {
|
|
7638
|
-
const token = await getValidToken();
|
|
7639
|
-
const { data: { user }, error: error51 } = await supabase.auth.getUser(token);
|
|
7640
|
-
if (error51) throw error51;
|
|
7641
|
-
if (!user) throw new Error("User not found");
|
|
7642
|
-
return {
|
|
7643
|
-
id: user.id,
|
|
7644
|
-
email: user.email || "Unknown"
|
|
7645
|
-
};
|
|
7646
|
-
}
|
|
7647
|
-
|
|
7648
|
-
// src/lib/organization.ts
|
|
7649
|
-
var import_prompts4 = __toESM(require("prompts"));
|
|
7650
|
-
|
|
7651
7561
|
// ../shared/src/type-guards.ts
|
|
7652
7562
|
function isRecord(value) {
|
|
7653
7563
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -27963,7 +27873,7 @@ var headlessAgentRequestSchema = external_exports.object({
|
|
|
27963
27873
|
});
|
|
27964
27874
|
|
|
27965
27875
|
// ../shared/src/cli-version.ts
|
|
27966
|
-
var CLI_VERSION = "0.2.
|
|
27876
|
+
var CLI_VERSION = "0.2.775";
|
|
27967
27877
|
|
|
27968
27878
|
// ../shared/src/version.ts
|
|
27969
27879
|
function compareVersions(v1, v2) {
|
|
@@ -28697,6 +28607,96 @@ function identityTokenUrl() {
|
|
|
28697
28607
|
return `http://127.0.0.1:${IDENTITY_PROXY_PORT}${IDENTITY_TOKEN_PATH}`;
|
|
28698
28608
|
}
|
|
28699
28609
|
|
|
28610
|
+
// src/lib/auth.ts
|
|
28611
|
+
var import_supabase_js2 = require("@supabase/supabase-js");
|
|
28612
|
+
|
|
28613
|
+
// src/lib/supabase.ts
|
|
28614
|
+
var import_supabase_js = require("@supabase/supabase-js");
|
|
28615
|
+
var supabaseUrl = process.env.REPLICAS_SUPABASE_URL || DEFAULT_SUPABASE_URL;
|
|
28616
|
+
var supabaseAnonKey = process.env.REPLICAS_SUPABASE_ANON_KEY || DEFAULT_SUPABASE_ANON_KEY;
|
|
28617
|
+
var supabase = (0, import_supabase_js.createClient)(supabaseUrl, supabaseAnonKey, {
|
|
28618
|
+
auth: {
|
|
28619
|
+
persistSession: true,
|
|
28620
|
+
autoRefreshToken: true
|
|
28621
|
+
}
|
|
28622
|
+
});
|
|
28623
|
+
|
|
28624
|
+
// src/lib/auth.ts
|
|
28625
|
+
var AuthRefreshError = class extends Error {
|
|
28626
|
+
constructor(message = 'Failed to refresh token. Please run "replicas login" again.') {
|
|
28627
|
+
super(message);
|
|
28628
|
+
this.name = "AuthRefreshError";
|
|
28629
|
+
}
|
|
28630
|
+
};
|
|
28631
|
+
var NotAuthenticatedError = class extends Error {
|
|
28632
|
+
constructor(message = 'Not authenticated. Please run "replicas login"') {
|
|
28633
|
+
super(message);
|
|
28634
|
+
this.name = "NotAuthenticatedError";
|
|
28635
|
+
}
|
|
28636
|
+
};
|
|
28637
|
+
function isReplicasAuthError(error51) {
|
|
28638
|
+
return error51 instanceof AuthRefreshError || error51 instanceof NotAuthenticatedError;
|
|
28639
|
+
}
|
|
28640
|
+
function isTokenExpired(expiresAt) {
|
|
28641
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
28642
|
+
return expiresAt - now < 60;
|
|
28643
|
+
}
|
|
28644
|
+
async function refreshToken() {
|
|
28645
|
+
const config3 = readConfig();
|
|
28646
|
+
if (!config3) {
|
|
28647
|
+
throw new NotAuthenticatedError();
|
|
28648
|
+
}
|
|
28649
|
+
let session;
|
|
28650
|
+
try {
|
|
28651
|
+
const { data, error: error51 } = await supabase.auth.refreshSession({
|
|
28652
|
+
refresh_token: config3.refresh_token
|
|
28653
|
+
});
|
|
28654
|
+
if (error51) throw error51;
|
|
28655
|
+
if (!data.session) throw new AuthRefreshError();
|
|
28656
|
+
session = data.session;
|
|
28657
|
+
} catch (error51) {
|
|
28658
|
+
if (error51 instanceof AuthRefreshError) throw error51;
|
|
28659
|
+
if ((0, import_supabase_js2.isAuthError)(error51) && !(0, import_supabase_js2.isAuthRetryableFetchError)(error51)) {
|
|
28660
|
+
throw new AuthRefreshError();
|
|
28661
|
+
}
|
|
28662
|
+
throw error51;
|
|
28663
|
+
}
|
|
28664
|
+
writeConfig({
|
|
28665
|
+
...config3,
|
|
28666
|
+
access_token: session.access_token,
|
|
28667
|
+
refresh_token: session.refresh_token,
|
|
28668
|
+
expires_at: session.expires_at || 0
|
|
28669
|
+
});
|
|
28670
|
+
}
|
|
28671
|
+
async function getValidToken() {
|
|
28672
|
+
const config3 = readConfig();
|
|
28673
|
+
if (!config3?.access_token) {
|
|
28674
|
+
throw new NotAuthenticatedError();
|
|
28675
|
+
}
|
|
28676
|
+
if (isTokenExpired(config3.expires_at)) {
|
|
28677
|
+
await refreshToken();
|
|
28678
|
+
const newConfig = readConfig();
|
|
28679
|
+
if (!newConfig) {
|
|
28680
|
+
throw new AuthRefreshError();
|
|
28681
|
+
}
|
|
28682
|
+
return newConfig.access_token;
|
|
28683
|
+
}
|
|
28684
|
+
return config3.access_token;
|
|
28685
|
+
}
|
|
28686
|
+
async function getCurrentUser() {
|
|
28687
|
+
const token = await getValidToken();
|
|
28688
|
+
const { data: { user }, error: error51 } = await supabase.auth.getUser(token);
|
|
28689
|
+
if (error51) throw error51;
|
|
28690
|
+
if (!user) throw new Error("User not found");
|
|
28691
|
+
return {
|
|
28692
|
+
id: user.id,
|
|
28693
|
+
email: user.email || "Unknown"
|
|
28694
|
+
};
|
|
28695
|
+
}
|
|
28696
|
+
|
|
28697
|
+
// src/lib/organization.ts
|
|
28698
|
+
var import_prompts4 = __toESM(require("prompts"));
|
|
28699
|
+
|
|
28700
28700
|
// src/lib/monolith-url.ts
|
|
28701
28701
|
var MONOLITH_URL = process.env.REPLICAS_MONOLITH_URL || process.env.MONOLITH_URL || "https://api.replicas.dev";
|
|
28702
28702
|
|
|
@@ -28822,6 +28822,54 @@ async function resolveActiveOrganization() {
|
|
|
28822
28822
|
}
|
|
28823
28823
|
}
|
|
28824
28824
|
|
|
28825
|
+
// src/lib/api.ts
|
|
28826
|
+
async function buildOrgAuthHeaders() {
|
|
28827
|
+
const extraHeaders = {};
|
|
28828
|
+
if (isAgentMode()) {
|
|
28829
|
+
const agent = readAgentConfig();
|
|
28830
|
+
if (!agent) {
|
|
28831
|
+
throw new Error("Agent mode config is incomplete");
|
|
28832
|
+
}
|
|
28833
|
+
extraHeaders["X-Workspace-Id"] = agent.workspace_id;
|
|
28834
|
+
return { bearer: agent.engine_secret, extraHeaders };
|
|
28835
|
+
}
|
|
28836
|
+
const bearer = await getValidToken();
|
|
28837
|
+
const selectedId = getConfirmedOrganizationId();
|
|
28838
|
+
const organizationId = selectedId ?? (await ensureOrganizationSelection({ interactive: true })).organization?.id;
|
|
28839
|
+
if (!organizationId) {
|
|
28840
|
+
throw new Error(
|
|
28841
|
+
'No organization selected. Please run "replicas org switch" to select an organization.'
|
|
28842
|
+
);
|
|
28843
|
+
}
|
|
28844
|
+
extraHeaders["Replicas-Org-Id"] = organizationId;
|
|
28845
|
+
return { bearer, extraHeaders };
|
|
28846
|
+
}
|
|
28847
|
+
async function orgAuthenticatedFetch(url2, options) {
|
|
28848
|
+
const { bearer, extraHeaders } = await buildOrgAuthHeaders();
|
|
28849
|
+
return apiFetch(url2, {
|
|
28850
|
+
"Authorization": `Bearer ${bearer}`,
|
|
28851
|
+
...extraHeaders
|
|
28852
|
+
}, options);
|
|
28853
|
+
}
|
|
28854
|
+
async function orgAuthenticatedSseStream(url2, options) {
|
|
28855
|
+
const { bearer, extraHeaders } = await buildOrgAuthHeaders();
|
|
28856
|
+
const response = await fetch(`${MONOLITH_URL}${url2}`, {
|
|
28857
|
+
method: "POST",
|
|
28858
|
+
headers: {
|
|
28859
|
+
"Authorization": `Bearer ${bearer}`,
|
|
28860
|
+
"Content-Type": "application/json",
|
|
28861
|
+
"Accept": "text/event-stream",
|
|
28862
|
+
...extraHeaders
|
|
28863
|
+
},
|
|
28864
|
+
body: JSON.stringify(options.body)
|
|
28865
|
+
});
|
|
28866
|
+
if (!response.ok || !response.body) {
|
|
28867
|
+
await throwResponseError(response);
|
|
28868
|
+
return;
|
|
28869
|
+
}
|
|
28870
|
+
await readSseStream(response.body, options.onEvent);
|
|
28871
|
+
}
|
|
28872
|
+
|
|
28825
28873
|
// src/lib/oauth-flow-ui.ts
|
|
28826
28874
|
var LABEL_WIDTH = 16;
|
|
28827
28875
|
var brand = import_chalk.default.hex("#3eeba3");
|
|
@@ -28829,7 +28877,8 @@ function field(label, value) {
|
|
|
28829
28877
|
console.log(` ${import_chalk.default.gray(label.padEnd(LABEL_WIDTH))}${value}`);
|
|
28830
28878
|
}
|
|
28831
28879
|
function appliesTo(target) {
|
|
28832
|
-
|
|
28880
|
+
if (!target.isUserScoped) return "Organization Settings";
|
|
28881
|
+
return target.sharesWithOrganization ? "Personal Settings + Organization Settings" : "Personal Settings";
|
|
28833
28882
|
}
|
|
28834
28883
|
function hyperlink(url2) {
|
|
28835
28884
|
return `\x1B]8;;${url2}\x07${url2}\x1B]8;;\x07`;
|
|
@@ -28857,18 +28906,24 @@ async function resolveAuthFlowTarget(identity) {
|
|
|
28857
28906
|
...identity,
|
|
28858
28907
|
replicasAccount: null,
|
|
28859
28908
|
organization: null,
|
|
28860
|
-
role: null
|
|
28909
|
+
role: null,
|
|
28910
|
+
sharesWithOrganization: false
|
|
28861
28911
|
};
|
|
28862
28912
|
}
|
|
28863
28913
|
const [replicasAccount, active] = await Promise.all([
|
|
28864
28914
|
getCurrentUser().then((user) => user.email).catch(() => null),
|
|
28865
28915
|
ensureOrganization({ interactive: true })
|
|
28866
28916
|
]);
|
|
28917
|
+
const sharesWithOrganization = identity.isUserScoped && active.organization !== null && active.role === "admin" && await Promise.all([
|
|
28918
|
+
orgAuthenticatedFetch("/v1/organization/onboarding"),
|
|
28919
|
+
orgAuthenticatedFetch(identity.orgEndpoint)
|
|
28920
|
+
]).then(([{ onboarding }, orgCredential]) => !onboarding.completed && !orgCredential.hasCredentials).catch(() => false);
|
|
28867
28921
|
return {
|
|
28868
28922
|
...identity,
|
|
28869
28923
|
replicasAccount,
|
|
28870
28924
|
organization: active.organization,
|
|
28871
|
-
role: active.role
|
|
28925
|
+
role: active.role,
|
|
28926
|
+
sharesWithOrganization
|
|
28872
28927
|
};
|
|
28873
28928
|
}
|
|
28874
28929
|
async function verifyAuthFlowTarget(target, assumeYes) {
|
|
@@ -28879,6 +28934,15 @@ async function verifyAuthFlowTarget(target, assumeYes) {
|
|
|
28879
28934
|
if (!target.isUserScoped) {
|
|
28880
28935
|
console.log(import_chalk.default.gray("\n Use --user to apply this to your personal account instead."));
|
|
28881
28936
|
}
|
|
28937
|
+
if (target.sharesWithOrganization) {
|
|
28938
|
+
console.log(
|
|
28939
|
+
import_chalk.default.gray(
|
|
28940
|
+
`
|
|
28941
|
+
${target.organization?.name ?? "Your organization"} has no ${target.credentialLabel} default yet,
|
|
28942
|
+
so this also becomes the organization default.`
|
|
28943
|
+
)
|
|
28944
|
+
);
|
|
28945
|
+
}
|
|
28882
28946
|
console.log();
|
|
28883
28947
|
if (assumeYes) {
|
|
28884
28948
|
console.log(import_chalk.default.gray(" Confirmed by --yes."));
|
|
@@ -29362,54 +29426,6 @@ async function connectSSH(token, host, proxyCommand, localForward) {
|
|
|
29362
29426
|
var import_chalk6 = __toESM(require("chalk"));
|
|
29363
29427
|
var import_prompts6 = __toESM(require("prompts"));
|
|
29364
29428
|
|
|
29365
|
-
// src/lib/api.ts
|
|
29366
|
-
async function buildOrgAuthHeaders() {
|
|
29367
|
-
const extraHeaders = {};
|
|
29368
|
-
if (isAgentMode()) {
|
|
29369
|
-
const agent = readAgentConfig();
|
|
29370
|
-
if (!agent) {
|
|
29371
|
-
throw new Error("Agent mode config is incomplete");
|
|
29372
|
-
}
|
|
29373
|
-
extraHeaders["X-Workspace-Id"] = agent.workspace_id;
|
|
29374
|
-
return { bearer: agent.engine_secret, extraHeaders };
|
|
29375
|
-
}
|
|
29376
|
-
const bearer = await getValidToken();
|
|
29377
|
-
const selectedId = getConfirmedOrganizationId();
|
|
29378
|
-
const organizationId = selectedId ?? (await ensureOrganizationSelection({ interactive: true })).organization?.id;
|
|
29379
|
-
if (!organizationId) {
|
|
29380
|
-
throw new Error(
|
|
29381
|
-
'No organization selected. Please run "replicas org switch" to select an organization.'
|
|
29382
|
-
);
|
|
29383
|
-
}
|
|
29384
|
-
extraHeaders["Replicas-Org-Id"] = organizationId;
|
|
29385
|
-
return { bearer, extraHeaders };
|
|
29386
|
-
}
|
|
29387
|
-
async function orgAuthenticatedFetch(url2, options) {
|
|
29388
|
-
const { bearer, extraHeaders } = await buildOrgAuthHeaders();
|
|
29389
|
-
return apiFetch(url2, {
|
|
29390
|
-
"Authorization": `Bearer ${bearer}`,
|
|
29391
|
-
...extraHeaders
|
|
29392
|
-
}, options);
|
|
29393
|
-
}
|
|
29394
|
-
async function orgAuthenticatedSseStream(url2, options) {
|
|
29395
|
-
const { bearer, extraHeaders } = await buildOrgAuthHeaders();
|
|
29396
|
-
const response = await fetch(`${MONOLITH_URL}${url2}`, {
|
|
29397
|
-
method: "POST",
|
|
29398
|
-
headers: {
|
|
29399
|
-
"Authorization": `Bearer ${bearer}`,
|
|
29400
|
-
"Content-Type": "application/json",
|
|
29401
|
-
"Accept": "text/event-stream",
|
|
29402
|
-
...extraHeaders
|
|
29403
|
-
},
|
|
29404
|
-
body: JSON.stringify(options.body)
|
|
29405
|
-
});
|
|
29406
|
-
if (!response.ok || !response.body) {
|
|
29407
|
-
await throwResponseError(response);
|
|
29408
|
-
return;
|
|
29409
|
-
}
|
|
29410
|
-
await readSseStream(response.body, options.onEvent);
|
|
29411
|
-
}
|
|
29412
|
-
|
|
29413
29429
|
// src/lib/git.ts
|
|
29414
29430
|
var import_child_process2 = require("child_process");
|
|
29415
29431
|
var import_path2 = __toESM(require("path"));
|
|
@@ -30061,7 +30077,7 @@ async function runCredentialAuthFlow(options, flow) {
|
|
|
30061
30077
|
const { providerLabel, credentialLabel, connectorLabel } = flow;
|
|
30062
30078
|
const totalSteps = flow.oauthSteps + UPLOAD_STEPS;
|
|
30063
30079
|
const isUserScoped = options.org !== true;
|
|
30064
|
-
const identity = { isUserScoped, credentialLabel, connectorLabel };
|
|
30080
|
+
const identity = { isUserScoped, orgEndpoint: flow.orgEndpoint, credentialLabel, connectorLabel };
|
|
30065
30081
|
try {
|
|
30066
30082
|
if (options.org === true && options.user === true) {
|
|
30067
30083
|
throw new Error("Pass either --org or --user, not both.");
|
|
@@ -30085,7 +30101,7 @@ async function runCredentialAuthFlow(options, flow) {
|
|
|
30085
30101
|
const { result: response, reauthenticated } = await withAuthRecovery(
|
|
30086
30102
|
() => orgAuthenticatedFetch(isUserScoped ? flow.userEndpoint : flow.orgEndpoint, {
|
|
30087
30103
|
method: "POST",
|
|
30088
|
-
body: flow.toRequestBody(tokens)
|
|
30104
|
+
body: { ...flow.toRequestBody(tokens), share_with_org: target.sharesWithOrganization }
|
|
30089
30105
|
}),
|
|
30090
30106
|
async () => {
|
|
30091
30107
|
const current = await resolveAuthFlowTarget(identity);
|
|
@@ -30103,7 +30119,10 @@ async function runCredentialAuthFlow(options, flow) {
|
|
|
30103
30119
|
if (!response.success) {
|
|
30104
30120
|
throw new Error(response.error || `Failed to upload ${providerLabel} credentials to Replicas`);
|
|
30105
30121
|
}
|
|
30106
|
-
const saved =
|
|
30122
|
+
const saved = {
|
|
30123
|
+
...reauthenticated || !target.organization ? await resolveAuthFlowTarget(identity) : target,
|
|
30124
|
+
sharesWithOrganization: response.sharedWithOrganization === true
|
|
30125
|
+
};
|
|
30107
30126
|
renderAuthFlowSuccess(providerLabel, response, saved);
|
|
30108
30127
|
const completionUrl = new URL("/cli-login/success", process.env.REPLICAS_WEB_URL || DEFAULT_WEB_APP_URL);
|
|
30109
30128
|
completionUrl.searchParams.set("provider", flow.provider);
|