@amerged/ohmyhost-cli 0.1.18 → 0.1.20
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 +1 -1
- package/dist/bin.js +2 -2
- package/dist/{chunk-MLABIGQ6.js → chunk-2VVAKEZO.js} +1292 -296
- package/dist/{chunk-4HFQ54FP.js → chunk-7D2RFLWM.js} +7 -3
- package/dist/credential-store.js +5 -1
- package/dist/index.js +66 -6
- package/package.json +1 -1
- package/src/apps/product-cli/cli.ts +337 -92
- package/src/apps/product-cli/command.ts +111 -6
- package/src/apps/product-cli/composition.ts +748 -120
- package/src/apps/product-cli/credential-store.ts +5 -3
- package/src/apps/product-cli/index.ts +2 -0
- package/src/apps/product-cli/product-api.ts +4 -4
- package/src/apps/product-cli/profile-store.ts +609 -0
- package/src/apps/product-cli/project-link-store.ts +169 -20
- package/src/apps/product-cli/runtime-contract.ts +15 -4
- package/src/apps/product-cli/workspace.ts +75 -13
- package/src/packages/sdk-ts/generated/sdk.gen.ts +1 -1
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
CredentialStoreIntegrityError,
|
|
3
4
|
CredentialStoreUnavailableError,
|
|
5
|
+
LEGACY_CREDENTIAL_ACCOUNT,
|
|
4
6
|
NativeNapiKeyringCredentialStore,
|
|
5
7
|
__commonJS,
|
|
6
8
|
__require,
|
|
7
|
-
__toESM
|
|
8
|
-
|
|
9
|
+
__toESM,
|
|
10
|
+
createNativeEntryFactory
|
|
11
|
+
} from "./chunk-7D2RFLWM.js";
|
|
9
12
|
|
|
10
13
|
// node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
11
14
|
var require_code = __commonJS({
|
|
@@ -14930,7 +14933,398 @@ async function requireIgnoredTokenFile(path) {
|
|
|
14930
14933
|
}
|
|
14931
14934
|
}
|
|
14932
14935
|
|
|
14936
|
+
// apps/product-cli/dist/profile-store.js
|
|
14937
|
+
import { randomUUID } from "node:crypto";
|
|
14938
|
+
import { link, mkdir as mkdir2, open as open2, readFile, readdir, unlink } from "node:fs/promises";
|
|
14939
|
+
import { homedir } from "node:os";
|
|
14940
|
+
import { join } from "node:path";
|
|
14941
|
+
var ProfileNameConflictError = class extends Error {
|
|
14942
|
+
profile;
|
|
14943
|
+
constructor(profile) {
|
|
14944
|
+
super("The profile name is used by another saved login");
|
|
14945
|
+
this.profile = profile;
|
|
14946
|
+
this.name = "ProfileNameConflictError";
|
|
14947
|
+
}
|
|
14948
|
+
};
|
|
14949
|
+
var ProfileLimitError = class extends Error {
|
|
14950
|
+
saved;
|
|
14951
|
+
constructor(saved) {
|
|
14952
|
+
super("The saved logins of this environment reached their limit");
|
|
14953
|
+
this.saved = saved;
|
|
14954
|
+
this.name = "ProfileLimitError";
|
|
14955
|
+
}
|
|
14956
|
+
};
|
|
14957
|
+
var MAX_PROFILES = 64;
|
|
14958
|
+
var MAX_INDEX_ATTEMPTS = 64;
|
|
14959
|
+
var INDEX_FILE = /^index-([1-9][0-9]{0,14})\.json$/u;
|
|
14960
|
+
var SERVICE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
14961
|
+
var PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,62}$/u;
|
|
14962
|
+
var USER_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
|
|
14963
|
+
var ORGANIZATION_ID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
14964
|
+
var isProfileName = (value) => PROFILE_NAME.test(value);
|
|
14965
|
+
var isProfileUserId = (value) => USER_ID.test(value);
|
|
14966
|
+
var profileAccount = (profile) => `profile:${profile.userId}:${profile.organizationId ?? "none"}`;
|
|
14967
|
+
var sameLogin = (left, right) => profileAccount(left) === profileAccount(right);
|
|
14968
|
+
var profileIndexDirectory = (serviceName) => join(homedir(), ".ohmyhost", "profiles", serviceName);
|
|
14969
|
+
var nextProfileIndex = (current, profile, replaces) => [
|
|
14970
|
+
...current.filter((saved) => !sameLogin(saved, profile) && (replaces === void 0 || !sameLogin(saved, replaces))),
|
|
14971
|
+
profile
|
|
14972
|
+
];
|
|
14973
|
+
var admitProfile = (next, profile) => {
|
|
14974
|
+
const conflict = next.find((saved) => saved.name === profile.name && saved !== profile);
|
|
14975
|
+
if (conflict !== void 0)
|
|
14976
|
+
throw new ProfileNameConflictError(conflict);
|
|
14977
|
+
if (next.length > MAX_PROFILES)
|
|
14978
|
+
throw new ProfileLimitError(next.length - 1);
|
|
14979
|
+
};
|
|
14980
|
+
var NativeKeyringProfileRegistry = class {
|
|
14981
|
+
serviceName;
|
|
14982
|
+
#createEntry;
|
|
14983
|
+
#index;
|
|
14984
|
+
constructor(createEntry = void 0, serviceName = "ohmyhost", indexDirectory) {
|
|
14985
|
+
this.serviceName = serviceName;
|
|
14986
|
+
if (!SERVICE_NAME.test(serviceName))
|
|
14987
|
+
throw new CredentialStoreUnavailableError();
|
|
14988
|
+
this.#createEntry = createEntry ?? createNativeEntryFactory(serviceName);
|
|
14989
|
+
this.#index = new ProfileIndexFiles(indexDirectory ?? profileIndexDirectory(serviceName));
|
|
14990
|
+
}
|
|
14991
|
+
async list() {
|
|
14992
|
+
return (await this.#index.read()).profiles;
|
|
14993
|
+
}
|
|
14994
|
+
credential(profile) {
|
|
14995
|
+
return new NativeNapiKeyringCredentialStore(this.#createEntry, this.serviceName, profileAccount(profile));
|
|
14996
|
+
}
|
|
14997
|
+
legacy() {
|
|
14998
|
+
return new NativeNapiKeyringCredentialStore(this.#createEntry, this.serviceName, LEGACY_CREDENTIAL_ACCOUNT);
|
|
14999
|
+
}
|
|
15000
|
+
async save(profile, credential, replaces) {
|
|
15001
|
+
assertProfile(profile);
|
|
15002
|
+
const before = await this.list();
|
|
15003
|
+
admitProfile(nextProfileIndex(before, profile, replaces), profile);
|
|
15004
|
+
await this.credential(profile).replace(credential);
|
|
15005
|
+
try {
|
|
15006
|
+
await this.#index.update((current) => {
|
|
15007
|
+
const next = nextProfileIndex(current, profile, replaces);
|
|
15008
|
+
admitProfile(next, profile);
|
|
15009
|
+
return next;
|
|
15010
|
+
});
|
|
15011
|
+
} catch (error) {
|
|
15012
|
+
if (!before.some((saved) => sameLogin(saved, profile)))
|
|
15013
|
+
await this.credential(profile).clear();
|
|
15014
|
+
throw error;
|
|
15015
|
+
}
|
|
15016
|
+
if (replaces !== void 0 && !sameLogin(replaces, profile))
|
|
15017
|
+
await this.credential(replaces).clear();
|
|
15018
|
+
}
|
|
15019
|
+
async remove(profile) {
|
|
15020
|
+
await this.credential(profile).clear();
|
|
15021
|
+
await this.#index.update((current) => current.some((saved) => sameLogin(saved, profile)) ? current.filter((saved) => !sameLogin(saved, profile)) : void 0);
|
|
15022
|
+
}
|
|
15023
|
+
};
|
|
15024
|
+
var ProfileIndexFiles = class {
|
|
15025
|
+
directory;
|
|
15026
|
+
constructor(directory) {
|
|
15027
|
+
this.directory = directory;
|
|
15028
|
+
}
|
|
15029
|
+
async read() {
|
|
15030
|
+
for (let attempt = 0; attempt < MAX_INDEX_ATTEMPTS; attempt += 1) {
|
|
15031
|
+
const version2 = await this.#newest();
|
|
15032
|
+
if (version2 === 0)
|
|
15033
|
+
return { version: version2, profiles: [] };
|
|
15034
|
+
let serialized;
|
|
15035
|
+
try {
|
|
15036
|
+
serialized = await readFile(this.#file(version2), "utf8");
|
|
15037
|
+
} catch (error) {
|
|
15038
|
+
if (fileErrorCode(error) === "ENOENT")
|
|
15039
|
+
continue;
|
|
15040
|
+
throw new CredentialStoreUnavailableError();
|
|
15041
|
+
}
|
|
15042
|
+
return { version: version2, profiles: parseProfileIndex(serialized) };
|
|
15043
|
+
}
|
|
15044
|
+
throw new CredentialStoreUnavailableError();
|
|
15045
|
+
}
|
|
15046
|
+
/** Applies `change` to the newest index; `undefined` means nothing to change. */
|
|
15047
|
+
async update(change) {
|
|
15048
|
+
try {
|
|
15049
|
+
await mkdir2(this.directory, { recursive: true, mode: 448 });
|
|
15050
|
+
} catch {
|
|
15051
|
+
throw new CredentialStoreUnavailableError();
|
|
15052
|
+
}
|
|
15053
|
+
for (let attempt = 0; attempt < MAX_INDEX_ATTEMPTS; attempt += 1) {
|
|
15054
|
+
const { version: version2, profiles } = await this.read();
|
|
15055
|
+
const next = change(profiles);
|
|
15056
|
+
if (next === void 0)
|
|
15057
|
+
return;
|
|
15058
|
+
const temporary = join(this.directory, `.index-${randomUUID()}.tmp`);
|
|
15059
|
+
try {
|
|
15060
|
+
await writeDurably(temporary, serializeProfileIndex(next));
|
|
15061
|
+
await link(temporary, this.#file(version2 + 1));
|
|
15062
|
+
} catch (error) {
|
|
15063
|
+
if (fileErrorCode(error) === "EEXIST")
|
|
15064
|
+
continue;
|
|
15065
|
+
throw new CredentialStoreUnavailableError();
|
|
15066
|
+
} finally {
|
|
15067
|
+
await removeTemporary(temporary);
|
|
15068
|
+
}
|
|
15069
|
+
if (await this.#newest() !== version2 + 1)
|
|
15070
|
+
continue;
|
|
15071
|
+
await this.#prune(version2);
|
|
15072
|
+
return;
|
|
15073
|
+
}
|
|
15074
|
+
throw new CredentialStoreUnavailableError();
|
|
15075
|
+
}
|
|
15076
|
+
async #newest() {
|
|
15077
|
+
let names;
|
|
15078
|
+
try {
|
|
15079
|
+
names = await readdir(this.directory);
|
|
15080
|
+
} catch (error) {
|
|
15081
|
+
if (fileErrorCode(error) === "ENOENT")
|
|
15082
|
+
return 0;
|
|
15083
|
+
throw new CredentialStoreUnavailableError();
|
|
15084
|
+
}
|
|
15085
|
+
return names.reduce((newest, name) => Math.max(newest, indexVersion(name)), 0);
|
|
15086
|
+
}
|
|
15087
|
+
/**
|
|
15088
|
+
* Keeps the superseded version for readers that listed it just now; older ones go. Cleanup only:
|
|
15089
|
+
* a file left behind is ignored, since readers take the newest version, and another process may
|
|
15090
|
+
* be removing the same files at this moment.
|
|
15091
|
+
*/
|
|
15092
|
+
async #prune(superseded) {
|
|
15093
|
+
let names;
|
|
15094
|
+
try {
|
|
15095
|
+
names = await readdir(this.directory);
|
|
15096
|
+
} catch {
|
|
15097
|
+
return;
|
|
15098
|
+
}
|
|
15099
|
+
for (const name of names) {
|
|
15100
|
+
const version2 = indexVersion(name);
|
|
15101
|
+
if (version2 === 0 || version2 >= superseded)
|
|
15102
|
+
continue;
|
|
15103
|
+
try {
|
|
15104
|
+
await unlink(join(this.directory, name));
|
|
15105
|
+
} catch {
|
|
15106
|
+
}
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
#file(version2) {
|
|
15110
|
+
return join(this.directory, `index-${String(version2)}.json`);
|
|
15111
|
+
}
|
|
15112
|
+
};
|
|
15113
|
+
var indexVersion = (name) => Number(INDEX_FILE.exec(name)?.[1] ?? 0);
|
|
15114
|
+
var removeTemporary = async (path) => {
|
|
15115
|
+
try {
|
|
15116
|
+
await unlink(path);
|
|
15117
|
+
} catch {
|
|
15118
|
+
}
|
|
15119
|
+
};
|
|
15120
|
+
var fileErrorCode = (error) => typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
15121
|
+
var writeDurably = async (path, text) => {
|
|
15122
|
+
const handle = await open2(path, "wx", 384);
|
|
15123
|
+
try {
|
|
15124
|
+
await handle.writeFile(text, "utf8");
|
|
15125
|
+
await handle.sync();
|
|
15126
|
+
} finally {
|
|
15127
|
+
await handle.close();
|
|
15128
|
+
}
|
|
15129
|
+
};
|
|
15130
|
+
var serializeProfileIndex = (profiles) => JSON.stringify({
|
|
15131
|
+
version: 1,
|
|
15132
|
+
profiles: [...profiles].sort((left, right) => left.name < right.name ? -1 : 1).map((profile) => ({
|
|
15133
|
+
name: profile.name,
|
|
15134
|
+
user_id: profile.userId,
|
|
15135
|
+
organization_id: profile.organizationId,
|
|
15136
|
+
organization_name: profile.organizationName
|
|
15137
|
+
}))
|
|
15138
|
+
});
|
|
15139
|
+
var parseProfileIndex = (serialized) => {
|
|
15140
|
+
try {
|
|
15141
|
+
const value = JSON.parse(serialized);
|
|
15142
|
+
if (!isRecord(value, ["version", "profiles"]) || value["version"] !== 1)
|
|
15143
|
+
throw new Error("Invalid profile index");
|
|
15144
|
+
const entries = value["profiles"];
|
|
15145
|
+
if (!Array.isArray(entries) || entries.length > MAX_PROFILES)
|
|
15146
|
+
throw new Error("Invalid profile index");
|
|
15147
|
+
const profiles = entries.map((entry) => {
|
|
15148
|
+
if (!isRecord(entry, ["name", "user_id", "organization_id", "organization_name"]))
|
|
15149
|
+
throw new Error("Invalid profile");
|
|
15150
|
+
const profile = {
|
|
15151
|
+
name: entry["name"],
|
|
15152
|
+
userId: entry["user_id"],
|
|
15153
|
+
organizationId: entry["organization_id"],
|
|
15154
|
+
organizationName: entry["organization_name"]
|
|
15155
|
+
};
|
|
15156
|
+
assertProfile(profile);
|
|
15157
|
+
return profile;
|
|
15158
|
+
});
|
|
15159
|
+
if (new Set(profiles.map((profile) => profile.name)).size !== profiles.length || new Set(profiles.map(profileAccount)).size !== profiles.length)
|
|
15160
|
+
throw new Error("Duplicate profile");
|
|
15161
|
+
return profiles;
|
|
15162
|
+
} catch {
|
|
15163
|
+
throw new CredentialStoreIntegrityError();
|
|
15164
|
+
}
|
|
15165
|
+
};
|
|
15166
|
+
var assertProfile = (profile) => {
|
|
15167
|
+
if (typeof profile.name !== "string" || !PROFILE_NAME.test(profile.name) || typeof profile.userId !== "string" || !USER_ID.test(profile.userId) || !(profile.organizationId === null || typeof profile.organizationId === "string" && ORGANIZATION_ID.test(profile.organizationId)) || !(profile.organizationName === null || typeof profile.organizationName === "string" && profile.organizationName.length > 0 && profile.organizationName.length <= 128 && !new RegExp("\\p{Cc}", "u").test(profile.organizationName)) || profile.organizationId === null && profile.organizationName !== null)
|
|
15168
|
+
throw new CredentialStoreIntegrityError();
|
|
15169
|
+
};
|
|
15170
|
+
var isRecord = (value, keys) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).sort().join(",") === [...keys].sort().join(",");
|
|
15171
|
+
var selectProfile = (profiles, request) => {
|
|
15172
|
+
if (request.name !== void 0 && request.bound !== void 0 && request.name !== request.bound)
|
|
15173
|
+
return { outcome: "conflict", field: "binding", profile: null };
|
|
15174
|
+
const named = request.name ?? request.bound;
|
|
15175
|
+
if (named !== void 0) {
|
|
15176
|
+
const profile = profiles.find((saved) => saved.name === named);
|
|
15177
|
+
if (profile === void 0)
|
|
15178
|
+
return { outcome: "not_found" };
|
|
15179
|
+
if (request.organizationId !== void 0 && profile.organizationId !== request.organizationId)
|
|
15180
|
+
return { outcome: "conflict", field: "organization", profile };
|
|
15181
|
+
if (request.userId !== void 0 && profile.userId !== request.userId)
|
|
15182
|
+
return { outcome: "conflict", field: "user", profile };
|
|
15183
|
+
return { outcome: "selected", profile };
|
|
15184
|
+
}
|
|
15185
|
+
const candidates = profiles.filter((profile) => (request.organizationId === void 0 || profile.organizationId === request.organizationId) && (request.userId === void 0 || profile.userId === request.userId));
|
|
15186
|
+
const only = candidates[0];
|
|
15187
|
+
if (candidates.length === 1 && only !== void 0)
|
|
15188
|
+
return { outcome: "selected", profile: only };
|
|
15189
|
+
if (candidates.length > 1)
|
|
15190
|
+
return { outcome: "ambiguous", candidates };
|
|
15191
|
+
return profiles.length === 0 ? { outcome: "none" } : { outcome: "not_found" };
|
|
15192
|
+
};
|
|
15193
|
+
var selectProfileForOrganization = (profiles, request) => {
|
|
15194
|
+
const user = request.userId === void 0 ? {} : { userId: request.userId };
|
|
15195
|
+
if (request.name !== void 0 || request.bound !== void 0)
|
|
15196
|
+
return selectProfile(profiles, {
|
|
15197
|
+
...request.name === void 0 ? {} : { name: request.name },
|
|
15198
|
+
...request.bound === void 0 ? {} : { bound: request.bound },
|
|
15199
|
+
...user
|
|
15200
|
+
});
|
|
15201
|
+
const candidates = profiles.filter((profile) => request.userId === void 0 || profile.userId === request.userId);
|
|
15202
|
+
const inOrganization = candidates.filter((profile) => profile.organizationId === request.organizationId);
|
|
15203
|
+
if (inOrganization.length > 1)
|
|
15204
|
+
return { outcome: "ambiguous", candidates: inOrganization };
|
|
15205
|
+
const unscoped = candidates.filter((profile) => profile.organizationId === null);
|
|
15206
|
+
const [member] = inOrganization;
|
|
15207
|
+
if (member !== void 0)
|
|
15208
|
+
return { outcome: "selected", profile: member };
|
|
15209
|
+
const [open5] = unscoped;
|
|
15210
|
+
if (unscoped.length === 1 && open5 !== void 0)
|
|
15211
|
+
return { outcome: "selected", profile: open5 };
|
|
15212
|
+
return selectProfile(profiles, user);
|
|
15213
|
+
};
|
|
15214
|
+
var defaultProfileName = (profiles, login) => {
|
|
15215
|
+
const same = profiles.find((profile) => profile.userId === login.userId && profile.organizationId === login.organizationId);
|
|
15216
|
+
if (same !== void 0)
|
|
15217
|
+
return same.name;
|
|
15218
|
+
const base = slug(login.organizationName ?? "") || (login.organizationId === null ? "account" : "workspace");
|
|
15219
|
+
const free = (name) => profiles.every((profile) => profile.name !== name);
|
|
15220
|
+
if (free(base))
|
|
15221
|
+
return base;
|
|
15222
|
+
const suffix = (login.organizationId ?? login.userId).replace(/[^A-Za-z0-9]/gu, "").slice(-6).toLowerCase();
|
|
15223
|
+
for (let attempt = 1; attempt <= profiles.length + 1; attempt += 1) {
|
|
15224
|
+
const candidate = `${base.slice(0, 48)}-${suffix}${attempt === 1 ? "" : `-${String(attempt)}`}`;
|
|
15225
|
+
if (free(candidate))
|
|
15226
|
+
return candidate;
|
|
15227
|
+
}
|
|
15228
|
+
throw new CredentialStoreIntegrityError();
|
|
15229
|
+
};
|
|
15230
|
+
var slug = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+/u, "").slice(0, 40).replace(/-+$/u, "");
|
|
15231
|
+
var publicProfile = (profile) => ({
|
|
15232
|
+
name: profile.name,
|
|
15233
|
+
user_id: profile.userId,
|
|
15234
|
+
organization_id: profile.organizationId,
|
|
15235
|
+
organization_name: profile.organizationName
|
|
15236
|
+
});
|
|
15237
|
+
var profileLimitAction = (commandPrefix) => `Nothing was saved: this environment already keeps ${String(MAX_PROFILES)} saved logins, the most one computer holds. Remove one you no longer need with '${commandPrefix} logout --profile-name NAME --json' ('${commandPrefix} profile list --json' shows them), then retry.`;
|
|
15238
|
+
var ProfileSelectionError = class extends Error {
|
|
15239
|
+
code;
|
|
15240
|
+
exitCode;
|
|
15241
|
+
suggestedAction;
|
|
15242
|
+
constructor(code, exitCode, suggestedAction) {
|
|
15243
|
+
super(`${code}: ${suggestedAction}`);
|
|
15244
|
+
this.code = code;
|
|
15245
|
+
this.exitCode = exitCode;
|
|
15246
|
+
this.suggestedAction = suggestedAction;
|
|
15247
|
+
this.name = "ProfileSelectionError";
|
|
15248
|
+
}
|
|
15249
|
+
};
|
|
15250
|
+
var profileSelectionError = (selection, request, commandPrefix, selector) => {
|
|
15251
|
+
const choose = selector === "cli" ? `pass --profile-name NAME (see '${commandPrefix} profile list --json') or set OHMYHOST_PROFILE for this process` : "pass profile_name (see profile_list) or set OHMYHOST_PROFILE in this MCP server's configuration";
|
|
15252
|
+
const login = `${commandPrefix} login${request.organizationId === void 0 ? "" : ` --organization ${request.organizationId}`}${request.userId === void 0 ? "" : ` --user ${request.userId}`}${(request.name ?? request.bound) === void 0 ? "" : ` --profile-name ${String(request.name ?? request.bound)}`} --json`;
|
|
15253
|
+
if (selection.outcome === "none")
|
|
15254
|
+
return new ProfileSelectionError("authentication_required", 5, `No login is saved on this computer. Run '${login}'.`);
|
|
15255
|
+
if (selection.outcome === "not_found")
|
|
15256
|
+
return new ProfileSelectionError("profile_not_found", 5, `No saved login matches ${describeRequest(request)}. Run '${login}' to add it, signing in as that user; '${commandPrefix} profile list --json' shows the saved logins.`);
|
|
15257
|
+
if (selection.outcome === "ambiguous")
|
|
15258
|
+
return new ProfileSelectionError("profile_selection_required", 2, `Several saved logins could run this: ${selection.candidates.map((profile2) => profile2.name).join(", ")}. Ask the customer which account to use, then ${choose}.`);
|
|
15259
|
+
if (selection.field === "binding")
|
|
15260
|
+
return new ProfileSelectionError("profile_context_mismatch", 2, `This call names profile ${String(request.name)}, but the process is bound to OHMYHOST_PROFILE=${String(request.bound)}. Use the bound profile or start a process for the other one.`);
|
|
15261
|
+
const profile = selection.profile;
|
|
15262
|
+
return new ProfileSelectionError("profile_context_mismatch", 2, selection.field === "organization" ? `Profile ${String(profile?.name)} is signed in to organization ${String(profile?.organizationId ?? "none")}, but this targets organization ${String(request.organizationId)}. Use the profile of that organization or change the target; nothing was sent.` : `Profile ${String(profile?.name)} belongs to user ${String(profile?.userId)}, not ${String(request.userId)}. Use that user's profile; nothing was sent.`);
|
|
15263
|
+
};
|
|
15264
|
+
var describeRequest = (request) => {
|
|
15265
|
+
const parts = [
|
|
15266
|
+
...(request.name ?? request.bound) === void 0 ? [] : [`profile ${String(request.name ?? request.bound)}`],
|
|
15267
|
+
...request.organizationId === void 0 ? [] : [`organization ${request.organizationId}`],
|
|
15268
|
+
...request.userId === void 0 ? [] : [`user ${request.userId}`]
|
|
15269
|
+
];
|
|
15270
|
+
return parts.length === 0 ? "this request" : parts.join(", ");
|
|
15271
|
+
};
|
|
15272
|
+
var describeProfile = (profile) => `profile ${profile.name} (user ${profile.userId}, ${profile.organizationId === null ? "no organization" : `organization ${profile.organizationName === null ? "" : `${profile.organizationName} `}${profile.organizationId}`})`;
|
|
15273
|
+
|
|
15274
|
+
// packages/workos-auth-contracts/dist/parsing.js
|
|
15275
|
+
var WorkosContractError = class extends Error {
|
|
15276
|
+
code;
|
|
15277
|
+
constructor(code, message) {
|
|
15278
|
+
super(message);
|
|
15279
|
+
this.code = code;
|
|
15280
|
+
this.name = "WorkosContractError";
|
|
15281
|
+
}
|
|
15282
|
+
};
|
|
15283
|
+
var expectRecord = (value, code = "invalid_object") => {
|
|
15284
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
15285
|
+
throw new WorkosContractError(code, "WorkOS response must be an object");
|
|
15286
|
+
}
|
|
15287
|
+
return value;
|
|
15288
|
+
};
|
|
15289
|
+
function expectString(value, field, options = {}) {
|
|
15290
|
+
if (value === void 0 && options.optional === true)
|
|
15291
|
+
return void 0;
|
|
15292
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
15293
|
+
throw new WorkosContractError("invalid_field", `WorkOS field ${field} must be a non-empty string`);
|
|
15294
|
+
}
|
|
15295
|
+
return value;
|
|
15296
|
+
}
|
|
15297
|
+
function expectPositiveInteger(value, field, options = {}) {
|
|
15298
|
+
if (value === void 0 && options.optional === true)
|
|
15299
|
+
return void 0;
|
|
15300
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
15301
|
+
throw new WorkosContractError("invalid_field", `WorkOS field ${field} must be positive`);
|
|
15302
|
+
}
|
|
15303
|
+
return value;
|
|
15304
|
+
}
|
|
15305
|
+
var parseJsonObject = (body) => {
|
|
15306
|
+
try {
|
|
15307
|
+
return expectRecord(JSON.parse(body));
|
|
15308
|
+
} catch (error) {
|
|
15309
|
+
if (error instanceof WorkosContractError)
|
|
15310
|
+
throw error;
|
|
15311
|
+
throw new WorkosContractError("invalid_json", "WorkOS returned invalid JSON");
|
|
15312
|
+
}
|
|
15313
|
+
};
|
|
15314
|
+
|
|
15315
|
+
// packages/workos-auth-contracts/dist/user-api-keys.js
|
|
15316
|
+
var WORKOS_USER_API_KEY_PATTERN = /^sk_[A-Za-z0-9_-]{20,128}$/u;
|
|
15317
|
+
|
|
14933
15318
|
// apps/product-cli/dist/command.js
|
|
15319
|
+
var extractProfileName = (argv) => {
|
|
15320
|
+
const index = argv.indexOf("--profile-name");
|
|
15321
|
+
if (index < 0)
|
|
15322
|
+
return { argv };
|
|
15323
|
+
const value = argv[index + 1];
|
|
15324
|
+
if (value === void 0 || !isProfileName(value) || argv.indexOf("--profile-name", index + 1) >= 0)
|
|
15325
|
+
throw new InvalidCommandError();
|
|
15326
|
+
return { argv: [...argv.slice(0, index), ...argv.slice(index + 2)], profileName: value };
|
|
15327
|
+
};
|
|
14934
15328
|
var InvalidCommandError = class extends Error {
|
|
14935
15329
|
constructor() {
|
|
14936
15330
|
super("The command arguments are invalid.");
|
|
@@ -14945,11 +15339,17 @@ var recognizedCommandLabel = (argv) => {
|
|
|
14945
15339
|
return "version";
|
|
14946
15340
|
while (values.length > 0) {
|
|
14947
15341
|
const value = values.shift();
|
|
15342
|
+
if (value === "--profile-name") {
|
|
15343
|
+
values.shift();
|
|
15344
|
+
continue;
|
|
15345
|
+
}
|
|
14948
15346
|
if (value === "--json" || value === "--dry-run" || value === "--revoke" || value === "--yes" || value === "--wait" || value === "--follow" || value === "--stdin") {
|
|
14949
15347
|
continue;
|
|
14950
15348
|
}
|
|
14951
15349
|
if (value === "help" || value === "version" || value === "whoami" || value === "init" || value === "login" || value === "logout")
|
|
14952
15350
|
return value;
|
|
15351
|
+
if (value === "profile" && values[0] === "list")
|
|
15352
|
+
return "profile list";
|
|
14953
15353
|
if (value === "github" && values[0] === "connect")
|
|
14954
15354
|
return "github connect";
|
|
14955
15355
|
if (value === "github" && values[0] === "status")
|
|
@@ -15159,16 +15559,20 @@ var parseProductCliCommand = (argv) => {
|
|
|
15159
15559
|
if (dryRun)
|
|
15160
15560
|
throw new InvalidCommandError();
|
|
15161
15561
|
if (first === "login" && !revoke && !yes && !wait && !follow && !stdin) {
|
|
15162
|
-
const options = parseOptionalOptions(values, ["--organization"]);
|
|
15562
|
+
const options = parseOptionalOptions(values, ["--organization", "--user"]);
|
|
15163
15563
|
const organizationId = options["--organization"];
|
|
15164
|
-
|
|
15564
|
+
const userId = options["--user"];
|
|
15565
|
+
if (organizationId !== void 0 && !ORGANIZATION_ULID.test(organizationId) || userId !== void 0 && !isProfileUserId(userId))
|
|
15165
15566
|
throw new InvalidCommandError();
|
|
15166
15567
|
return {
|
|
15167
15568
|
kind: "login",
|
|
15168
15569
|
...organizationId === void 0 ? {} : { organizationId },
|
|
15570
|
+
...userId === void 0 ? {} : { userId },
|
|
15169
15571
|
credentialStore
|
|
15170
15572
|
};
|
|
15171
15573
|
}
|
|
15574
|
+
if (first === "profile" && values.length === 1 && values[0] === "list" && !revoke && !yes && !wait && !follow && !stdin)
|
|
15575
|
+
return { kind: "profile-list" };
|
|
15172
15576
|
if (first === "logout") {
|
|
15173
15577
|
if (!yes && !wait && !follow && !stdin && values.length === 0)
|
|
15174
15578
|
return { kind: "logout", revoke, credentialStore };
|
|
@@ -16092,15 +16496,32 @@ var parseProductCliCommand = (argv) => {
|
|
|
16092
16496
|
const name = values.shift();
|
|
16093
16497
|
if (!isUserSecretName(name))
|
|
16094
16498
|
throw new InvalidCommandError();
|
|
16095
|
-
const options =
|
|
16499
|
+
const options = parseOptionalOptions(values, [
|
|
16500
|
+
"--project",
|
|
16501
|
+
"--environment",
|
|
16502
|
+
"--idempotency-key",
|
|
16503
|
+
"--token-user",
|
|
16504
|
+
"--token-organization",
|
|
16505
|
+
"--profile-user",
|
|
16506
|
+
"--profile-organization"
|
|
16507
|
+
]);
|
|
16508
|
+
const projectId = options["--project"];
|
|
16509
|
+
const environmentId = options["--environment"];
|
|
16510
|
+
const idempotencyKey = options["--idempotency-key"];
|
|
16511
|
+
const environmentToken2 = handedAccount(options["--token-user"], options["--token-organization"]);
|
|
16512
|
+
const profileAccount2 = handedAccount(options["--profile-user"], options["--profile-organization"]);
|
|
16513
|
+
if (projectId === void 0 || environmentId === void 0 || idempotencyKey === void 0 || environmentToken2 !== void 0 && profileAccount2 !== void 0)
|
|
16514
|
+
throw new InvalidCommandError();
|
|
16096
16515
|
return {
|
|
16097
16516
|
kind: "secret-set",
|
|
16098
|
-
projectId
|
|
16099
|
-
environmentId
|
|
16517
|
+
projectId,
|
|
16518
|
+
environmentId,
|
|
16100
16519
|
name,
|
|
16101
|
-
idempotencyKey
|
|
16520
|
+
idempotencyKey,
|
|
16102
16521
|
stdin,
|
|
16103
16522
|
wait,
|
|
16523
|
+
...environmentToken2 === void 0 ? {} : { environmentToken: environmentToken2 },
|
|
16524
|
+
...profileAccount2 === void 0 ? {} : { profileAccount: profileAccount2 },
|
|
16104
16525
|
credentialStore
|
|
16105
16526
|
};
|
|
16106
16527
|
}
|
|
@@ -16349,6 +16770,13 @@ var accessTtlSeconds = (value) => {
|
|
|
16349
16770
|
return seconds;
|
|
16350
16771
|
};
|
|
16351
16772
|
var ORGANIZATION_ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
16773
|
+
var handedAccount = (userId, organizationId) => {
|
|
16774
|
+
if (userId === void 0 && organizationId === void 0)
|
|
16775
|
+
return void 0;
|
|
16776
|
+
if (userId === void 0 || organizationId === void 0 || !isProfileUserId(userId) || WORKOS_USER_API_KEY_PATTERN.test(userId) || !ORGANIZATION_ULID.test(organizationId))
|
|
16777
|
+
throw new InvalidCommandError();
|
|
16778
|
+
return { userId, organizationId };
|
|
16779
|
+
};
|
|
16352
16780
|
var isValue = (value) => value !== void 0 && value.length > 0 && !value.startsWith("--");
|
|
16353
16781
|
var validGithubBranchName = (value) => !(value.length < 1 || value.length > 1024 || !/^[A-Za-z0-9]/u.test(value) || value.startsWith("-") || value.startsWith("/") || value.endsWith("/") || value.endsWith(".") || value.includes("//") || value.includes("..") || value.includes("@{") || [...value].some((character) => {
|
|
16354
16782
|
const code = character.codePointAt(0) ?? 0;
|
|
@@ -16357,46 +16785,6 @@ var validGithubBranchName = (value) => !(value.length < 1 || value.length > 1024
|
|
|
16357
16785
|
var validPaidHostname = (value) => /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(value) && !value.endsWith(".poc.aixyte.com") && value !== "omh.st" && !value.endsWith(".omh.st") && value !== "ohmyho.st" && !value.endsWith(".ohmyho.st");
|
|
16358
16786
|
var isUserSecretName = (value) => isValue(value) && /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) && value !== "DATABASE_URL" && value !== "HYPERDRIVE" && value !== "BETTER_AUTH_SECRET" && value !== "BETTER_AUTH_URL" && value !== "OHMYHOST_MAIL_KEY" && value !== "OHMYHOST_MAIL_GATEWAY_URL";
|
|
16359
16787
|
|
|
16360
|
-
// apps/product-cli/dist/workspace.js
|
|
16361
|
-
var InteractiveSessionRequiredError = class extends Error {
|
|
16362
|
-
constructor() {
|
|
16363
|
-
super("Workspace selection requires an interactive login");
|
|
16364
|
-
this.name = "InteractiveSessionRequiredError";
|
|
16365
|
-
}
|
|
16366
|
-
};
|
|
16367
|
-
var chooseWorkspace = (organizations, requested) => {
|
|
16368
|
-
if (requested !== void 0) {
|
|
16369
|
-
const match = organizations.find((organization) => organization.id === requested);
|
|
16370
|
-
return match === void 0 ? { outcome: "not_a_member", organizations } : { outcome: "selected", organization: match };
|
|
16371
|
-
}
|
|
16372
|
-
if (organizations.length === 0)
|
|
16373
|
-
return { outcome: "no_workspace" };
|
|
16374
|
-
const only = organizations[0];
|
|
16375
|
-
if (organizations.length === 1 && only !== void 0)
|
|
16376
|
-
return { outcome: "selected", organization: only };
|
|
16377
|
-
return { outcome: "choice_required", organizations };
|
|
16378
|
-
};
|
|
16379
|
-
var selectWorkspace = async (session, organization, signal) => {
|
|
16380
|
-
const stored = await session.store.read();
|
|
16381
|
-
if (stored === void 0)
|
|
16382
|
-
throw new InteractiveSessionRequiredError();
|
|
16383
|
-
const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
|
|
16384
|
-
await session.store.replace({
|
|
16385
|
-
...refreshed,
|
|
16386
|
-
workosOrganizationId: organization.workos_id,
|
|
16387
|
-
organizationId: organization.id
|
|
16388
|
-
});
|
|
16389
|
-
};
|
|
16390
|
-
var selectedWorkspaceId = async (session) => (await session.store.read())?.organizationId ?? null;
|
|
16391
|
-
var workspaceOf = (credential) => ({
|
|
16392
|
-
...credential.workosOrganizationId === void 0 ? {} : { workosOrganizationId: credential.workosOrganizationId },
|
|
16393
|
-
...credential.organizationId === void 0 ? {} : { organizationId: credential.organizationId }
|
|
16394
|
-
});
|
|
16395
|
-
var publicWorkspace = (organization) => ({
|
|
16396
|
-
id: organization.id,
|
|
16397
|
-
name: organization.name
|
|
16398
|
-
});
|
|
16399
|
-
|
|
16400
16788
|
// packages/contracts/dist/feedback.js
|
|
16401
16789
|
var FEEDBACK_HISTORY_PAGE_SIZE = 25;
|
|
16402
16790
|
var FEEDBACK_REPLY_MAX_LENGTH = 2e3;
|
|
@@ -16923,7 +17311,7 @@ var PROBLEM_POLICY = {
|
|
|
16923
17311
|
github_connection_required: {
|
|
16924
17312
|
status: 409,
|
|
16925
17313
|
retryable: false,
|
|
16926
|
-
action: "Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for
|
|
17314
|
+
action: "This workspace has no GitHub connection yet. Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for it, complete its browser link, then repeat the original source link. If the project belongs to another account, use that account's login instead; error.acting_as names the login this ran as."
|
|
16927
17315
|
},
|
|
16928
17316
|
github_connection_revoked: {
|
|
16929
17317
|
status: 409,
|
|
@@ -16997,7 +17385,11 @@ var PROBLEM_POLICY = {
|
|
|
16997
17385
|
retryable: false,
|
|
16998
17386
|
action: "Run 'ohmyhost organization list --json' and select one with 'ohmyhost organization use --organization ULID --json', or create the first workspace."
|
|
16999
17387
|
},
|
|
17000
|
-
resource_not_found: {
|
|
17388
|
+
resource_not_found: {
|
|
17389
|
+
status: 404,
|
|
17390
|
+
retryable: false,
|
|
17391
|
+
action: "Check the resource identifier and the account: a project is visible only to its own organization, and error.acting_as names the login this ran as. Use the login of the account that owns it."
|
|
17392
|
+
},
|
|
17001
17393
|
idempotency_key_reused: {
|
|
17002
17394
|
status: 409,
|
|
17003
17395
|
retryable: false,
|
|
@@ -17488,7 +17880,7 @@ var parseAccountProfile = (value) => {
|
|
|
17488
17880
|
});
|
|
17489
17881
|
if (new Set(workspaces.map((workspace) => workspace.id)).size !== organizationIds.length)
|
|
17490
17882
|
throw new ResponseContractError();
|
|
17491
|
-
return workspaces;
|
|
17883
|
+
return { userId: input["user_id"], organizations: workspaces };
|
|
17492
17884
|
};
|
|
17493
17885
|
var parseProject = (value) => {
|
|
17494
17886
|
const input = exactRecord2(value, [
|
|
@@ -17842,7 +18234,7 @@ var parseOperation = (value) => {
|
|
|
17842
18234
|
const blockingOperationId = input["blocking_operation_id"];
|
|
17843
18235
|
if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3))
|
|
17844
18236
|
throw new ResponseContractError();
|
|
17845
|
-
if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !
|
|
18237
|
+
if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !isRecord2(result)) {
|
|
17846
18238
|
throw new ResponseContractError();
|
|
17847
18239
|
}
|
|
17848
18240
|
let progress;
|
|
@@ -17903,12 +18295,12 @@ function parseOperationFailure(value) {
|
|
|
17903
18295
|
return safe;
|
|
17904
18296
|
}
|
|
17905
18297
|
var exactRecord2 = (value, allowed) => {
|
|
17906
|
-
if (!
|
|
18298
|
+
if (!isRecord2(value) || Object.keys(value).some((key) => !allowed.includes(key))) {
|
|
17907
18299
|
throw new ResponseContractError();
|
|
17908
18300
|
}
|
|
17909
18301
|
return value;
|
|
17910
18302
|
};
|
|
17911
|
-
var
|
|
18303
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17912
18304
|
var boundedString = (value, minimum, maximum) => typeof value === "string" && value.length >= minimum && value.length <= maximum;
|
|
17913
18305
|
var isUlid = (value) => typeof value === "string" && /^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value);
|
|
17914
18306
|
var isIsoInstant = (value) => typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
@@ -17936,8 +18328,81 @@ var isPaidValidationRecord = (value) => {
|
|
|
17936
18328
|
};
|
|
17937
18329
|
var isHttpsUrlValue = (value) => typeof value === "string" && isHttpsUrl(value);
|
|
17938
18330
|
|
|
18331
|
+
// apps/product-cli/dist/workspace.js
|
|
18332
|
+
var InteractiveSessionRequiredError = class extends Error {
|
|
18333
|
+
constructor() {
|
|
18334
|
+
super("Workspace selection requires an interactive login");
|
|
18335
|
+
this.name = "InteractiveSessionRequiredError";
|
|
18336
|
+
}
|
|
18337
|
+
};
|
|
18338
|
+
var ProfileOrganizationFixedError = class extends Error {
|
|
18339
|
+
profile;
|
|
18340
|
+
organization;
|
|
18341
|
+
constructor(profile, organization) {
|
|
18342
|
+
super("The saved login is bound to another organization");
|
|
18343
|
+
this.profile = profile;
|
|
18344
|
+
this.organization = organization;
|
|
18345
|
+
this.name = "ProfileOrganizationFixedError";
|
|
18346
|
+
}
|
|
18347
|
+
};
|
|
18348
|
+
var AccountVerificationError = class extends Error {
|
|
18349
|
+
constructor() {
|
|
18350
|
+
super("The signed-in account does not match the requested one");
|
|
18351
|
+
this.name = "AccountVerificationError";
|
|
18352
|
+
}
|
|
18353
|
+
};
|
|
18354
|
+
var chooseWorkspace = (organizations, requested) => {
|
|
18355
|
+
if (requested !== void 0) {
|
|
18356
|
+
const match = organizations.find((organization) => organization.id === requested);
|
|
18357
|
+
return match === void 0 ? { outcome: "not_a_member", organizations } : { outcome: "selected", organization: match };
|
|
18358
|
+
}
|
|
18359
|
+
if (organizations.length === 0)
|
|
18360
|
+
return { outcome: "no_workspace" };
|
|
18361
|
+
const only = organizations[0];
|
|
18362
|
+
if (organizations.length === 1 && only !== void 0)
|
|
18363
|
+
return { outcome: "selected", organization: only };
|
|
18364
|
+
return { outcome: "choice_required", organizations };
|
|
18365
|
+
};
|
|
18366
|
+
var verifiedUserId = (identity) => {
|
|
18367
|
+
const actor = parseCurrentIdentity(identity).actor_id;
|
|
18368
|
+
if (!actor.startsWith("user:") || actor.length === 5)
|
|
18369
|
+
throw new AccountVerificationError();
|
|
18370
|
+
return actor.slice(5);
|
|
18371
|
+
};
|
|
18372
|
+
var assertVerifiedLogin = (identity, userId, organizationId) => {
|
|
18373
|
+
const organizations = parseCurrentIdentity(identity).organization_ids;
|
|
18374
|
+
if (verifiedUserId(identity) !== userId || (organizationId === null ? organizations.length !== 0 : organizations.length !== 1 || organizations[0] !== organizationId))
|
|
18375
|
+
throw new AccountVerificationError();
|
|
18376
|
+
};
|
|
18377
|
+
var selectWorkspace = async (session, organization, signal, identity) => {
|
|
18378
|
+
if (session.profile.organizationId === organization.id)
|
|
18379
|
+
return session.profile;
|
|
18380
|
+
if (session.profile.organizationId !== null)
|
|
18381
|
+
throw new ProfileOrganizationFixedError(session.profile, organization);
|
|
18382
|
+
const stored = await session.store.read();
|
|
18383
|
+
if (stored === void 0)
|
|
18384
|
+
throw new InteractiveSessionRequiredError();
|
|
18385
|
+
const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
|
|
18386
|
+
assertVerifiedLogin(await identity(refreshed.accessToken), session.profile.userId, organization.id);
|
|
18387
|
+
const bound = {
|
|
18388
|
+
...session.profile,
|
|
18389
|
+
organizationId: organization.id,
|
|
18390
|
+
organizationName: organization.name
|
|
18391
|
+
};
|
|
18392
|
+
await session.profiles.save(bound, { ...refreshed, workosOrganizationId: organization.workos_id, organizationId: organization.id }, session.profile);
|
|
18393
|
+
return bound;
|
|
18394
|
+
};
|
|
18395
|
+
var workspaceOf = (credential) => ({
|
|
18396
|
+
...credential.workosOrganizationId === void 0 ? {} : { workosOrganizationId: credential.workosOrganizationId },
|
|
18397
|
+
...credential.organizationId === void 0 ? {} : { organizationId: credential.organizationId }
|
|
18398
|
+
});
|
|
18399
|
+
var publicWorkspace = (organization) => ({
|
|
18400
|
+
id: organization.id,
|
|
18401
|
+
name: organization.name
|
|
18402
|
+
});
|
|
18403
|
+
|
|
17939
18404
|
// apps/product-cli/dist/cli.js
|
|
17940
|
-
import { open as
|
|
18405
|
+
import { open as open3 } from "node:fs/promises";
|
|
17941
18406
|
|
|
17942
18407
|
// packages/contracts/dist/database-access.js
|
|
17943
18408
|
function parseProjectDatabaseWrite(value) {
|
|
@@ -18089,7 +18554,7 @@ function exact2(v, keys) {
|
|
|
18089
18554
|
// apps/product-cli/package.json
|
|
18090
18555
|
var package_default = {
|
|
18091
18556
|
name: "@ohmyhost/product-cli",
|
|
18092
|
-
version: "0.1.
|
|
18557
|
+
version: "0.1.20",
|
|
18093
18558
|
private: true,
|
|
18094
18559
|
ohmyhost: {
|
|
18095
18560
|
deployment: "production",
|
|
@@ -18259,47 +18724,6 @@ function assertRecharge(value) {
|
|
|
18259
18724
|
assertBillingTaxIssue(v["billing_issue"]);
|
|
18260
18725
|
}
|
|
18261
18726
|
|
|
18262
|
-
// packages/workos-auth-contracts/dist/parsing.js
|
|
18263
|
-
var WorkosContractError = class extends Error {
|
|
18264
|
-
code;
|
|
18265
|
-
constructor(code, message) {
|
|
18266
|
-
super(message);
|
|
18267
|
-
this.code = code;
|
|
18268
|
-
this.name = "WorkosContractError";
|
|
18269
|
-
}
|
|
18270
|
-
};
|
|
18271
|
-
var expectRecord = (value, code = "invalid_object") => {
|
|
18272
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
18273
|
-
throw new WorkosContractError(code, "WorkOS response must be an object");
|
|
18274
|
-
}
|
|
18275
|
-
return value;
|
|
18276
|
-
};
|
|
18277
|
-
function expectString(value, field, options = {}) {
|
|
18278
|
-
if (value === void 0 && options.optional === true)
|
|
18279
|
-
return void 0;
|
|
18280
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
18281
|
-
throw new WorkosContractError("invalid_field", `WorkOS field ${field} must be a non-empty string`);
|
|
18282
|
-
}
|
|
18283
|
-
return value;
|
|
18284
|
-
}
|
|
18285
|
-
function expectPositiveInteger(value, field, options = {}) {
|
|
18286
|
-
if (value === void 0 && options.optional === true)
|
|
18287
|
-
return void 0;
|
|
18288
|
-
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
18289
|
-
throw new WorkosContractError("invalid_field", `WorkOS field ${field} must be positive`);
|
|
18290
|
-
}
|
|
18291
|
-
return value;
|
|
18292
|
-
}
|
|
18293
|
-
var parseJsonObject = (body) => {
|
|
18294
|
-
try {
|
|
18295
|
-
return expectRecord(JSON.parse(body));
|
|
18296
|
-
} catch (error) {
|
|
18297
|
-
if (error instanceof WorkosContractError)
|
|
18298
|
-
throw error;
|
|
18299
|
-
throw new WorkosContractError("invalid_json", "WorkOS returned invalid JSON");
|
|
18300
|
-
}
|
|
18301
|
-
};
|
|
18302
|
-
|
|
18303
18727
|
// apps/product-cli/dist/device-token-expiry.js
|
|
18304
18728
|
var DeviceTokenExpiryError = class extends Error {
|
|
18305
18729
|
constructor() {
|
|
@@ -18420,9 +18844,10 @@ var PsqlUnavailableError = class extends Error {
|
|
|
18420
18844
|
var CLI_VERSION = package_default.version;
|
|
18421
18845
|
var HELP_USAGE = Object.freeze({
|
|
18422
18846
|
init: "ohmyhost init [--directory PATH] [--root PATH] [--project SLUG] [--region us|eu] [--dry-run] --json (pass the project's hosting region so storage.jurisdiction matches it; us when omitted)",
|
|
18423
|
-
login: "ohmyhost login [--organization ULID] --json",
|
|
18424
|
-
logout: "ohmyhost logout [--revoke] --json",
|
|
18425
|
-
whoami: "ohmyhost whoami --json",
|
|
18847
|
+
login: "ohmyhost login [--organization ULID] [--user USER_ID] [--profile-name NAME] --json (adds one saved login; nothing is saved unless the browser signed in as that user and organization)",
|
|
18848
|
+
logout: "ohmyhost logout [--profile-name NAME] [--revoke] --json (removes only the selected saved login)",
|
|
18849
|
+
whoami: "ohmyhost whoami [--profile-name NAME] --json (the effective user, organization and saved login)",
|
|
18850
|
+
"profile list": "ohmyhost profile list --json (saved logins on this computer: name, user and organization, never a token; pass --profile-name NAME or set OHMYHOST_PROFILE to choose one)",
|
|
18426
18851
|
"github connect": "ohmyhost github connect --organization ULID --idempotency-key KEY --json (connect once, then link covered repositories without another browser consent)",
|
|
18427
18852
|
"github status": "ohmyhost github status --organization ULID --json",
|
|
18428
18853
|
"export create": "ohmyhost export create --project ULID --idempotency-key KEY --stdin --json (password on stdin only; one accepted SQL ZIP per project per 24 hours)",
|
|
@@ -18437,9 +18862,9 @@ var HELP_USAGE = Object.freeze({
|
|
|
18437
18862
|
"credits usage": "ohmyhost credits usage --organization ULID --month YYYY-MM [--cursor ULID] --json",
|
|
18438
18863
|
"budget get": "ohmyhost budget get --project ULID --json",
|
|
18439
18864
|
"budget set": "ohmyhost budget set --project ULID --credits NUMBER|none [--mode continue|stop] --idempotency-key KEY --json",
|
|
18440
|
-
"organization create": "ohmyhost organization create --name NAME --idempotency-key KEY [--source SOURCE] --json (SOURCE is optional attribution from a link's r value; the new workspace
|
|
18441
|
-
"organization list": "ohmyhost organization list --json (the workspaces
|
|
18442
|
-
"organization use": "ohmyhost organization use --organization ULID --json",
|
|
18865
|
+
"organization create": "ohmyhost organization create --name NAME --idempotency-key KEY [--source SOURCE] --json (SOURCE is optional attribution from a link's r value; a login without organization is bound to the new workspace, another login keeps its own)",
|
|
18866
|
+
"organization list": "ohmyhost organization list [--profile-name NAME] --json (the workspaces of the chosen login's user and the one that login is scoped to)",
|
|
18867
|
+
"organization use": "ohmyhost organization use --organization ULID [--profile-name NAME] --json (binds a login that has no organization yet; another organization needs its own login)",
|
|
18443
18868
|
"operation get": "ohmyhost operation get OPERATION_ULID --json",
|
|
18444
18869
|
"operation reconcile": "ohmyhost operation reconcile OPERATION_ULID --idempotency-key KEY --yes --json",
|
|
18445
18870
|
"token create": "ohmyhost token create --organization ULID --name NAME --idempotency-key KEY --out .env.local --json",
|
|
@@ -18489,7 +18914,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
18489
18914
|
delete: "ohmyhost delete --project ULID --if-match ETAG --confirmation-token TOKEN --idempotency-key KEY --yes --json",
|
|
18490
18915
|
"secret list": "ohmyhost secret list --project ULID --environment ENVIRONMENT_ULID --json",
|
|
18491
18916
|
"function runs": "ohmyhost function runs --project ULID --environment ENVIRONMENT_ULID [--limit 1-100] --json",
|
|
18492
|
-
"secret set": `printf '%s' "$SECRET_VALUE" | ohmyhost secret set NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY --stdin [--wait] --json`,
|
|
18917
|
+
"secret set": `printf '%s' "$SECRET_VALUE" | ohmyhost secret set NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--profile-user USER_ID --profile-organization ULID | --token-user USER_ID --token-organization ULID] --stdin [--wait] --json (with --profile-user and --profile-organization the saved login that runs it must belong to that user and organization; with --token-user and --token-organization it runs only with an OHMYHOST_TOKEN of that user and organization, never with a saved login)`,
|
|
18493
18918
|
"secret delete": "ohmyhost secret delete NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--wait] --json",
|
|
18494
18919
|
"mail setup": "ohmyhost mail setup --project ULID --environment ULID --domain DOMAIN --sending true --receiving false --idempotency-key KEY --json",
|
|
18495
18920
|
"mail status": "ohmyhost mail status --project ULID --environment ULID --json",
|
|
@@ -18507,8 +18932,8 @@ var HELP_COMMANDS = Object.freeze(Object.entries(HELP_USAGE).map(([command, usag
|
|
|
18507
18932
|
var cliMetadataResult = (kind, topic) => kind === "help" ? success("help", {
|
|
18508
18933
|
status: "succeeded",
|
|
18509
18934
|
cli_version: CLI_VERSION,
|
|
18510
|
-
usage: "ohmyhost <command> [options] --json",
|
|
18511
|
-
environment: "OHMYHOST_ENVIRONMENT=development|production (default: production)",
|
|
18935
|
+
usage: "ohmyhost <command> [options] [--profile-name NAME] --json",
|
|
18936
|
+
environment: "OHMYHOST_ENVIRONMENT=development|production (default: production); OHMYHOST_PROFILE=NAME binds this process to one saved login",
|
|
18512
18937
|
commands: HELP_COMMANDS.filter((entry) => !topic || entry.command === topic)
|
|
18513
18938
|
}) : success("version", { status: "succeeded", cli_version: CLI_VERSION });
|
|
18514
18939
|
var ProductCliFailure = class extends Error {
|
|
@@ -18529,8 +18954,11 @@ var ProductCliFailure = class extends Error {
|
|
|
18529
18954
|
};
|
|
18530
18955
|
var ProductCli = class {
|
|
18531
18956
|
dependencies;
|
|
18957
|
+
/** The saved login of this run; binding a login without organization replaces it. */
|
|
18958
|
+
#profile;
|
|
18532
18959
|
constructor(dependencies) {
|
|
18533
18960
|
this.dependencies = dependencies;
|
|
18961
|
+
this.#profile = dependencies.profile;
|
|
18534
18962
|
}
|
|
18535
18963
|
async run(argv, signal = new AbortController().signal) {
|
|
18536
18964
|
let command;
|
|
@@ -18560,8 +18988,10 @@ var ProductCli = class {
|
|
|
18560
18988
|
if (this.dependencies.apiToken !== void 0 && (command.kind === "organization-create" || command.kind === "organization-list" || command.kind === "organization-use")) {
|
|
18561
18989
|
throw new InteractiveSessionRequiredError();
|
|
18562
18990
|
}
|
|
18991
|
+
if (command.kind === "profile-list")
|
|
18992
|
+
return await this.listProfiles();
|
|
18563
18993
|
if (command.kind === "login")
|
|
18564
|
-
return await this.login(command.
|
|
18994
|
+
return await this.login(command.organizationId, command.userId, signal);
|
|
18565
18995
|
if (command.kind === "logout") {
|
|
18566
18996
|
return await this.logout(command.credentialStore, command.revoke, signal);
|
|
18567
18997
|
}
|
|
@@ -18672,35 +19102,48 @@ var ProductCli = class {
|
|
|
18672
19102
|
...command.signupSource === void 0 ? {} : { signupSource: command.signupSource },
|
|
18673
19103
|
idempotencyKey: command.idempotencyKey
|
|
18674
19104
|
}));
|
|
18675
|
-
const selection =
|
|
19105
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, organization.id);
|
|
18676
19106
|
if (selection.outcome !== "selected")
|
|
18677
19107
|
throw new ProductCliFailure(2, "organization_not_found", true, `The new workspace is not yet readable for this account. Run '${this.dependencies.commandPrefix} organization use --organization ${organization.id} --json' again.`);
|
|
19108
|
+
const current = this.#profile;
|
|
19109
|
+
if (current !== void 0 && current.organizationId !== null && current.organizationId !== organization.id)
|
|
19110
|
+
return success("organization create", {
|
|
19111
|
+
status: "succeeded",
|
|
19112
|
+
organization,
|
|
19113
|
+
selected: current.organizationId,
|
|
19114
|
+
profile: publicProfile(current),
|
|
19115
|
+
next_action: `Run '${this.dependencies.commandPrefix} login --organization ${organization.id} --user ${current.userId} --json' to add a login for the new workspace as the same user; profile ${current.name} stays in organization ${current.organizationId}.`
|
|
19116
|
+
});
|
|
19117
|
+
const profile = await this.bindOrganization(selection.organization, signal);
|
|
18678
19118
|
return success("organization create", {
|
|
18679
19119
|
status: "succeeded",
|
|
18680
19120
|
organization,
|
|
18681
19121
|
selected: organization.id,
|
|
19122
|
+
profile: publicProfile(profile),
|
|
18682
19123
|
next_action: `Run '${this.dependencies.commandPrefix} whoami --json' to confirm the selected workspace, then create a project.`,
|
|
18683
19124
|
...await this.githubNextAction(await this.authenticatedApi(command.credentialStore, signal), organization.id)
|
|
18684
19125
|
});
|
|
18685
19126
|
}
|
|
18686
19127
|
if (command.kind === "organization-list") {
|
|
18687
19128
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18688
|
-
const
|
|
19129
|
+
const account = await api.getAccountProfile();
|
|
18689
19130
|
return success("organization list", {
|
|
18690
19131
|
status: "listed",
|
|
18691
|
-
organizations: organizations.map(publicWorkspace),
|
|
18692
|
-
selected:
|
|
19132
|
+
organizations: account.organizations.map(publicWorkspace),
|
|
19133
|
+
selected: this.#profile?.organizationId ?? null
|
|
18693
19134
|
});
|
|
18694
19135
|
}
|
|
18695
19136
|
if (command.kind === "organization-use") {
|
|
18696
19137
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18697
|
-
const selection =
|
|
19138
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, command.organizationId);
|
|
18698
19139
|
if (selection.outcome !== "selected")
|
|
18699
19140
|
throw this.unknownWorkspace(selection);
|
|
19141
|
+
const profile = await this.bindOrganization(selection.organization, signal);
|
|
18700
19142
|
return success("organization use", {
|
|
18701
19143
|
status: "selected",
|
|
18702
19144
|
organization: publicWorkspace(selection.organization),
|
|
18703
19145
|
selected: selection.organization.id,
|
|
19146
|
+
profile: publicProfile(profile),
|
|
18704
19147
|
...await this.githubNextAction(await this.authenticatedApi(command.credentialStore, signal), selection.organization.id)
|
|
18705
19148
|
});
|
|
18706
19149
|
}
|
|
@@ -18788,7 +19231,7 @@ var ProductCli = class {
|
|
|
18788
19231
|
return success(command.kind === "project-dev-share-link" ? "project dev-share link" : command.kind === "project-dev-share-rotate" ? "project dev-share rotate" : command.kind === "project-dev-share-revoke" ? "project dev-share revoke" : "project dev-access mode", { status: "succeeded", state });
|
|
18789
19232
|
}
|
|
18790
19233
|
if (command.kind === "database-write") {
|
|
18791
|
-
const file = await
|
|
19234
|
+
const file = await open3(command.statementFile, "r"), buffer = Buffer.alloc(65537);
|
|
18792
19235
|
let statement;
|
|
18793
19236
|
try {
|
|
18794
19237
|
const stat3 = await file.stat();
|
|
@@ -18901,8 +19344,10 @@ var ProductCli = class {
|
|
|
18901
19344
|
return failure(name, 3, error.code, false, error.message);
|
|
18902
19345
|
if (error instanceof InteractiveSessionRequiredError)
|
|
18903
19346
|
return failure(name, 2, "interactive_login_required", false, `A user API token cannot create, list or select a workspace. Run '${this.dependencies.commandPrefix} login --json' in a process without OHMYHOST_TOKEN.`);
|
|
19347
|
+
if (error instanceof ProfileLimitError)
|
|
19348
|
+
return failure(name, 2, "profile_limit_reached", false, profileLimitAction(this.dependencies.commandPrefix));
|
|
18904
19349
|
this.recordDiagnostic(diagnosticDescriptor(error, signal));
|
|
18905
|
-
return classifyFailure(name, error, signal);
|
|
19350
|
+
return classifyFailure(name, error, signal, this.context());
|
|
18906
19351
|
}
|
|
18907
19352
|
}
|
|
18908
19353
|
githubConnectAction(organizationId) {
|
|
@@ -18925,22 +19370,53 @@ var ProductCli = class {
|
|
|
18925
19370
|
return success("whoami", {
|
|
18926
19371
|
status: "authenticated",
|
|
18927
19372
|
identity,
|
|
19373
|
+
context: this.context(),
|
|
18928
19374
|
...identity.organization_ids.length === 1 && identity.organization_ids[0] !== void 0 ? await this.githubNextAction(api, identity.organization_ids[0]) : {}
|
|
18929
19375
|
});
|
|
18930
|
-
const selection =
|
|
18931
|
-
if (selection.outcome === "selected")
|
|
19376
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, void 0);
|
|
19377
|
+
if (selection.outcome === "selected") {
|
|
19378
|
+
await this.bindOrganization(selection.organization, signal);
|
|
18932
19379
|
return success("whoami", {
|
|
18933
19380
|
status: "authenticated",
|
|
18934
19381
|
identity: parseCurrentIdentity(await api.getCurrentIdentity()),
|
|
18935
19382
|
organization: publicWorkspace(selection.organization),
|
|
18936
19383
|
selected: selection.organization.id,
|
|
19384
|
+
context: this.context(),
|
|
18937
19385
|
...await this.githubNextAction(api, selection.organization.id)
|
|
18938
19386
|
});
|
|
19387
|
+
}
|
|
18939
19388
|
return success("whoami", {
|
|
18940
19389
|
status: "authenticated",
|
|
18941
19390
|
identity,
|
|
19391
|
+
context: this.context(),
|
|
18942
19392
|
organizations: selection.outcome === "choice_required" ? selection.organizations.map(publicWorkspace) : [],
|
|
18943
|
-
next_action:
|
|
19393
|
+
next_action: this.workspaceAction(selection)
|
|
19394
|
+
});
|
|
19395
|
+
}
|
|
19396
|
+
/** Which account this run acts as, without any credential. */
|
|
19397
|
+
context() {
|
|
19398
|
+
if (this.dependencies.apiToken !== void 0)
|
|
19399
|
+
return { credential: "environment_token" };
|
|
19400
|
+
return {
|
|
19401
|
+
credential: "profile",
|
|
19402
|
+
profile: this.#profile === void 0 ? null : publicProfile(this.#profile)
|
|
19403
|
+
};
|
|
19404
|
+
}
|
|
19405
|
+
/** The next step for a login that has no organization yet. */
|
|
19406
|
+
workspaceAction(selection) {
|
|
19407
|
+
const prefix = this.dependencies.commandPrefix;
|
|
19408
|
+
const named = this.#profile === void 0 ? "" : ` --profile-name ${this.#profile.name}`;
|
|
19409
|
+
return selection.outcome === "choice_required" ? `Ask the customer which workspace to use, then run '${prefix} organization use --organization ULID${named} --json'.` : `Run '${prefix} organization create --name NAME --idempotency-key KEY${named} --json' to create the first workspace.`;
|
|
19410
|
+
}
|
|
19411
|
+
async listProfiles() {
|
|
19412
|
+
const profiles = this.dependencies.profiles;
|
|
19413
|
+
if (profiles === void 0)
|
|
19414
|
+
throw new ProviderCapabilityBlockedError();
|
|
19415
|
+
return success("profile list", {
|
|
19416
|
+
status: "listed",
|
|
19417
|
+
profiles: (await profiles.list()).map(publicProfile),
|
|
19418
|
+
bound: this.dependencies.profileBinding ?? null,
|
|
19419
|
+
environment_token: this.dependencies.apiToken !== void 0
|
|
18944
19420
|
});
|
|
18945
19421
|
}
|
|
18946
19422
|
recordDiagnostic(event) {
|
|
@@ -18953,7 +19429,11 @@ var ProductCli = class {
|
|
|
18953
19429
|
}
|
|
18954
19430
|
}
|
|
18955
19431
|
// The reviewed boundary proof parses this signature, so keep its parameters brace-free.
|
|
18956
|
-
async login(
|
|
19432
|
+
async login(organizationId, expectedUserId, signal) {
|
|
19433
|
+
const profiles = this.dependencies.profiles;
|
|
19434
|
+
const readIdentity = this.dependencies.currentIdentity;
|
|
19435
|
+
if (profiles === void 0 || readIdentity === void 0)
|
|
19436
|
+
throw new ProviderCapabilityBlockedError();
|
|
18957
19437
|
const signInDeadline = this.dependencies.now().getTime() + SIGN_IN_WINDOW_MS;
|
|
18958
19438
|
let session = await this.dependencies.deviceFlow.authorize();
|
|
18959
19439
|
await presentAuthorization(this.dependencies.deviceAuthorizationPresenter, session.authorization);
|
|
@@ -18990,53 +19470,112 @@ var ProductCli = class {
|
|
|
18990
19470
|
throw error;
|
|
18991
19471
|
throw new ProductCliFailure(4, "device_token_expiry_missing", false, "Run login again; the authorization response was incomplete.");
|
|
18992
19472
|
}
|
|
18993
|
-
|
|
19473
|
+
const signedIn = {
|
|
18994
19474
|
accessToken: result.tokens.accessToken,
|
|
18995
19475
|
refreshToken: result.tokens.refreshToken,
|
|
18996
19476
|
expiresAt
|
|
18997
|
-
}
|
|
18998
|
-
const
|
|
18999
|
-
|
|
19477
|
+
};
|
|
19478
|
+
const account = await this.dependencies.accountProfile(async () => signedIn.accessToken);
|
|
19479
|
+
if (!isProfileUserId(account.userId))
|
|
19480
|
+
throw this.unverifiedLogin();
|
|
19481
|
+
if (expectedUserId !== void 0 && account.userId !== expectedUserId)
|
|
19482
|
+
throw new ProductCliFailure(4, "login_account_mismatch", false, `The browser signed in as user ${account.userId}, not ${expectedUserId}. Nothing was saved and every saved login is unchanged. Sign out of ohmyho.st in that browser or use the browser profile of ${expectedUserId}, then run '${this.dependencies.commandPrefix} login${organizationId === void 0 ? "" : ` --organization ${organizationId}`} --user ${expectedUserId} --json' again.`);
|
|
19483
|
+
const selection = chooseWorkspace(account.organizations, organizationId);
|
|
19484
|
+
if (selection.outcome === "not_a_member")
|
|
19485
|
+
throw this.unknownWorkspace(selection);
|
|
19486
|
+
const credential = selection.outcome === "selected" ? await runBounded((bounded2) => this.dependencies.tokenRefresher.refresh(signedIn.refreshToken, bounded2, selection.organization.workos_id), signal, this.dependencies.lifecycleTimeoutMs) : signedIn;
|
|
19487
|
+
const identity = await readIdentity(async () => credential.accessToken);
|
|
19488
|
+
const scopedId = selection.outcome === "selected" ? selection.organization.id : parseCurrentIdentity(identity).organization_ids[0] ?? null;
|
|
19489
|
+
const organization = scopedId === null ? null : account.organizations.find((candidate) => candidate.id === scopedId);
|
|
19490
|
+
if (organization === void 0)
|
|
19491
|
+
throw this.unverifiedLogin();
|
|
19492
|
+
try {
|
|
19493
|
+
assertVerifiedLogin(identity, account.userId, scopedId);
|
|
19494
|
+
} catch (error) {
|
|
19495
|
+
if (error instanceof AccountVerificationError)
|
|
19496
|
+
throw this.unverifiedLogin();
|
|
19497
|
+
throw error;
|
|
19498
|
+
}
|
|
19499
|
+
const login = {
|
|
19500
|
+
userId: account.userId,
|
|
19501
|
+
organizationId: scopedId,
|
|
19502
|
+
organizationName: organization?.name ?? null
|
|
19503
|
+
};
|
|
19504
|
+
const profile = {
|
|
19505
|
+
name: this.dependencies.profileName ?? defaultProfileName(await profiles.list(), login),
|
|
19506
|
+
...login
|
|
19507
|
+
};
|
|
19508
|
+
try {
|
|
19509
|
+
await profiles.save(profile, organization === null ? credential : {
|
|
19510
|
+
...credential,
|
|
19511
|
+
workosOrganizationId: organization.workos_id,
|
|
19512
|
+
organizationId: organization.id
|
|
19513
|
+
});
|
|
19514
|
+
} catch (error) {
|
|
19515
|
+
if (!(error instanceof ProfileNameConflictError))
|
|
19516
|
+
throw error;
|
|
19517
|
+
throw new ProductCliFailure(2, "profile_name_conflict", false, `The name ${profile.name} already belongs to the saved login of user ${error.profile.userId} in organization ${String(error.profile.organizationId ?? "none")}. Nothing was saved. Choose another --profile-name, or remove that login with '${this.dependencies.commandPrefix} logout --profile-name ${profile.name} --json' first.`);
|
|
19518
|
+
}
|
|
19519
|
+
this.#profile = profile;
|
|
19000
19520
|
const readGithubConnection = this.dependencies.githubConnectionStatus;
|
|
19001
|
-
if (
|
|
19521
|
+
if (organization !== null)
|
|
19002
19522
|
return success("login", {
|
|
19003
19523
|
status: "authenticated",
|
|
19004
|
-
organization: publicWorkspace(
|
|
19524
|
+
organization: publicWorkspace(organization),
|
|
19525
|
+
profile: publicProfile(profile),
|
|
19005
19526
|
...readGithubConnection === void 0 ? {} : await this.githubNextAction({
|
|
19006
|
-
getGithubConnection: (selectedOrganizationId) => readGithubConnection(() =>
|
|
19007
|
-
},
|
|
19527
|
+
getGithubConnection: (selectedOrganizationId) => readGithubConnection(async () => credential.accessToken, selectedOrganizationId)
|
|
19528
|
+
}, organization.id)
|
|
19008
19529
|
});
|
|
19009
|
-
if (selection.outcome === "not_a_member")
|
|
19010
|
-
throw this.unknownWorkspace(selection);
|
|
19011
19530
|
return success("login", {
|
|
19012
19531
|
status: "authenticated",
|
|
19013
19532
|
organization: null,
|
|
19533
|
+
profile: publicProfile(profile),
|
|
19014
19534
|
organizations: selection.outcome === "choice_required" ? selection.organizations.map(publicWorkspace) : [],
|
|
19015
|
-
next_action:
|
|
19535
|
+
next_action: this.workspaceAction(selection)
|
|
19016
19536
|
});
|
|
19017
19537
|
}
|
|
19018
19538
|
}
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19539
|
+
/** The provider answered with another account than the one this login requested. */
|
|
19540
|
+
unverifiedLogin() {
|
|
19541
|
+
return new ProductCliFailure(4, "login_verification_failed", true, `The signed-in account could not be verified; nothing was saved. Run '${this.dependencies.commandPrefix} login --json' again.`);
|
|
19542
|
+
}
|
|
19543
|
+
/**
|
|
19544
|
+
* Bind the login of this run, which has no organization yet, to one of its user's organizations.
|
|
19545
|
+
* A login that already has one keeps it; another organization is its own login.
|
|
19546
|
+
*/
|
|
19547
|
+
async bindOrganization(organization, signal) {
|
|
19548
|
+
const profiles = this.dependencies.profiles;
|
|
19549
|
+
const profile = this.#profile;
|
|
19550
|
+
if (profiles === void 0 || profile === void 0)
|
|
19551
|
+
throw new InteractiveSessionRequiredError();
|
|
19552
|
+
let bound;
|
|
19553
|
+
try {
|
|
19554
|
+
bound = await runBounded((bounded2) => selectWorkspace({
|
|
19555
|
+
store: profiles.credential(profile),
|
|
19556
|
+
refresher: this.dependencies.tokenRefresher,
|
|
19557
|
+
profiles,
|
|
19558
|
+
profile
|
|
19559
|
+
}, organization, bounded2, (accessToken) => this.dependencies.productApi(async () => accessToken).getCurrentIdentity()), signal, this.dependencies.lifecycleTimeoutMs);
|
|
19560
|
+
} catch (error) {
|
|
19561
|
+
if (error instanceof AccountVerificationError)
|
|
19562
|
+
throw this.unverifiedLogin();
|
|
19563
|
+
if (!(error instanceof ProfileOrganizationFixedError))
|
|
19564
|
+
throw error;
|
|
19565
|
+
throw new ProductCliFailure(2, "profile_organization_fixed", false, `Profile ${profile.name} stays signed in to organization ${String(profile.organizationId)}. Run '${this.dependencies.commandPrefix} login --organization ${organization.id} --user ${profile.userId} --json' to add a separate login for ${organization.id} as the same user; a saved login never switches organizations.`);
|
|
19566
|
+
}
|
|
19567
|
+
this.#profile = bound;
|
|
19568
|
+
return bound;
|
|
19031
19569
|
}
|
|
19032
19570
|
unknownWorkspace(selection) {
|
|
19033
19571
|
return new ProductCliFailure(2, "organization_not_found", false, `You are not a member of that workspace. Run '${this.dependencies.commandPrefix} organization list --json' and choose one of ${String(selection.outcome === "not_a_member" ? selection.organizations.length : 0)}.`);
|
|
19034
19572
|
}
|
|
19035
19573
|
async logout(credentialStore, revoke, signal) {
|
|
19036
|
-
const store = this.
|
|
19574
|
+
const store = this.store(credentialStore);
|
|
19575
|
+
const removed = this.#profile === void 0 ? {} : { profile: publicProfile(this.#profile) };
|
|
19037
19576
|
if (!revoke) {
|
|
19038
|
-
await
|
|
19039
|
-
return success("logout", { status: "logged_out" });
|
|
19577
|
+
await this.forgetLogin(store);
|
|
19578
|
+
return success("logout", { status: "logged_out", ...removed });
|
|
19040
19579
|
}
|
|
19041
19580
|
const credential = await store.read();
|
|
19042
19581
|
let revocationError;
|
|
@@ -19047,7 +19586,7 @@ var ProductCli = class {
|
|
|
19047
19586
|
} catch (error) {
|
|
19048
19587
|
revocationError = error;
|
|
19049
19588
|
} finally {
|
|
19050
|
-
await
|
|
19589
|
+
await this.forgetLogin(store);
|
|
19051
19590
|
}
|
|
19052
19591
|
if (revocationError !== void 0) {
|
|
19053
19592
|
if (isCancellation(revocationError) && signal.aborted)
|
|
@@ -19063,7 +19602,29 @@ var ProductCli = class {
|
|
|
19063
19602
|
}
|
|
19064
19603
|
throw revocationError;
|
|
19065
19604
|
}
|
|
19066
|
-
return success("logout", { status: "logged_out" });
|
|
19605
|
+
return success("logout", { status: "logged_out", ...removed });
|
|
19606
|
+
}
|
|
19607
|
+
/** The credential store of this run's saved login; it follows a binding made during the run. */
|
|
19608
|
+
store(kind) {
|
|
19609
|
+
const profiles = this.dependencies.profiles;
|
|
19610
|
+
if (profiles !== void 0 && this.#profile !== void 0)
|
|
19611
|
+
return profiles.credential(this.#profile);
|
|
19612
|
+
if (this.dependencies.credentialStore === void 0)
|
|
19613
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19614
|
+
return this.dependencies.credentialStore(kind);
|
|
19615
|
+
}
|
|
19616
|
+
/** Removes only this run's saved login; every other saved login stays untouched. */
|
|
19617
|
+
async forgetLogin(store) {
|
|
19618
|
+
const profiles = this.dependencies.profiles;
|
|
19619
|
+
if (profiles !== void 0 && this.#profile !== void 0)
|
|
19620
|
+
await profiles.remove(this.#profile);
|
|
19621
|
+
else
|
|
19622
|
+
await store.clear();
|
|
19623
|
+
}
|
|
19624
|
+
/** How to sign this run's login in again, naming its organization and alias when known. */
|
|
19625
|
+
loginAction() {
|
|
19626
|
+
const profile = this.#profile;
|
|
19627
|
+
return `Run '${this.dependencies.commandPrefix} login${profile?.organizationId === void 0 || profile.organizationId === null ? "" : ` --organization ${profile.organizationId}`}${profile === void 0 ? "" : ` --profile-name ${profile.name}`} --json'.`;
|
|
19067
19628
|
}
|
|
19068
19629
|
async getOperation(command, signal) {
|
|
19069
19630
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19116,7 +19677,7 @@ var ProductCli = class {
|
|
|
19116
19677
|
async getProjectStatus(command, signal) {
|
|
19117
19678
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19118
19679
|
const project = parseProjectStatus(await api.getProjectStatus(command.projectId));
|
|
19119
|
-
return success("project status", { status: "succeeded", project });
|
|
19680
|
+
return success("project status", { status: "succeeded", project, context: this.context() });
|
|
19120
19681
|
}
|
|
19121
19682
|
async createDevAccessTicket(command, signal) {
|
|
19122
19683
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19259,14 +19820,18 @@ var ProductCli = class {
|
|
|
19259
19820
|
const source = project.source;
|
|
19260
19821
|
if (project.project_id !== command.projectId || source === null || source.status !== "ready" || source.repository_full_name.toLowerCase() !== `${command.repositoryOwner}/${command.repositoryName}`.toLowerCase())
|
|
19261
19822
|
throw new ResponseContractError();
|
|
19823
|
+
const [organizationId, ...others] = parseCurrentIdentity(await api.getCurrentIdentity()).organization_ids;
|
|
19824
|
+
if (organizationId === void 0 || others.length > 0)
|
|
19825
|
+
throw new ResponseContractError();
|
|
19262
19826
|
await this.dependencies.projectLinkStore.save({
|
|
19263
|
-
version:
|
|
19827
|
+
version: 2,
|
|
19264
19828
|
api_origin: this.dependencies.productApiOrigin,
|
|
19829
|
+
organization_id: organizationId,
|
|
19265
19830
|
project_id: command.projectId,
|
|
19266
19831
|
installation_id: source.installation_id,
|
|
19267
19832
|
repository_full_name: source.repository_full_name
|
|
19268
19833
|
});
|
|
19269
|
-
return success("link", { status: "completed", operation });
|
|
19834
|
+
return success("link", { status: "completed", operation, organization_id: organizationId });
|
|
19270
19835
|
}
|
|
19271
19836
|
async configureAutoDeploy(command, signal) {
|
|
19272
19837
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19509,10 +20074,10 @@ var ProductCli = class {
|
|
|
19509
20074
|
async authenticatedToken(credentialStore, signal) {
|
|
19510
20075
|
if (this.dependencies.apiToken !== void 0)
|
|
19511
20076
|
return this.dependencies.apiToken;
|
|
19512
|
-
const store = this.
|
|
20077
|
+
const store = this.store(credentialStore);
|
|
19513
20078
|
const stored = await store.read();
|
|
19514
20079
|
if (stored === void 0) {
|
|
19515
|
-
throw new ProductCliFailure(5, "authentication_required", false,
|
|
20080
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19516
20081
|
}
|
|
19517
20082
|
let refresh;
|
|
19518
20083
|
try {
|
|
@@ -19520,8 +20085,8 @@ var ProductCli = class {
|
|
|
19520
20085
|
} catch (error) {
|
|
19521
20086
|
if (!(error instanceof TokenRefreshRejectedError))
|
|
19522
20087
|
throw error;
|
|
19523
|
-
await
|
|
19524
|
-
throw new ProductCliFailure(5, "authentication_required", false,
|
|
20088
|
+
await this.forgetLogin(store);
|
|
20089
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19525
20090
|
}
|
|
19526
20091
|
if (refresh.refreshed)
|
|
19527
20092
|
await store.replace(refresh.credential);
|
|
@@ -19587,7 +20152,16 @@ var redactVerificationUri = (value) => {
|
|
|
19587
20152
|
url.hash = "";
|
|
19588
20153
|
return url.toString();
|
|
19589
20154
|
};
|
|
19590
|
-
var
|
|
20155
|
+
var ACCOUNT_SENSITIVE_PROBLEMS = /* @__PURE__ */ new Set([
|
|
20156
|
+
"resource_not_found",
|
|
20157
|
+
"forbidden",
|
|
20158
|
+
"organization_required",
|
|
20159
|
+
"github_connection_required",
|
|
20160
|
+
"github_connection_revoked",
|
|
20161
|
+
"repository_not_installed"
|
|
20162
|
+
]);
|
|
20163
|
+
var classifyCliFailure = (command, error, signal) => classifyFailure(command, error, signal);
|
|
20164
|
+
var classifyFailure = (command, error, signal, actingAs) => {
|
|
19591
20165
|
if (signal.aborted || isCancellation(error)) {
|
|
19592
20166
|
return failure(command, 130, "command_cancelled", true, "Run the command again.");
|
|
19593
20167
|
}
|
|
@@ -19621,7 +20195,7 @@ var classifyFailure = (command, error, signal) => {
|
|
|
19621
20195
|
if (looksLikeProblem(error)) {
|
|
19622
20196
|
try {
|
|
19623
20197
|
const problem = parseSafeProblem(error);
|
|
19624
|
-
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl);
|
|
20198
|
+
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl, ACCOUNT_SENSITIVE_PROBLEMS.has(problem.code) ? actingAs : void 0);
|
|
19625
20199
|
} catch {
|
|
19626
20200
|
return failure(command, 7, "response_contract_invalid", false, "Retry only after the client or server contract is repaired.");
|
|
19627
20201
|
}
|
|
@@ -19641,7 +20215,7 @@ var success = (command, body) => ({
|
|
|
19641
20215
|
`,
|
|
19642
20216
|
stderr: ""
|
|
19643
20217
|
});
|
|
19644
|
-
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl) => {
|
|
20218
|
+
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl, actingAs) => {
|
|
19645
20219
|
const document = {
|
|
19646
20220
|
version: 1,
|
|
19647
20221
|
command,
|
|
@@ -19654,7 +20228,8 @@ var failure = (command, exitCode, code, retryable, suggestedAction, operation, r
|
|
|
19654
20228
|
...retryAfterSeconds === void 0 ? {} : { retry_after_seconds: retryAfterSeconds },
|
|
19655
20229
|
...requestId === void 0 ? {} : { request_id: requestId },
|
|
19656
20230
|
...docsUrl === void 0 ? {} : { docs_url: docsUrl },
|
|
19657
|
-
...operation?.error === void 0 ? {} : { message: operation.error.message }
|
|
20231
|
+
...operation?.error === void 0 ? {} : { message: operation.error.message },
|
|
20232
|
+
...actingAs === void 0 ? {} : { acting_as: actingAs }
|
|
19658
20233
|
}
|
|
19659
20234
|
};
|
|
19660
20235
|
return { exitCode, stdout: `${JSON.stringify(document)}
|
|
@@ -19787,6 +20362,8 @@ var parsedCommandName = (command) => {
|
|
|
19787
20362
|
return "secret delete";
|
|
19788
20363
|
if (command.kind === "managed-mail")
|
|
19789
20364
|
return command.label;
|
|
20365
|
+
if (command.kind === "profile-list")
|
|
20366
|
+
return "profile list";
|
|
19790
20367
|
return command.kind;
|
|
19791
20368
|
};
|
|
19792
20369
|
var boundedSignal = (parent, timeoutMs) => AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
|
|
@@ -21224,6 +21801,23 @@ var deleteProject = (parameters, options) => {
|
|
|
21224
21801
|
...params
|
|
21225
21802
|
});
|
|
21226
21803
|
};
|
|
21804
|
+
var getProject = (parameters, options) => {
|
|
21805
|
+
const params = buildClientParams([parameters], [
|
|
21806
|
+
{
|
|
21807
|
+
args: [
|
|
21808
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
21809
|
+
{ in: "path", key: "project_id" }
|
|
21810
|
+
]
|
|
21811
|
+
}
|
|
21812
|
+
]);
|
|
21813
|
+
return (options?.client ?? client).get({
|
|
21814
|
+
responseStyle: "data",
|
|
21815
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
21816
|
+
url: "/v1/projects/{project_id}",
|
|
21817
|
+
...options,
|
|
21818
|
+
...params
|
|
21819
|
+
});
|
|
21820
|
+
};
|
|
21227
21821
|
var getProjectContext = (parameters, options) => {
|
|
21228
21822
|
const params = buildClientParams([parameters], [
|
|
21229
21823
|
{
|
|
@@ -22908,18 +23502,75 @@ var GeneratedSdkProductApi = class {
|
|
|
22908
23502
|
};
|
|
22909
23503
|
|
|
22910
23504
|
// apps/product-cli/dist/project-link-store.js
|
|
22911
|
-
import { appendFile, chmod, mkdir as
|
|
22912
|
-
import { join } from "node:path";
|
|
22913
|
-
|
|
23505
|
+
import { appendFile, chmod, mkdir as mkdir3, readFile as readFile2, readdir as readdir2, rename, stat, unlink as unlink2, writeFile } from "node:fs/promises";
|
|
23506
|
+
import { join as join2 } from "node:path";
|
|
23507
|
+
var ULID2 = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
23508
|
+
var LINKS = "links";
|
|
23509
|
+
var LEGACY_LINK = "link.json";
|
|
23510
|
+
async function readProjectLinks(directory, apiOrigin) {
|
|
23511
|
+
let names = [];
|
|
22914
23512
|
try {
|
|
22915
|
-
|
|
22916
|
-
if (typeof link !== "object" || link === null)
|
|
22917
|
-
return null;
|
|
22918
|
-
const { version: version2, api_origin, project_id } = link;
|
|
22919
|
-
return version2 === 1 && api_origin === apiOrigin && typeof project_id === "string" && /^[0-9A-HJKMNP-TV-Z]{26}$/u.test(project_id) ? project_id : null;
|
|
23513
|
+
names = await readdir2(join2(directory, LINKS));
|
|
22920
23514
|
} catch {
|
|
22921
|
-
|
|
23515
|
+
names = [];
|
|
22922
23516
|
}
|
|
23517
|
+
const links = [];
|
|
23518
|
+
for (const name of names.sort()) {
|
|
23519
|
+
const organizationId = /^([0-7][0-9A-HJKMNP-TV-Z]{25})\.json$/u.exec(name)?.[1];
|
|
23520
|
+
if (organizationId === void 0)
|
|
23521
|
+
continue;
|
|
23522
|
+
const link2 = await readJson(join2(directory, LINKS, name));
|
|
23523
|
+
if (isRecord3(link2, [
|
|
23524
|
+
"version",
|
|
23525
|
+
"api_origin",
|
|
23526
|
+
"organization_id",
|
|
23527
|
+
"project_id",
|
|
23528
|
+
"installation_id",
|
|
23529
|
+
"repository_full_name"
|
|
23530
|
+
]) && link2["version"] === 2 && link2["api_origin"] === apiOrigin && link2["organization_id"] === organizationId && typeof link2["project_id"] === "string" && ULID2.test(link2["project_id"]) && typeof link2["installation_id"] === "string" && typeof link2["repository_full_name"] === "string")
|
|
23531
|
+
links.push(link2);
|
|
23532
|
+
}
|
|
23533
|
+
const legacy = await readJson(join2(directory, LEGACY_LINK));
|
|
23534
|
+
return {
|
|
23535
|
+
links,
|
|
23536
|
+
legacy: typeof legacy === "object" && legacy !== null && legacy["version"] === 1 && legacy["api_origin"] === apiOrigin && typeof legacy["project_id"] === "string" && ULID2.test(legacy["project_id"]) && typeof legacy["installation_id"] === "string" && typeof legacy["repository_full_name"] === "string" ? {
|
|
23537
|
+
project_id: legacy["project_id"],
|
|
23538
|
+
installation_id: legacy["installation_id"],
|
|
23539
|
+
repository_full_name: legacy["repository_full_name"]
|
|
23540
|
+
} : null
|
|
23541
|
+
};
|
|
23542
|
+
}
|
|
23543
|
+
function chooseLinkedProject(directory, organizationId) {
|
|
23544
|
+
if (organizationId !== void 0) {
|
|
23545
|
+
const link2 = directory.links.find((candidate) => candidate.organization_id === organizationId);
|
|
23546
|
+
if (link2 !== void 0)
|
|
23547
|
+
return { outcome: "linked", projectId: link2.project_id, organizationId };
|
|
23548
|
+
return directory.legacy === null ? { outcome: "none" } : { outcome: "linked", projectId: directory.legacy.project_id, organizationId: null };
|
|
23549
|
+
}
|
|
23550
|
+
const candidates = [
|
|
23551
|
+
...directory.links.map((link2) => ({
|
|
23552
|
+
projectId: link2.project_id,
|
|
23553
|
+
organizationId: link2.organization_id
|
|
23554
|
+
})),
|
|
23555
|
+
...directory.legacy === null ? [] : [{ projectId: directory.legacy.project_id, organizationId: null }]
|
|
23556
|
+
];
|
|
23557
|
+
const [only] = candidates;
|
|
23558
|
+
if (candidates.length === 1 && only !== void 0)
|
|
23559
|
+
return { outcome: "linked", ...only };
|
|
23560
|
+
if (candidates.length === 0)
|
|
23561
|
+
return { outcome: "none" };
|
|
23562
|
+
return {
|
|
23563
|
+
outcome: "ambiguous",
|
|
23564
|
+
organizationIds: directory.links.map((link2) => link2.organization_id)
|
|
23565
|
+
};
|
|
23566
|
+
}
|
|
23567
|
+
function linkedOrganization(directory, projectId) {
|
|
23568
|
+
const organizations = new Set(directory.links.filter((link2) => link2.project_id === projectId).map((link2) => link2.organization_id));
|
|
23569
|
+
const [organizationId] = organizations;
|
|
23570
|
+
return organizations.size === 1 ? organizationId : void 0;
|
|
23571
|
+
}
|
|
23572
|
+
async function removeLegacyProjectLink(directory) {
|
|
23573
|
+
await unlink2(join2(directory, LEGACY_LINK));
|
|
22923
23574
|
}
|
|
22924
23575
|
var OwnerOnlyProjectLinkStore = class {
|
|
22925
23576
|
directory;
|
|
@@ -22930,10 +23581,15 @@ var OwnerOnlyProjectLinkStore = class {
|
|
|
22930
23581
|
this.repositoryRoot = repositoryRoot;
|
|
22931
23582
|
}
|
|
22932
23583
|
async save(metadata) {
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
const
|
|
22936
|
-
const
|
|
23584
|
+
if (!ULID2.test(metadata.organization_id))
|
|
23585
|
+
throw new TypeError("Invalid linked organization");
|
|
23586
|
+
const links = join2(this.directory, LINKS);
|
|
23587
|
+
for (const directory of [this.directory, links]) {
|
|
23588
|
+
await mkdir3(directory, { mode: 448, recursive: true });
|
|
23589
|
+
await chmod(directory, 448);
|
|
23590
|
+
}
|
|
23591
|
+
const target = join2(links, `${metadata.organization_id}.json`);
|
|
23592
|
+
const temporary = join2(links, `.link-${process.pid}.tmp`);
|
|
22937
23593
|
await writeFile(temporary, `${JSON.stringify(metadata)}
|
|
22938
23594
|
`, { encoding: "utf8", mode: 384 });
|
|
22939
23595
|
await chmod(temporary, 384);
|
|
@@ -22943,16 +23599,25 @@ var OwnerOnlyProjectLinkStore = class {
|
|
|
22943
23599
|
await ignoreLinkDirectory(this.repositoryRoot);
|
|
22944
23600
|
}
|
|
22945
23601
|
};
|
|
23602
|
+
async function readJson(path) {
|
|
23603
|
+
try {
|
|
23604
|
+
const value = JSON.parse(await readFile2(path, "utf8"));
|
|
23605
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
23606
|
+
} catch {
|
|
23607
|
+
return null;
|
|
23608
|
+
}
|
|
23609
|
+
}
|
|
23610
|
+
var isRecord3 = (value, keys) => typeof value === "object" && value !== null && Object.keys(value).sort().join(",") === [...keys].sort().join(",");
|
|
22946
23611
|
async function ignoreLinkDirectory(root) {
|
|
22947
23612
|
try {
|
|
22948
|
-
await stat(
|
|
23613
|
+
await stat(join2(root, ".git"));
|
|
22949
23614
|
} catch {
|
|
22950
23615
|
return;
|
|
22951
23616
|
}
|
|
22952
|
-
const file =
|
|
23617
|
+
const file = join2(root, ".gitignore");
|
|
22953
23618
|
let current = "";
|
|
22954
23619
|
try {
|
|
22955
|
-
current = await
|
|
23620
|
+
current = await readFile2(file, "utf8");
|
|
22956
23621
|
} catch {
|
|
22957
23622
|
current = "";
|
|
22958
23623
|
}
|
|
@@ -22965,7 +23630,7 @@ async function ignoreLinkDirectory(root) {
|
|
|
22965
23630
|
|
|
22966
23631
|
// apps/product-cli/dist/repository-init.js
|
|
22967
23632
|
import { createHash } from "node:crypto";
|
|
22968
|
-
import { lstat as lstat2, open as
|
|
23633
|
+
import { lstat as lstat2, open as open4, readFile as readFile3, readdir as readdir3, stat as stat2 } from "node:fs/promises";
|
|
22969
23634
|
import { dirname as dirname2, posix, resolve as resolve2 } from "node:path";
|
|
22970
23635
|
|
|
22971
23636
|
// packages/contracts/dist/application-root.js
|
|
@@ -23933,7 +24598,7 @@ async function cronBlockers(framework, config, applicationDirectory, files) {
|
|
|
23933
24598
|
];
|
|
23934
24599
|
}
|
|
23935
24600
|
const modulePath = framework === "vite" ? "src/ohmyhost/companion.ts" : WORKER_MODULE_PATH;
|
|
23936
|
-
const source = files.includes(modulePath) ? await
|
|
24601
|
+
const source = files.includes(modulePath) ? await readFile3(resolve2(applicationDirectory, modulePath), "utf8") : null;
|
|
23937
24602
|
if (framework !== "functions" && source !== null && !DEFAULT_EXPORT_PATTERN.test(source)) {
|
|
23938
24603
|
return [defaultExportBlocker(modulePath)];
|
|
23939
24604
|
}
|
|
@@ -23957,7 +24622,7 @@ async function storageClientBlockers(config, applicationDirectory, files) {
|
|
|
23957
24622
|
const information = await stat2(absolute);
|
|
23958
24623
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
23959
24624
|
continue;
|
|
23960
|
-
if (callsStorageClient(await
|
|
24625
|
+
if (callsStorageClient(await readFile3(absolute, "utf8")))
|
|
23961
24626
|
return [];
|
|
23962
24627
|
}
|
|
23963
24628
|
return [
|
|
@@ -23978,7 +24643,7 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
|
|
|
23978
24643
|
const information = await stat2(absolute);
|
|
23979
24644
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
23980
24645
|
continue;
|
|
23981
|
-
const source = (await
|
|
24646
|
+
const source = (await readFile3(absolute, "utf8")).replaceAll(INERT_SOURCE_PATTERN, " ");
|
|
23982
24647
|
if (DATABASE_PRIVATE_BINDING_PATTERN.test(source)) {
|
|
23983
24648
|
blockers.push({
|
|
23984
24649
|
code: "database_binding_private",
|
|
@@ -23997,7 +24662,7 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
|
|
|
23997
24662
|
async function workerModuleBlockers(framework, applicationDirectory, files) {
|
|
23998
24663
|
if (framework !== "functions" || !files.includes(WORKER_MODULE_PATH))
|
|
23999
24664
|
return [];
|
|
24000
|
-
const source = await
|
|
24665
|
+
const source = await readFile3(resolve2(applicationDirectory, WORKER_MODULE_PATH), "utf8");
|
|
24001
24666
|
return DEFAULT_EXPORT_PATTERN.test(source) ? [] : [defaultExportBlocker(WORKER_MODULE_PATH)];
|
|
24002
24667
|
}
|
|
24003
24668
|
function migrationFilenameBlockers(files, configuredPath, provider) {
|
|
@@ -24024,7 +24689,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
24024
24689
|
}
|
|
24025
24690
|
const migrations = await Promise.all(migrationPaths.map(async (path) => Object.freeze({
|
|
24026
24691
|
path,
|
|
24027
|
-
body: new Uint8Array(await
|
|
24692
|
+
body: new Uint8Array(await readFile3(resolve2(applicationDirectory, root, path)))
|
|
24028
24693
|
})));
|
|
24029
24694
|
const blockers = [];
|
|
24030
24695
|
for (const migration of migrations) {
|
|
@@ -24063,7 +24728,7 @@ async function readExistingConfiguration(repositoryRoot, repositoryFiles, applic
|
|
|
24063
24728
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES) {
|
|
24064
24729
|
throw new RepositoryInitError("repository_configuration_invalid", "Existing ohmyhost.yaml must be one bounded regular file.");
|
|
24065
24730
|
}
|
|
24066
|
-
const configuration = await
|
|
24731
|
+
const configuration = await readFile3(configurationPath, "utf8");
|
|
24067
24732
|
let parsed;
|
|
24068
24733
|
try {
|
|
24069
24734
|
parsed = parseOhmyhostConfigYaml(configuration);
|
|
@@ -24191,7 +24856,7 @@ async function tryReadPackageJson(root) {
|
|
|
24191
24856
|
}
|
|
24192
24857
|
function hasDirectSafeBuildCommand(packageJson, framework) {
|
|
24193
24858
|
const scripts = packageJson["scripts"];
|
|
24194
|
-
if (!
|
|
24859
|
+
if (!isRecord4(scripts) || typeof scripts["build"] !== "string")
|
|
24195
24860
|
return false;
|
|
24196
24861
|
const command = scripts["build"];
|
|
24197
24862
|
if (command.includes("..") || /(?:^|\s)cd(?:\s|$)/u.test(command))
|
|
@@ -24239,7 +24904,7 @@ async function collectRepositoryFiles(root) {
|
|
|
24239
24904
|
const directory = directories.pop();
|
|
24240
24905
|
if (directory === void 0)
|
|
24241
24906
|
break;
|
|
24242
|
-
for (const entry of await
|
|
24907
|
+
for (const entry of await readdir3(resolve2(root, directory), { withFileTypes: true })) {
|
|
24243
24908
|
const relative = directory === "" ? entry.name : posix.join(directory, entry.name);
|
|
24244
24909
|
if (entry.isSymbolicLink()) {
|
|
24245
24910
|
throw new RepositoryInitError("repository_symlink_forbidden", "Repository symlinks are forbidden");
|
|
@@ -24272,8 +24937,8 @@ async function readPackageJson(root) {
|
|
|
24272
24937
|
throw new RepositoryInitError("package_manifest_invalid", "package.json is invalid");
|
|
24273
24938
|
}
|
|
24274
24939
|
try {
|
|
24275
|
-
const value = JSON.parse(await
|
|
24276
|
-
if (!
|
|
24940
|
+
const value = JSON.parse(await readFile3(path, "utf8"));
|
|
24941
|
+
if (!isRecord4(value))
|
|
24277
24942
|
throw new TypeError();
|
|
24278
24943
|
return value;
|
|
24279
24944
|
} catch {
|
|
@@ -24284,7 +24949,7 @@ function dependencyNames(packageJson) {
|
|
|
24284
24949
|
const names = /* @__PURE__ */ new Set();
|
|
24285
24950
|
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
24286
24951
|
const value = packageJson[field];
|
|
24287
|
-
if (!
|
|
24952
|
+
if (!isRecord4(value))
|
|
24288
24953
|
continue;
|
|
24289
24954
|
for (const [name, version2] of Object.entries(value)) {
|
|
24290
24955
|
if (typeof version2 === "string" && version2.length > 0)
|
|
@@ -24423,9 +25088,9 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
24423
25088
|
});
|
|
24424
25089
|
}
|
|
24425
25090
|
const scripts = packageJson["scripts"];
|
|
24426
|
-
const scriptRecord =
|
|
25091
|
+
const scriptRecord = isRecord4(scripts) ? scripts : {};
|
|
24427
25092
|
if (framework === "functions") {
|
|
24428
|
-
} else if (!
|
|
25093
|
+
} else if (!isRecord4(scripts) || typeof scripts["build"] !== "string" || scripts["build"].length === 0) {
|
|
24429
25094
|
blockers.push({ code: "build_script_missing", message: "Declare a non-empty build script." });
|
|
24430
25095
|
} else if (!hasDirectSafeBuildCommand(packageJson, framework)) {
|
|
24431
25096
|
blockers.push({
|
|
@@ -24441,7 +25106,7 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
24441
25106
|
});
|
|
24442
25107
|
}
|
|
24443
25108
|
if (framework === "nextjs") {
|
|
24444
|
-
const dependencies =
|
|
25109
|
+
const dependencies = isRecord4(packageJson["dependencies"]) ? packageJson["dependencies"] : {};
|
|
24445
25110
|
const nextBuild = scriptRecord["build"] === "next build" || scriptRecord["build"] === "next build --webpack";
|
|
24446
25111
|
const nextPackageConfigured = nextBuild && compatibility.frameworks[0]?.classification !== "unsupported" && exactVersion(dependencies["react"]) && exactVersion(dependencies["react-dom"]) && frameworkConfigs.next !== null;
|
|
24447
25112
|
const nextConfigured = nextPackageConfigured && frameworkConfigs.ambiguous.every((config) => config !== "open-next");
|
|
@@ -24466,7 +25131,7 @@ function exactVersion(value) {
|
|
|
24466
25131
|
function dependencyVersion(packageJson, name) {
|
|
24467
25132
|
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
24468
25133
|
const dependencies = packageJson[field];
|
|
24469
|
-
if (!
|
|
25134
|
+
if (!isRecord4(dependencies))
|
|
24470
25135
|
continue;
|
|
24471
25136
|
const version2 = dependencies[name];
|
|
24472
25137
|
if (typeof version2 === "string" && version2.length > 0)
|
|
@@ -24537,7 +25202,7 @@ async function detectNextStandaloneOutput(root, framework, nextConfigPath) {
|
|
|
24537
25202
|
const information = await stat2(path);
|
|
24538
25203
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
|
|
24539
25204
|
return false;
|
|
24540
|
-
return declaresStandaloneOutput(await
|
|
25205
|
+
return declaresStandaloneOutput(await readFile3(path, "utf8"));
|
|
24541
25206
|
}
|
|
24542
25207
|
function declaresStandaloneOutput(source) {
|
|
24543
25208
|
const tokens = sourceTokens(source);
|
|
@@ -24606,7 +25271,7 @@ async function detectTanStackRuntime(root, files, framework, packageJson, viteCo
|
|
|
24606
25271
|
const information = await stat2(path);
|
|
24607
25272
|
if (!information.isFile() || information.size > MAXIMUM_PACKAGE_BYTES)
|
|
24608
25273
|
return "ambiguous";
|
|
24609
|
-
const source = await
|
|
25274
|
+
const source = await readFile3(path, "utf8");
|
|
24610
25275
|
const tokens = sourceTokens(source);
|
|
24611
25276
|
if (isCompleteTanStackStatic(tokens))
|
|
24612
25277
|
return "static";
|
|
@@ -24678,10 +25343,10 @@ function directSourceObjectProperty(tokens, start, end, name) {
|
|
|
24678
25343
|
}
|
|
24679
25344
|
return null;
|
|
24680
25345
|
}
|
|
24681
|
-
function matchingSourceDelimiter(tokens, start,
|
|
25346
|
+
function matchingSourceDelimiter(tokens, start, open5, close) {
|
|
24682
25347
|
let depth = 0;
|
|
24683
25348
|
for (let index = start; index < tokens.length; index += 1) {
|
|
24684
|
-
if (tokens[index] ===
|
|
25349
|
+
if (tokens[index] === open5)
|
|
24685
25350
|
depth += 1;
|
|
24686
25351
|
if (tokens[index] === close)
|
|
24687
25352
|
depth -= 1;
|
|
@@ -24715,7 +25380,7 @@ async function detectR2Storage(root, files) {
|
|
|
24715
25380
|
const information = await stat2(path);
|
|
24716
25381
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
|
|
24717
25382
|
continue;
|
|
24718
|
-
const source = await
|
|
25383
|
+
const source = await readFile3(path, "utf8");
|
|
24719
25384
|
if (/\br2_buckets\b/u.test(source))
|
|
24720
25385
|
return true;
|
|
24721
25386
|
}
|
|
@@ -24757,7 +25422,7 @@ function viteCompanionScaffold(files, capabilities) {
|
|
|
24757
25422
|
async function createConfiguration(root, configuration) {
|
|
24758
25423
|
let handle;
|
|
24759
25424
|
try {
|
|
24760
|
-
handle = await
|
|
25425
|
+
handle = await open4(resolve2(root, "ohmyhost.yaml"), "wx", 384);
|
|
24761
25426
|
await handle.writeFile(configuration, "utf8");
|
|
24762
25427
|
await handle.sync();
|
|
24763
25428
|
} catch (error) {
|
|
@@ -24770,7 +25435,7 @@ async function createConfiguration(root, configuration) {
|
|
|
24770
25435
|
await handle?.close();
|
|
24771
25436
|
}
|
|
24772
25437
|
}
|
|
24773
|
-
function
|
|
25438
|
+
function isRecord4(value) {
|
|
24774
25439
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24775
25440
|
}
|
|
24776
25441
|
function isNodeError(value) {
|
|
@@ -24778,11 +25443,8 @@ function isNodeError(value) {
|
|
|
24778
25443
|
}
|
|
24779
25444
|
|
|
24780
25445
|
// apps/product-cli/dist/composition.js
|
|
24781
|
-
import { randomUUID } from "node:crypto";
|
|
24782
|
-
import { join as
|
|
24783
|
-
|
|
24784
|
-
// packages/workos-auth-contracts/dist/user-api-keys.js
|
|
24785
|
-
var WORKOS_USER_API_KEY_PATTERN = /^sk_[A-Za-z0-9_-]{20,128}$/u;
|
|
25446
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
25447
|
+
import { join as join3 } from "node:path";
|
|
24786
25448
|
|
|
24787
25449
|
// packages/workos-auth-contracts/dist/device-flow.js
|
|
24788
25450
|
var WORKOS_AUTHKIT_DEVICE_AUTHORIZATION_ENDPOINT = "https://api.workos.com/user_management/authorize/device";
|
|
@@ -25012,37 +25674,99 @@ function environmentToken(environment = {}) {
|
|
|
25012
25674
|
throw new ProductApiAuthenticationError("invalid_environment_token");
|
|
25013
25675
|
return value;
|
|
25014
25676
|
}
|
|
25677
|
+
function environmentProfile(environment = {}) {
|
|
25678
|
+
const value = environment.OHMYHOST_PROFILE;
|
|
25679
|
+
if (value === void 0 || value === "")
|
|
25680
|
+
return void 0;
|
|
25681
|
+
if (!isProfileName(value))
|
|
25682
|
+
throw new ProductApiAuthenticationError("invalid_profile_binding");
|
|
25683
|
+
return value;
|
|
25684
|
+
}
|
|
25685
|
+
var profileRequest = (input, bound) => ({
|
|
25686
|
+
...input?.name === void 0 ? {} : { name: input.name },
|
|
25687
|
+
...bound === void 0 ? {} : { bound },
|
|
25688
|
+
...input?.organizationId === void 0 ? {} : { organizationId: input.organizationId },
|
|
25689
|
+
...input?.userId === void 0 ? {} : { userId: input.userId }
|
|
25690
|
+
});
|
|
25015
25691
|
var createProductionAuthenticatedProductApi = async (signal, overrides = {}) => {
|
|
25016
25692
|
const transport = await createAuthenticatedProductTransport(signal, overrides);
|
|
25017
25693
|
return new GeneratedSdkProductApi(transport.baseUrl, transport.accessToken, transport.fetch);
|
|
25018
25694
|
};
|
|
25019
25695
|
var createAuthenticatedProductTransport = async (signal, overrides = {}) => {
|
|
25020
|
-
const
|
|
25696
|
+
const platform = resolveProductCliEnvironment(overrides.environment);
|
|
25021
25697
|
const apiToken = environmentToken(overrides.environment);
|
|
25698
|
+
const bound = environmentProfile(overrides.environment);
|
|
25699
|
+
const baseUrl = overrides.productApiOrigin ?? platform.apiOrigin;
|
|
25022
25700
|
const boundedFetch = createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES);
|
|
25023
|
-
|
|
25701
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
25702
|
+
const binding = overrides.request?.bindOrganization === true;
|
|
25703
|
+
const request = profileRequest(overrides.request, bound);
|
|
25704
|
+
if (apiToken !== void 0) {
|
|
25705
|
+
await verifyEnvironmentTokenContext({
|
|
25706
|
+
token: apiToken,
|
|
25707
|
+
request: binding ? profileRequest({}, bound) : request,
|
|
25708
|
+
registry,
|
|
25709
|
+
baseUrl,
|
|
25710
|
+
fetch: boundedFetch
|
|
25711
|
+
});
|
|
25024
25712
|
return {
|
|
25025
|
-
baseUrl
|
|
25713
|
+
baseUrl,
|
|
25026
25714
|
accessToken: apiToken,
|
|
25027
25715
|
fetch: boundedFetch,
|
|
25028
|
-
|
|
25029
|
-
|
|
25716
|
+
workspace: void 0,
|
|
25717
|
+
context: { credential: "environment_token", profile: null }
|
|
25030
25718
|
};
|
|
25031
|
-
|
|
25032
|
-
const stored = await store.read();
|
|
25033
|
-
if (stored === void 0)
|
|
25034
|
-
throw new ProductApiAuthenticationError("authentication_required");
|
|
25719
|
+
}
|
|
25035
25720
|
const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
|
|
25036
25721
|
const refresher = overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now);
|
|
25037
|
-
const
|
|
25722
|
+
const profiles = await loadProfiles(registry, {
|
|
25723
|
+
baseUrl,
|
|
25724
|
+
fetch: boundedFetch,
|
|
25725
|
+
signal,
|
|
25726
|
+
now,
|
|
25727
|
+
refresher
|
|
25728
|
+
});
|
|
25729
|
+
const selection = binding && request.organizationId !== void 0 ? selectProfileForOrganization(profiles, {
|
|
25730
|
+
...request,
|
|
25731
|
+
organizationId: request.organizationId
|
|
25732
|
+
}) : selectProfile(profiles, request);
|
|
25733
|
+
if (selection.outcome !== "selected")
|
|
25734
|
+
throw profileSelectionError(selection, request, platform.commandPrefix, overrides.selector ?? "cli");
|
|
25735
|
+
const profile = selection.profile;
|
|
25736
|
+
const store = registry.credential(profile);
|
|
25737
|
+
const stored = await store.read();
|
|
25738
|
+
if (stored === void 0)
|
|
25739
|
+
throw missingLogin(profile, platform.commandPrefix);
|
|
25740
|
+
const credential = await currentCredential(stored, store, signal, now, refresher, () => registry.remove(profile));
|
|
25038
25741
|
return {
|
|
25039
|
-
baseUrl
|
|
25742
|
+
baseUrl,
|
|
25040
25743
|
accessToken: credential.accessToken,
|
|
25041
25744
|
fetch: boundedFetch,
|
|
25042
|
-
workspace: { store, refresher }
|
|
25745
|
+
workspace: { store, refresher, profiles: registry, profile },
|
|
25746
|
+
context: { credential: "profile", profile }
|
|
25747
|
+
};
|
|
25748
|
+
};
|
|
25749
|
+
var listSavedProfiles = async (signal, overrides = {}) => {
|
|
25750
|
+
const platform = resolveProductCliEnvironment(overrides.environment);
|
|
25751
|
+
const apiToken = environmentToken(overrides.environment);
|
|
25752
|
+
const bound = environmentProfile(overrides.environment);
|
|
25753
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
25754
|
+
const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
|
|
25755
|
+
const profiles = apiToken === void 0 ? await loadProfiles(registry, {
|
|
25756
|
+
baseUrl: overrides.productApiOrigin ?? platform.apiOrigin,
|
|
25757
|
+
fetch: createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES),
|
|
25758
|
+
signal,
|
|
25759
|
+
now,
|
|
25760
|
+
refresher: overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now)
|
|
25761
|
+
}) : await registry.list();
|
|
25762
|
+
return {
|
|
25763
|
+
profiles: profiles.map(publicProfile),
|
|
25764
|
+
bound: bound ?? null,
|
|
25765
|
+
environment_token: apiToken !== void 0
|
|
25043
25766
|
};
|
|
25044
25767
|
};
|
|
25045
|
-
var
|
|
25768
|
+
var missingLogin = (profile, commandPrefix) => new ProfileSelectionError("authentication_required", 5, `The saved login ${profile.name} has no credential on this computer. Run '${commandPrefix} login${profile.organizationId === null ? "" : ` --organization ${profile.organizationId}`} --profile-name ${profile.name} --json'.`);
|
|
25769
|
+
var currentCredential = async (stored, store, signal, now, tokenRefresher, forget) => {
|
|
25046
25770
|
const expiresAt = stored.expiresAt === void 0 ? NaN : Date.parse(stored.expiresAt);
|
|
25047
25771
|
if (Number.isFinite(expiresAt) && expiresAt > now().getTime() + REQUEST_TIMEOUT_MS) {
|
|
25048
25772
|
return stored;
|
|
@@ -25058,119 +25782,350 @@ var currentCredential = async (stored, store, signal, now, tokenRefresher) => {
|
|
|
25058
25782
|
return carried;
|
|
25059
25783
|
} catch (error) {
|
|
25060
25784
|
if (error instanceof TokenRefreshRejectedError) {
|
|
25061
|
-
await
|
|
25785
|
+
await forget();
|
|
25062
25786
|
throw new ProductApiAuthenticationError("authentication_required");
|
|
25063
25787
|
}
|
|
25064
25788
|
throw new ProductApiAuthenticationError("token_refresh_failed");
|
|
25065
25789
|
}
|
|
25066
25790
|
};
|
|
25791
|
+
var loadProfiles = async (registry, context) => {
|
|
25792
|
+
const legacy = registry.legacy();
|
|
25793
|
+
const stored = await legacy.read();
|
|
25794
|
+
if (stored !== void 0)
|
|
25795
|
+
await migrateLegacyLogin(registry, legacy, stored, context);
|
|
25796
|
+
return registry.list();
|
|
25797
|
+
};
|
|
25798
|
+
var migrateLegacyLogin = async (registry, legacy, stored, context) => {
|
|
25799
|
+
let credential;
|
|
25800
|
+
try {
|
|
25801
|
+
credential = await currentCredential(stored, legacy, context.signal, context.now, context.refresher, () => legacy.clear());
|
|
25802
|
+
} catch (error) {
|
|
25803
|
+
if (error instanceof ProductApiAuthenticationError && error.code === "authentication_required")
|
|
25804
|
+
return;
|
|
25805
|
+
throw error;
|
|
25806
|
+
}
|
|
25807
|
+
const api = new GeneratedSdkProductApi(context.baseUrl, credential.accessToken, context.fetch);
|
|
25808
|
+
let identity;
|
|
25809
|
+
try {
|
|
25810
|
+
identity = await api.getCurrentIdentity();
|
|
25811
|
+
} catch (error) {
|
|
25812
|
+
if (!isProblem(error, "unauthenticated"))
|
|
25813
|
+
throw error;
|
|
25814
|
+
return;
|
|
25815
|
+
}
|
|
25816
|
+
const userId = verifiedUserId(identity);
|
|
25817
|
+
const organizationId = parseCurrentIdentity(identity).organization_ids[0] ?? null;
|
|
25818
|
+
const organizationName = organizationId === null ? null : (await api.getAccountProfile()).organizations.find((organization) => organization.id === organizationId)?.name ?? null;
|
|
25819
|
+
const login = { userId, organizationId, organizationName };
|
|
25820
|
+
await registry.save({ name: defaultProfileName(await registry.list(), login), ...login }, credential);
|
|
25821
|
+
await legacy.clear();
|
|
25822
|
+
};
|
|
25823
|
+
var isProblem = (error, code) => typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
25824
|
+
var verifyEnvironmentTokenContext = async (input) => {
|
|
25825
|
+
const { request } = input;
|
|
25826
|
+
const named = request.name ?? request.bound;
|
|
25827
|
+
if (named === void 0 && request.organizationId === void 0 && request.userId === void 0)
|
|
25828
|
+
return null;
|
|
25829
|
+
const refuse = (target) => new ProfileSelectionError("environment_token_context_mismatch", 2, `${target} Unset OHMYHOST_TOKEN to use the saved login, or load the key of that account from its private env file. Nothing was sent.`);
|
|
25830
|
+
if (request.name !== void 0 && request.bound !== void 0 && request.name !== request.bound)
|
|
25831
|
+
throw refuse(`This call names profile ${request.name}, but the process is bound to OHMYHOST_PROFILE=${request.bound}.`);
|
|
25832
|
+
const profile = named === void 0 ? void 0 : (await input.registry.list()).find((saved) => saved.name === named);
|
|
25833
|
+
if (named !== void 0 && profile === void 0)
|
|
25834
|
+
throw refuse(`OHMYHOST_TOKEN overrides saved logins in this process, and no saved login is named ${named}.`);
|
|
25835
|
+
const { userId, organizationId } = await readTokenIdentity(input);
|
|
25836
|
+
const owner = `OHMYHOST_TOKEN belongs to user ${userId} in organization ${organizationId ?? "none"},`;
|
|
25837
|
+
if (profile !== void 0 && (profile.userId !== userId || profile.organizationId !== organizationId))
|
|
25838
|
+
throw refuse(`${owner} but this call names ${describeProfile(profile)}.`);
|
|
25839
|
+
if (request.organizationId !== void 0 && request.organizationId !== organizationId)
|
|
25840
|
+
throw refuse(`${owner} but this call targets organization ${request.organizationId}.`);
|
|
25841
|
+
if (request.userId !== void 0 && request.userId !== userId)
|
|
25842
|
+
throw refuse(`${owner} but this call targets user ${request.userId}.`);
|
|
25843
|
+
return { userId, organizationId };
|
|
25844
|
+
};
|
|
25845
|
+
var readTokenIdentity = async (input) => {
|
|
25846
|
+
const identity = parseCurrentIdentity(await new GeneratedSdkProductApi(input.baseUrl, input.token, input.fetch).getCurrentIdentity());
|
|
25847
|
+
return {
|
|
25848
|
+
userId: identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : identity.actor_id,
|
|
25849
|
+
organizationId: identity.organization_ids[0] ?? null
|
|
25850
|
+
};
|
|
25851
|
+
};
|
|
25852
|
+
var commandOrganizationId = (command) => {
|
|
25853
|
+
if (command.kind === "organization-use" || command.kind === "login")
|
|
25854
|
+
return void 0;
|
|
25855
|
+
if (command.kind === "feedback-submit")
|
|
25856
|
+
return command.report.organization_id;
|
|
25857
|
+
return "organizationId" in command && typeof command.organizationId === "string" ? command.organizationId : void 0;
|
|
25858
|
+
};
|
|
25859
|
+
var commandProjectId = (command) => {
|
|
25860
|
+
if (command.kind === "managed-mail")
|
|
25861
|
+
return command.request.projectId;
|
|
25862
|
+
return "projectId" in command && typeof command.projectId === "string" ? command.projectId : void 0;
|
|
25863
|
+
};
|
|
25067
25864
|
var runProductionCli = async (argv, environment, io, signal, overrides = {}) => {
|
|
25068
25865
|
const diagnostics = new StderrDiagnosticSink(io);
|
|
25866
|
+
const written = (result) => {
|
|
25867
|
+
io.writeStdout(result.stdout);
|
|
25868
|
+
if (result.stderr.length > 0)
|
|
25869
|
+
io.writeStderr(result.stderr);
|
|
25870
|
+
return result.exitCode;
|
|
25871
|
+
};
|
|
25872
|
+
let args;
|
|
25873
|
+
let profileName;
|
|
25874
|
+
try {
|
|
25875
|
+
const extracted = extractProfileName(argv);
|
|
25876
|
+
args = extracted.argv;
|
|
25877
|
+
profileName = extracted.profileName;
|
|
25878
|
+
} catch {
|
|
25879
|
+
return written(invalidCommandFailure(recognizedCommandLabel(argv)));
|
|
25880
|
+
}
|
|
25881
|
+
const workingDirectory = overrides.workingDirectory ?? process.cwd();
|
|
25069
25882
|
let command;
|
|
25883
|
+
let linked;
|
|
25884
|
+
let links = { links: [], legacy: null };
|
|
25070
25885
|
try {
|
|
25071
|
-
const parsed = await parseWithLinkedProject(
|
|
25072
|
-
|
|
25073
|
-
|
|
25886
|
+
const parsed = await parseWithLinkedProject(args, async () => {
|
|
25887
|
+
let platform2;
|
|
25888
|
+
try {
|
|
25889
|
+
platform2 = resolveProductCliEnvironment(environment);
|
|
25890
|
+
} catch {
|
|
25891
|
+
return { outcome: "none" };
|
|
25892
|
+
}
|
|
25893
|
+
links = await readProjectLinks(join3(workingDirectory, platform2.linkDirectory), platform2.apiOrigin);
|
|
25894
|
+
let named = profileName;
|
|
25895
|
+
try {
|
|
25896
|
+
named ??= environmentProfile(environment);
|
|
25897
|
+
} catch {
|
|
25898
|
+
}
|
|
25899
|
+
const profiles = named === void 0 ? [] : await (overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform2.credentialService)).list();
|
|
25900
|
+
return chooseLinkedProject(links, profiles.find((profile2) => profile2.name === named)?.organizationId ?? void 0);
|
|
25074
25901
|
});
|
|
25075
25902
|
command = parsed.command;
|
|
25076
|
-
|
|
25077
|
-
|
|
25903
|
+
linked = parsed.linked;
|
|
25904
|
+
if (linked !== null)
|
|
25905
|
+
io.writeStderr(`ohmyhost: using linked project ${linked.projectId}
|
|
25078
25906
|
`);
|
|
25079
25907
|
} catch (error) {
|
|
25080
|
-
|
|
25908
|
+
if (error instanceof LinkedProjectAmbiguousError)
|
|
25909
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "linked_project_selection_required", false, `This directory is linked to projects of several organizations (${error.organizationIds.join(", ")}). Pass --project ULID, or choose the login with --profile-name NAME; '${resolveProductCliEnvironment(environment).commandPrefix} profile list --json' shows the saved logins.`));
|
|
25910
|
+
if (error instanceof CredentialStoreUnavailableError)
|
|
25911
|
+
return written(errorDocument(recognizedCommandLabel(args), 3, "credential_store_unavailable", true, "Unlock or repair the selected credential store and retry."));
|
|
25912
|
+
const result = error instanceof InvalidCommandError ? invalidCommandFailure(recognizedCommandLabel(args)) : errorDocument("unknown", 1, "internal_error", false, "Retry the command.");
|
|
25081
25913
|
if (!(error instanceof InvalidCommandError)) {
|
|
25082
25914
|
diagnostics.record({
|
|
25083
|
-
correlationId:
|
|
25915
|
+
correlationId: randomUUID2(),
|
|
25084
25916
|
category: "internal",
|
|
25085
25917
|
subsystem: "parser",
|
|
25086
25918
|
errorKind: "unexpected"
|
|
25087
25919
|
});
|
|
25088
25920
|
}
|
|
25089
|
-
|
|
25090
|
-
return result2.exitCode;
|
|
25921
|
+
return written(result);
|
|
25091
25922
|
}
|
|
25092
25923
|
if (command.kind === "init") {
|
|
25924
|
+
if (profileName !== void 0)
|
|
25925
|
+
return written(invalidCommandFailure("init"));
|
|
25093
25926
|
return runOfflineInit(command, io, diagnostics);
|
|
25094
25927
|
}
|
|
25095
25928
|
if (command.kind === "help" || command.kind === "version") {
|
|
25096
|
-
|
|
25097
|
-
io.writeStdout(result2.stdout);
|
|
25098
|
-
return result2.exitCode;
|
|
25929
|
+
return written(cliMetadataResult(command.kind, command.kind === "help" ? command.topic : void 0));
|
|
25099
25930
|
}
|
|
25100
|
-
let
|
|
25931
|
+
let platform;
|
|
25101
25932
|
try {
|
|
25102
|
-
|
|
25933
|
+
platform = resolveProductCliEnvironment(environment);
|
|
25103
25934
|
} catch {
|
|
25104
|
-
|
|
25105
|
-
io.writeStdout(result2.stdout);
|
|
25106
|
-
return result2.exitCode;
|
|
25935
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "invalid_environment", false, "Set OHMYHOST_ENVIRONMENT to development or production."));
|
|
25107
25936
|
}
|
|
25108
25937
|
let apiToken;
|
|
25109
25938
|
try {
|
|
25110
25939
|
apiToken = environmentToken(environment);
|
|
25111
25940
|
} catch {
|
|
25112
|
-
|
|
25113
|
-
io.writeStdout(result2.stdout);
|
|
25114
|
-
return result2.exitCode;
|
|
25941
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "invalid_environment_token", false, "Set OHMYHOST_TOKEN to a valid user API key, or unset it to use interactive login."));
|
|
25115
25942
|
}
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
const selectedOverrides = {
|
|
25122
|
-
...overrides,
|
|
25123
|
-
credentialStore: overrides.credentialStore ?? new NativeNapiKeyringCredentialStore(void 0, profile.credentialService)
|
|
25124
|
-
};
|
|
25125
|
-
if (command.kind === "logout" && !command.revoke) {
|
|
25126
|
-
return runOfflineLogout(io, diagnostics, selectedOverrides);
|
|
25943
|
+
let bound;
|
|
25944
|
+
try {
|
|
25945
|
+
bound = environmentProfile(environment);
|
|
25946
|
+
} catch {
|
|
25947
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "invalid_profile_binding", false, `Set OHMYHOST_PROFILE to the name of a saved login ('${platform.commandPrefix} profile list --json'), or unset it.`));
|
|
25127
25948
|
}
|
|
25128
|
-
|
|
25949
|
+
if (apiToken !== void 0 && (command.kind === "login" || command.kind === "logout")) {
|
|
25950
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "environment_token_active", false, "Unset OHMYHOST_TOKEN before changing interactive login credentials. Logout does not revoke a user API key."));
|
|
25951
|
+
}
|
|
25952
|
+
const requiredToken = command.kind === "secret-set" ? command.environmentToken : void 0;
|
|
25953
|
+
const handedAccount2 = command.kind === "secret-set" ? requiredToken ?? command.profileAccount : void 0;
|
|
25954
|
+
if (requiredToken !== void 0 && apiToken === void 0)
|
|
25955
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "environment_token_required", false, `This command runs only with OHMYHOST_TOKEN holding a key of user ${requiredToken.userId} in organization ${requiredToken.organizationId}; a saved login cannot run it. Load that key from its private env file into this process and retry. Nothing was sent.`));
|
|
25956
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
25957
|
+
const linkDirectory = join3(workingDirectory, platform.linkDirectory);
|
|
25958
|
+
const projectId = commandProjectId(command);
|
|
25959
|
+
const organizationId = handedAccount2?.organizationId ?? commandOrganizationId(command) ?? linked?.organizationId ?? (projectId === void 0 ? void 0 : linkedOrganization(links, projectId));
|
|
25960
|
+
const request = profileRequest({
|
|
25961
|
+
...profileName === void 0 ? {} : { name: profileName },
|
|
25962
|
+
...organizationId === void 0 || organizationId === null ? {} : { organizationId },
|
|
25963
|
+
...handedAccount2 === void 0 ? {} : { userId: handedAccount2.userId }
|
|
25964
|
+
}, bound);
|
|
25129
25965
|
const transport = new FetchWorkosPublicTransport(signal, REQUEST_TIMEOUT_MS);
|
|
25966
|
+
const now = () => /* @__PURE__ */ new Date();
|
|
25967
|
+
const refresher = new PublicWorkosTokenRefresher(environment);
|
|
25968
|
+
const boundedFetch = () => createBoundedFetch(fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES);
|
|
25969
|
+
let profile;
|
|
25970
|
+
try {
|
|
25971
|
+
if (apiToken !== void 0) {
|
|
25972
|
+
if (command.kind !== "profile-list")
|
|
25973
|
+
await verifyEnvironmentTokenContext({
|
|
25974
|
+
token: apiToken,
|
|
25975
|
+
request,
|
|
25976
|
+
registry,
|
|
25977
|
+
baseUrl: platform.apiOrigin,
|
|
25978
|
+
fetch: boundedFetch()
|
|
25979
|
+
});
|
|
25980
|
+
if (linked !== null && linked.organizationId === null)
|
|
25981
|
+
await adoptLegacyLink({
|
|
25982
|
+
links,
|
|
25983
|
+
directory: linkDirectory,
|
|
25984
|
+
repositoryRoot: workingDirectory,
|
|
25985
|
+
apiOrigin: platform.apiOrigin,
|
|
25986
|
+
organizationId: (await readTokenIdentity({
|
|
25987
|
+
token: apiToken,
|
|
25988
|
+
baseUrl: platform.apiOrigin,
|
|
25989
|
+
fetch: boundedFetch()
|
|
25990
|
+
})).organizationId,
|
|
25991
|
+
accessToken: apiToken,
|
|
25992
|
+
fetch: boundedFetch(),
|
|
25993
|
+
actor: "OHMYHOST_TOKEN",
|
|
25994
|
+
commandPrefix: platform.commandPrefix
|
|
25995
|
+
});
|
|
25996
|
+
} else if (command.kind === "logout" && !command.revoke) {
|
|
25997
|
+
return await runOfflineLogout(registry, request, platform.commandPrefix, io, diagnostics);
|
|
25998
|
+
} else if (command.kind !== "login") {
|
|
25999
|
+
const profiles = await loadProfiles(registry, {
|
|
26000
|
+
baseUrl: platform.apiOrigin,
|
|
26001
|
+
fetch: boundedFetch(),
|
|
26002
|
+
signal,
|
|
26003
|
+
now,
|
|
26004
|
+
refresher
|
|
26005
|
+
});
|
|
26006
|
+
if (command.kind !== "profile-list") {
|
|
26007
|
+
const selection = command.kind === "organization-use" ? selectProfileForOrganization(profiles, {
|
|
26008
|
+
...request,
|
|
26009
|
+
organizationId: command.organizationId
|
|
26010
|
+
}) : selectProfile(profiles, request);
|
|
26011
|
+
if (selection.outcome !== "selected")
|
|
26012
|
+
throw profileSelectionError(selection, request, platform.commandPrefix, "cli");
|
|
26013
|
+
profile = selection.profile;
|
|
26014
|
+
io.writeStderr(`ohmyhost: acting as ${describeProfile(profile)}
|
|
26015
|
+
`);
|
|
26016
|
+
if (linked !== null && linked.organizationId === null) {
|
|
26017
|
+
const selected2 = await createAuthenticatedProductTransport(signal, {
|
|
26018
|
+
environment,
|
|
26019
|
+
profiles: registry,
|
|
26020
|
+
request: { name: profile.name }
|
|
26021
|
+
});
|
|
26022
|
+
await adoptLegacyLink({
|
|
26023
|
+
links,
|
|
26024
|
+
directory: linkDirectory,
|
|
26025
|
+
repositoryRoot: workingDirectory,
|
|
26026
|
+
apiOrigin: platform.apiOrigin,
|
|
26027
|
+
organizationId: profile.organizationId,
|
|
26028
|
+
accessToken: selected2.accessToken,
|
|
26029
|
+
fetch: selected2.fetch,
|
|
26030
|
+
actor: `profile ${profile.name}`,
|
|
26031
|
+
commandPrefix: platform.commandPrefix
|
|
26032
|
+
});
|
|
26033
|
+
}
|
|
26034
|
+
}
|
|
26035
|
+
}
|
|
26036
|
+
} catch (error) {
|
|
26037
|
+
return written(compositionFailure(recognizedCommandLabel(args), error, signal, platform.commandPrefix));
|
|
26038
|
+
}
|
|
26039
|
+
const selected = profile;
|
|
26040
|
+
const productApiOrigin = platform.apiOrigin;
|
|
25130
26041
|
const lifecycle = new GeneratedSdkSessionRevoker({
|
|
25131
26042
|
environment,
|
|
25132
|
-
|
|
26043
|
+
profiles: registry,
|
|
26044
|
+
...selected === void 0 ? {} : { request: { name: selected.name } }
|
|
25133
26045
|
});
|
|
25134
26046
|
const cli = new ProductCli({
|
|
25135
26047
|
...apiToken === void 0 ? {} : { apiToken },
|
|
25136
|
-
commandPrefix:
|
|
25137
|
-
|
|
26048
|
+
commandPrefix: platform.commandPrefix,
|
|
26049
|
+
profiles: registry,
|
|
26050
|
+
...selected === void 0 ? {} : { profile: selected },
|
|
26051
|
+
// A login in a process bound to one saved login renews exactly that one.
|
|
26052
|
+
...(profileName ?? bound) === void 0 ? {} : { profileName: profileName ?? bound },
|
|
26053
|
+
...bound === void 0 ? {} : { profileBinding: bound },
|
|
25138
26054
|
deviceFlow: new WorkosDeviceFlowClient({
|
|
25139
|
-
clientId:
|
|
26055
|
+
clientId: platform.clientId,
|
|
25140
26056
|
scope: "openid offline_access",
|
|
25141
|
-
verificationUri:
|
|
25142
|
-
}, transport, { now
|
|
26057
|
+
verificationUri: platform.verificationUri
|
|
26058
|
+
}, transport, { now }),
|
|
25143
26059
|
deviceAuthorizationPresenter: new StderrDeviceAuthorizationPresenter(io),
|
|
25144
|
-
productApi: (accessToken) => new GeneratedSdkProductApi(productApiOrigin, accessToken,
|
|
25145
|
-
// Login receives read-only workspace and GitHub queries instead of the product surface.
|
|
25146
|
-
accountProfile: async (accessToken) => await new GeneratedSdkProductApi(productApiOrigin, accessToken,
|
|
25147
|
-
|
|
26060
|
+
productApi: (accessToken) => new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()),
|
|
26061
|
+
// Login receives read-only identity, workspace and GitHub queries instead of the product surface.
|
|
26062
|
+
accountProfile: async (accessToken) => await new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()).getAccountProfile(),
|
|
26063
|
+
currentIdentity: async (accessToken) => await new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()).getCurrentIdentity(),
|
|
26064
|
+
githubConnectionStatus: (accessToken, organizationId2) => getGithubOrganizationConnection({ organization_id: organizationId2 }, {
|
|
25148
26065
|
client: createClient({
|
|
25149
26066
|
baseUrl: productApiOrigin,
|
|
25150
26067
|
auth: accessToken,
|
|
25151
26068
|
throwOnError: true,
|
|
25152
|
-
fetch:
|
|
26069
|
+
fetch: boundedFetch()
|
|
25153
26070
|
}),
|
|
25154
26071
|
signal
|
|
25155
26072
|
}),
|
|
25156
|
-
projectLinkStore: new OwnerOnlyProjectLinkStore(
|
|
25157
|
-
productApiOrigin
|
|
25158
|
-
tokenRefresher:
|
|
26073
|
+
projectLinkStore: new OwnerOnlyProjectLinkStore(linkDirectory, workingDirectory),
|
|
26074
|
+
productApiOrigin,
|
|
26075
|
+
tokenRefresher: refresher,
|
|
25159
26076
|
tokenRevoker: lifecycle,
|
|
25160
|
-
now
|
|
26077
|
+
now,
|
|
25161
26078
|
waitUntil,
|
|
25162
26079
|
lifecycleTimeoutMs: REQUEST_TIMEOUT_MS,
|
|
25163
|
-
createCorrelationId:
|
|
26080
|
+
createCorrelationId: randomUUID2,
|
|
25164
26081
|
diagnosticSink: diagnostics,
|
|
25165
26082
|
operationEventSink: { write: (value) => io.writeStdout(value) },
|
|
25166
26083
|
readSecretValue: overrides.readSecretValue ?? ((readSignal) => readSecretValueFromStandardInput(process.stdin, readSignal)),
|
|
25167
26084
|
spawnPsql: runLocalPsql
|
|
25168
26085
|
});
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
26086
|
+
return written(await cli.runCommand(command, signal));
|
|
26087
|
+
};
|
|
26088
|
+
var adoptLegacyLink = async (input) => {
|
|
26089
|
+
const legacy = input.links.legacy;
|
|
26090
|
+
if (legacy === null)
|
|
26091
|
+
return;
|
|
26092
|
+
const refuse = () => new ProfileSelectionError("linked_project_organization_mismatch", 2, `This directory's link names project ${legacy.project_id}, which ${input.actor} cannot see${input.organizationId === null ? "" : ` in organization ${input.organizationId}`}. Nothing was sent to it. Pass --project ULID or --profile-name NAME for the account that owns it, or run '${input.commandPrefix} link' again for this account.`);
|
|
26093
|
+
let organizationId;
|
|
26094
|
+
try {
|
|
26095
|
+
const project = await getProject({ project_id: legacy.project_id }, {
|
|
26096
|
+
client: createClient({
|
|
26097
|
+
baseUrl: input.apiOrigin,
|
|
26098
|
+
auth: input.accessToken,
|
|
26099
|
+
fetch: input.fetch,
|
|
26100
|
+
throwOnError: true
|
|
26101
|
+
})
|
|
26102
|
+
});
|
|
26103
|
+
organizationId = project.organization_id;
|
|
26104
|
+
} catch (error) {
|
|
26105
|
+
if (isProblem(error, "resource_not_found") || isProblem(error, "forbidden"))
|
|
26106
|
+
throw refuse();
|
|
26107
|
+
throw error;
|
|
26108
|
+
}
|
|
26109
|
+
if (organizationId !== input.organizationId)
|
|
26110
|
+
throw refuse();
|
|
26111
|
+
await new OwnerOnlyProjectLinkStore(input.directory, input.repositoryRoot).save({
|
|
26112
|
+
version: 2,
|
|
26113
|
+
api_origin: input.apiOrigin,
|
|
26114
|
+
organization_id: organizationId,
|
|
26115
|
+
project_id: legacy.project_id,
|
|
26116
|
+
installation_id: legacy.installation_id,
|
|
26117
|
+
repository_full_name: legacy.repository_full_name
|
|
26118
|
+
});
|
|
26119
|
+
await removeLegacyProjectLink(input.directory);
|
|
26120
|
+
};
|
|
26121
|
+
var compositionFailure = (command, error, signal, commandPrefix) => {
|
|
26122
|
+
if (error instanceof ProfileSelectionError)
|
|
26123
|
+
return errorDocument(command, error.exitCode, error.code, false, error.suggestedAction);
|
|
26124
|
+
if (error instanceof ProfileLimitError)
|
|
26125
|
+
return errorDocument(command, 2, "profile_limit_reached", false, profileLimitAction(commandPrefix));
|
|
26126
|
+
if (error instanceof ProductApiAuthenticationError)
|
|
26127
|
+
return error.code === "authentication_required" ? errorDocument(command, 5, "authentication_required", false, `Run '${commandPrefix} login --json'.`) : errorDocument(command, 5, "token_refresh_failed", true, `Run '${commandPrefix} login --json' if retrying does not succeed.`);
|
|
26128
|
+
return classifyCliFailure(command, error, signal);
|
|
25174
26129
|
};
|
|
25175
26130
|
var runOfflineInit = async (command, io, diagnostics) => {
|
|
25176
26131
|
try {
|
|
@@ -25208,7 +26163,7 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
25208
26163
|
} catch (error) {
|
|
25209
26164
|
const known = error instanceof RepositoryInitError;
|
|
25210
26165
|
diagnostics.record({
|
|
25211
|
-
correlationId:
|
|
26166
|
+
correlationId: randomUUID2(),
|
|
25212
26167
|
category: known ? "configuration" : "internal",
|
|
25213
26168
|
subsystem: "runtime",
|
|
25214
26169
|
errorKind: known ? "invalid_response" : "unexpected"
|
|
@@ -25329,7 +26284,7 @@ var GeneratedSdkSessionRevoker = class {
|
|
|
25329
26284
|
fetch: transport.fetch,
|
|
25330
26285
|
throwOnError: true
|
|
25331
26286
|
});
|
|
25332
|
-
const result = await revokeCurrentSession({ "Idempotency-Key":
|
|
26287
|
+
const result = await revokeCurrentSession({ "Idempotency-Key": randomUUID2() }, { client: client2, signal });
|
|
25333
26288
|
if (result.revoked !== true)
|
|
25334
26289
|
throw new LifecycleTransportError();
|
|
25335
26290
|
} catch (error) {
|
|
@@ -25339,25 +26294,27 @@ var GeneratedSdkSessionRevoker = class {
|
|
|
25339
26294
|
}
|
|
25340
26295
|
}
|
|
25341
26296
|
};
|
|
25342
|
-
|
|
26297
|
+
var LinkedProjectAmbiguousError = class extends Error {
|
|
26298
|
+
organizationIds;
|
|
26299
|
+
constructor(organizationIds) {
|
|
26300
|
+
super("Several organizations' links apply");
|
|
26301
|
+
this.organizationIds = organizationIds;
|
|
26302
|
+
this.name = "LinkedProjectAmbiguousError";
|
|
26303
|
+
}
|
|
26304
|
+
};
|
|
26305
|
+
async function parseWithLinkedProject(argv, linkedProject) {
|
|
25343
26306
|
try {
|
|
25344
|
-
return { command: parseProductCliCommand(argv),
|
|
26307
|
+
return { command: parseProductCliCommand(argv), linked: null };
|
|
25345
26308
|
} catch (error) {
|
|
25346
26309
|
if (!(error instanceof InvalidCommandError) || argv.includes("--project"))
|
|
25347
26310
|
throw error;
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
projectId = null;
|
|
25353
|
-
}
|
|
25354
|
-
if (projectId === null)
|
|
26311
|
+
const linked = await linkedProject();
|
|
26312
|
+
if (linked.outcome === "ambiguous")
|
|
26313
|
+
throw new LinkedProjectAmbiguousError(linked.organizationIds);
|
|
26314
|
+
if (linked.outcome === "none")
|
|
25355
26315
|
throw error;
|
|
25356
26316
|
try {
|
|
25357
|
-
return {
|
|
25358
|
-
command: parseProductCliCommand([...argv, "--project", projectId]),
|
|
25359
|
-
linkedProjectId: projectId
|
|
25360
|
-
};
|
|
26317
|
+
return { command: parseProductCliCommand([...argv, "--project", linked.projectId]), linked };
|
|
25361
26318
|
} catch {
|
|
25362
26319
|
throw error;
|
|
25363
26320
|
}
|
|
@@ -25485,14 +26442,25 @@ var waitUntil = async (instant, signal) => {
|
|
|
25485
26442
|
};
|
|
25486
26443
|
var boundedSignal2 = (parent, timeoutMs) => AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
|
|
25487
26444
|
var isAbortOrTimeout = (error) => error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
25488
|
-
var runOfflineLogout = async (io, diagnostics
|
|
26445
|
+
var runOfflineLogout = async (registry, request, commandPrefix, io, diagnostics) => {
|
|
25489
26446
|
let result;
|
|
25490
26447
|
try {
|
|
25491
|
-
|
|
25492
|
-
|
|
26448
|
+
const selection = selectProfile(await registry.list(), request);
|
|
26449
|
+
if (selection.outcome === "selected") {
|
|
26450
|
+
await registry.remove(selection.profile);
|
|
26451
|
+
result = resultDocument("logout", 0, "logged_out", {
|
|
26452
|
+
profile: publicProfile(selection.profile)
|
|
26453
|
+
});
|
|
26454
|
+
} else if (selection.outcome === "none" && request.name === void 0 && request.bound === void 0) {
|
|
26455
|
+
await registry.legacy().clear();
|
|
26456
|
+
result = resultDocument("logout", 0, "logged_out");
|
|
26457
|
+
} else {
|
|
26458
|
+
const failure2 = profileSelectionError(selection, request, commandPrefix, "cli");
|
|
26459
|
+
result = errorDocument("logout", failure2.exitCode, failure2.code, false, failure2.suggestedAction);
|
|
26460
|
+
}
|
|
25493
26461
|
} catch {
|
|
25494
26462
|
diagnostics.record({
|
|
25495
|
-
correlationId:
|
|
26463
|
+
correlationId: randomUUID2(),
|
|
25496
26464
|
category: "credential_store",
|
|
25497
26465
|
subsystem: "credential_store",
|
|
25498
26466
|
errorKind: "unavailable"
|
|
@@ -25533,9 +26501,9 @@ var readSecretValueFromStandardInput = async (input, signal) => {
|
|
|
25533
26501
|
}
|
|
25534
26502
|
};
|
|
25535
26503
|
var invalidCommandFailure = (command) => errorDocument(command, 2, "invalid_command", false, "Check 'ohmyhost --help'.");
|
|
25536
|
-
var resultDocument = (command, exitCode, status) => ({
|
|
26504
|
+
var resultDocument = (command, exitCode, status, body = {}) => ({
|
|
25537
26505
|
exitCode,
|
|
25538
|
-
stdout: `${JSON.stringify({ version: 1, command, status })}
|
|
26506
|
+
stdout: `${JSON.stringify({ version: 1, command, status, ...body })}
|
|
25539
26507
|
`,
|
|
25540
26508
|
stderr: ""
|
|
25541
26509
|
});
|
|
@@ -25558,15 +26526,26 @@ var errorDocument = (command, exitCode, code, retryable, suggestedAction) => ({
|
|
|
25558
26526
|
export {
|
|
25559
26527
|
UserTokenFileError,
|
|
25560
26528
|
prepareUserTokenFile,
|
|
26529
|
+
ProfileNameConflictError,
|
|
26530
|
+
ProfileLimitError,
|
|
26531
|
+
isProfileName,
|
|
26532
|
+
isProfileUserId,
|
|
26533
|
+
sameLogin,
|
|
26534
|
+
profileIndexDirectory,
|
|
26535
|
+
NativeKeyringProfileRegistry,
|
|
26536
|
+
ProfileIndexFiles,
|
|
26537
|
+
selectProfile,
|
|
26538
|
+
selectProfileForOrganization,
|
|
26539
|
+
defaultProfileName,
|
|
26540
|
+
publicProfile,
|
|
26541
|
+
profileLimitAction,
|
|
26542
|
+
ProfileSelectionError,
|
|
26543
|
+
profileSelectionError,
|
|
26544
|
+
describeProfile,
|
|
26545
|
+
extractProfileName,
|
|
25561
26546
|
InvalidCommandError,
|
|
25562
26547
|
recognizedCommandLabel,
|
|
25563
26548
|
parseProductCliCommand,
|
|
25564
|
-
InteractiveSessionRequiredError,
|
|
25565
|
-
chooseWorkspace,
|
|
25566
|
-
selectWorkspace,
|
|
25567
|
-
selectedWorkspaceId,
|
|
25568
|
-
workspaceOf,
|
|
25569
|
-
publicWorkspace,
|
|
25570
26549
|
parsePaidDomainPlan,
|
|
25571
26550
|
parsePaidDomain,
|
|
25572
26551
|
parseCloudflareDnsAuthorization,
|
|
@@ -25578,6 +26557,15 @@ export {
|
|
|
25578
26557
|
parseOperationEvent,
|
|
25579
26558
|
parseSafeProblem,
|
|
25580
26559
|
parseOperation,
|
|
26560
|
+
InteractiveSessionRequiredError,
|
|
26561
|
+
ProfileOrganizationFixedError,
|
|
26562
|
+
AccountVerificationError,
|
|
26563
|
+
chooseWorkspace,
|
|
26564
|
+
verifiedUserId,
|
|
26565
|
+
assertVerifiedLogin,
|
|
26566
|
+
selectWorkspace,
|
|
26567
|
+
workspaceOf,
|
|
26568
|
+
publicWorkspace,
|
|
25581
26569
|
NetworkTransportError,
|
|
25582
26570
|
LifecycleTransportError,
|
|
25583
26571
|
TokenRefreshRejectedError,
|
|
@@ -25587,8 +26575,13 @@ export {
|
|
|
25587
26575
|
PsqlUnavailableError,
|
|
25588
26576
|
cliMetadataResult,
|
|
25589
26577
|
ProductCli,
|
|
26578
|
+
ACCOUNT_SENSITIVE_PROBLEMS,
|
|
26579
|
+
classifyCliFailure,
|
|
25590
26580
|
GeneratedSdkProductApi,
|
|
25591
|
-
|
|
26581
|
+
readProjectLinks,
|
|
26582
|
+
chooseLinkedProject,
|
|
26583
|
+
linkedOrganization,
|
|
26584
|
+
removeLegacyProjectLink,
|
|
25592
26585
|
OwnerOnlyProjectLinkStore,
|
|
25593
26586
|
RepositoryInitError,
|
|
25594
26587
|
RepositoryInitBlockedError,
|
|
@@ -25598,10 +26591,13 @@ export {
|
|
|
25598
26591
|
resolveProductCliEnvironment,
|
|
25599
26592
|
createProductionAuthenticatedProductApi,
|
|
25600
26593
|
createAuthenticatedProductTransport,
|
|
26594
|
+
listSavedProfiles,
|
|
26595
|
+
loadProfiles,
|
|
25601
26596
|
runProductionCli,
|
|
25602
26597
|
StderrDeviceAuthorizationPresenter,
|
|
25603
26598
|
PublicWorkosTokenRefresher,
|
|
25604
26599
|
GeneratedSdkSessionRevoker,
|
|
26600
|
+
LinkedProjectAmbiguousError,
|
|
25605
26601
|
parseWithLinkedProject,
|
|
25606
26602
|
readBoundedResponseBody,
|
|
25607
26603
|
createBoundedFetch,
|