@amerged/ohmyhost-cli 0.1.19 → 0.1.21
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-4HFQ54FP.js → chunk-7D2RFLWM.js} +7 -3
- package/dist/{chunk-57UUB7RK.js → chunk-EPTONNNW.js} +1449 -306
- package/dist/credential-store.js +5 -1
- package/dist/index.js +70 -6
- package/package.json +1 -1
- package/src/apps/product-cli/cli.ts +370 -92
- package/src/apps/product-cli/command.ts +163 -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 +4 -0
- package/src/apps/product-cli/product-api.ts +32 -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 +63 -7
- package/src/apps/product-cli/workspace.ts +75 -13
- package/src/packages/contracts/operation-failure.ts +3 -3
- package/src/packages/sdk-ts/generated/index.ts +21 -0
- package/src/packages/sdk-ts/generated/sdk.gen.ts +142 -0
- package/src/packages/sdk-ts/generated/types.gen.ts +200 -2
|
@@ -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")
|
|
@@ -14958,6 +15358,8 @@ var recognizedCommandLabel = (argv) => {
|
|
|
14958
15358
|
return "operation get";
|
|
14959
15359
|
if (value === "credits" && values[0] === "account")
|
|
14960
15360
|
return "credits account";
|
|
15361
|
+
if (value === "referral" && values[0] === "link")
|
|
15362
|
+
return "referral link";
|
|
14961
15363
|
if (value === "credits" && values[0] === "balance")
|
|
14962
15364
|
return "credits balance";
|
|
14963
15365
|
if (value === "billing" && values[0] === "recharge" && values[1] === "get")
|
|
@@ -15012,6 +15414,10 @@ var recognizedCommandLabel = (argv) => {
|
|
|
15012
15414
|
return "project dev-access mode";
|
|
15013
15415
|
if (value === "project" && values[0] === "dev-access" && values[1] === "create")
|
|
15014
15416
|
return "project dev-access create";
|
|
15417
|
+
if (value === "project" && values[0] === "flag" && values[1] === "status")
|
|
15418
|
+
return "project flag status";
|
|
15419
|
+
if (value === "project" && values[0] === "flag" && values[1] === "set")
|
|
15420
|
+
return "project flag set";
|
|
15015
15421
|
if (value === "database" && values[0] === "compute" && values[1] === "set")
|
|
15016
15422
|
return "database compute set";
|
|
15017
15423
|
if (value === "database" && values[0] === "compute" && values[1] === "get")
|
|
@@ -15159,16 +15565,20 @@ var parseProductCliCommand = (argv) => {
|
|
|
15159
15565
|
if (dryRun)
|
|
15160
15566
|
throw new InvalidCommandError();
|
|
15161
15567
|
if (first === "login" && !revoke && !yes && !wait && !follow && !stdin) {
|
|
15162
|
-
const options = parseOptionalOptions(values, ["--organization"]);
|
|
15568
|
+
const options = parseOptionalOptions(values, ["--organization", "--user"]);
|
|
15163
15569
|
const organizationId = options["--organization"];
|
|
15164
|
-
|
|
15570
|
+
const userId = options["--user"];
|
|
15571
|
+
if (organizationId !== void 0 && !ORGANIZATION_ULID.test(organizationId) || userId !== void 0 && !isProfileUserId(userId))
|
|
15165
15572
|
throw new InvalidCommandError();
|
|
15166
15573
|
return {
|
|
15167
15574
|
kind: "login",
|
|
15168
15575
|
...organizationId === void 0 ? {} : { organizationId },
|
|
15576
|
+
...userId === void 0 ? {} : { userId },
|
|
15169
15577
|
credentialStore
|
|
15170
15578
|
};
|
|
15171
15579
|
}
|
|
15580
|
+
if (first === "profile" && values.length === 1 && values[0] === "list" && !revoke && !yes && !wait && !follow && !stdin)
|
|
15581
|
+
return { kind: "profile-list" };
|
|
15172
15582
|
if (first === "logout") {
|
|
15173
15583
|
if (!yes && !wait && !follow && !stdin && values.length === 0)
|
|
15174
15584
|
return { kind: "logout", revoke, credentialStore };
|
|
@@ -15352,6 +15762,13 @@ var parseProductCliCommand = (argv) => {
|
|
|
15352
15762
|
const options = parseOptions(values, ["--organization"]);
|
|
15353
15763
|
return { kind: "credits-account", organizationId: options["--organization"], credentialStore };
|
|
15354
15764
|
}
|
|
15765
|
+
if (first === "referral" && values[0] === "link") {
|
|
15766
|
+
values.shift();
|
|
15767
|
+
if (yes || wait || follow || stdin)
|
|
15768
|
+
throw new InvalidCommandError();
|
|
15769
|
+
const options = parseOptions(values, ["--organization"]);
|
|
15770
|
+
return { kind: "referral-link", organizationId: options["--organization"], credentialStore };
|
|
15771
|
+
}
|
|
15355
15772
|
if (first === "credits" && values.shift() === "balance") {
|
|
15356
15773
|
if (yes || wait || follow || stdin)
|
|
15357
15774
|
throw new InvalidCommandError();
|
|
@@ -15548,6 +15965,28 @@ var parseProductCliCommand = (argv) => {
|
|
|
15548
15965
|
credentialStore
|
|
15549
15966
|
};
|
|
15550
15967
|
}
|
|
15968
|
+
if (subcommand === "flag") {
|
|
15969
|
+
const action = values.shift();
|
|
15970
|
+
if (yes || wait || follow || stdin)
|
|
15971
|
+
throw new InvalidCommandError();
|
|
15972
|
+
if (action === "status") {
|
|
15973
|
+
const options2 = parseOptions(values, ["--project"]);
|
|
15974
|
+
return { kind: "project-flag-status", projectId: options2["--project"], credentialStore };
|
|
15975
|
+
}
|
|
15976
|
+
if (action === "set") {
|
|
15977
|
+
const options2 = parseOptions(values, ["--project", "--enabled", "--idempotency-key"]);
|
|
15978
|
+
if (options2["--enabled"] !== "true" && options2["--enabled"] !== "false")
|
|
15979
|
+
throw new InvalidCommandError();
|
|
15980
|
+
return {
|
|
15981
|
+
kind: "project-flag-set",
|
|
15982
|
+
projectId: options2["--project"],
|
|
15983
|
+
enabled: options2["--enabled"] === "true",
|
|
15984
|
+
idempotencyKey: options2["--idempotency-key"],
|
|
15985
|
+
credentialStore
|
|
15986
|
+
};
|
|
15987
|
+
}
|
|
15988
|
+
throw new InvalidCommandError();
|
|
15989
|
+
}
|
|
15551
15990
|
if (subcommand === "dev-access" && values.shift() === "create") {
|
|
15552
15991
|
if (yes || wait || follow || stdin)
|
|
15553
15992
|
throw new InvalidCommandError();
|
|
@@ -16092,15 +16531,32 @@ var parseProductCliCommand = (argv) => {
|
|
|
16092
16531
|
const name = values.shift();
|
|
16093
16532
|
if (!isUserSecretName(name))
|
|
16094
16533
|
throw new InvalidCommandError();
|
|
16095
|
-
const options =
|
|
16534
|
+
const options = parseOptionalOptions(values, [
|
|
16535
|
+
"--project",
|
|
16536
|
+
"--environment",
|
|
16537
|
+
"--idempotency-key",
|
|
16538
|
+
"--token-user",
|
|
16539
|
+
"--token-organization",
|
|
16540
|
+
"--profile-user",
|
|
16541
|
+
"--profile-organization"
|
|
16542
|
+
]);
|
|
16543
|
+
const projectId = options["--project"];
|
|
16544
|
+
const environmentId = options["--environment"];
|
|
16545
|
+
const idempotencyKey = options["--idempotency-key"];
|
|
16546
|
+
const environmentToken2 = handedAccount(options["--token-user"], options["--token-organization"]);
|
|
16547
|
+
const profileAccount2 = handedAccount(options["--profile-user"], options["--profile-organization"]);
|
|
16548
|
+
if (projectId === void 0 || environmentId === void 0 || idempotencyKey === void 0 || environmentToken2 !== void 0 && profileAccount2 !== void 0)
|
|
16549
|
+
throw new InvalidCommandError();
|
|
16096
16550
|
return {
|
|
16097
16551
|
kind: "secret-set",
|
|
16098
|
-
projectId
|
|
16099
|
-
environmentId
|
|
16552
|
+
projectId,
|
|
16553
|
+
environmentId,
|
|
16100
16554
|
name,
|
|
16101
|
-
idempotencyKey
|
|
16555
|
+
idempotencyKey,
|
|
16102
16556
|
stdin,
|
|
16103
16557
|
wait,
|
|
16558
|
+
...environmentToken2 === void 0 ? {} : { environmentToken: environmentToken2 },
|
|
16559
|
+
...profileAccount2 === void 0 ? {} : { profileAccount: profileAccount2 },
|
|
16104
16560
|
credentialStore
|
|
16105
16561
|
};
|
|
16106
16562
|
}
|
|
@@ -16349,6 +16805,13 @@ var accessTtlSeconds = (value) => {
|
|
|
16349
16805
|
return seconds;
|
|
16350
16806
|
};
|
|
16351
16807
|
var ORGANIZATION_ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
16808
|
+
var handedAccount = (userId, organizationId) => {
|
|
16809
|
+
if (userId === void 0 && organizationId === void 0)
|
|
16810
|
+
return void 0;
|
|
16811
|
+
if (userId === void 0 || organizationId === void 0 || !isProfileUserId(userId) || WORKOS_USER_API_KEY_PATTERN.test(userId) || !ORGANIZATION_ULID.test(organizationId))
|
|
16812
|
+
throw new InvalidCommandError();
|
|
16813
|
+
return { userId, organizationId };
|
|
16814
|
+
};
|
|
16352
16815
|
var isValue = (value) => value !== void 0 && value.length > 0 && !value.startsWith("--");
|
|
16353
16816
|
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
16817
|
const code = character.codePointAt(0) ?? 0;
|
|
@@ -16357,46 +16820,6 @@ var validGithubBranchName = (value) => !(value.length < 1 || value.length > 1024
|
|
|
16357
16820
|
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
16821
|
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
16822
|
|
|
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
16823
|
// packages/contracts/dist/feedback.js
|
|
16401
16824
|
var FEEDBACK_HISTORY_PAGE_SIZE = 25;
|
|
16402
16825
|
var FEEDBACK_REPLY_MAX_LENGTH = 2e3;
|
|
@@ -16801,7 +17224,7 @@ function assertOrganizationAccount(value) {
|
|
|
16801
17224
|
var failures = {
|
|
16802
17225
|
operation_abandoned: [
|
|
16803
17226
|
"The operation stopped renewing its execution lease and was ended.",
|
|
16804
|
-
"Inspect project status and
|
|
17227
|
+
"Inspect project status and, when this operation names a deployment_id, its diagnostics with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs before creating a fresh plan. Keep this operation ID; the expired execution cannot resume."
|
|
16805
17228
|
],
|
|
16806
17229
|
database_write_rejected: [
|
|
16807
17230
|
"The database rejected this write; it was not committed.",
|
|
@@ -16865,7 +17288,7 @@ var failures = {
|
|
|
16865
17288
|
],
|
|
16866
17289
|
build_failed: [
|
|
16867
17290
|
"The build did not produce a verified deployable artifact.",
|
|
16868
|
-
"
|
|
17291
|
+
"Read the BUILD_FAILED diagnostic of this operation's deployment_id with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs; its excerpt is the tail of the install and build output. Correct that failure and verify the commands for the admitted commit before planning a new deployment."
|
|
16869
17292
|
],
|
|
16870
17293
|
insufficient_organization_credits: [
|
|
16871
17294
|
"The organization does not have sufficient available credits for this operation.",
|
|
@@ -16881,7 +17304,7 @@ var failures = {
|
|
|
16881
17304
|
],
|
|
16882
17305
|
runtime_candidate_failed: [
|
|
16883
17306
|
"The deployed candidate failed its runtime checks.",
|
|
16884
|
-
"
|
|
17307
|
+
"Read the HEALTH_CHECK_FAILED diagnostic of this operation's deployment_id with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs. Its route must answer 2xx to a cookieless GET without a redirect; status_code and, for owners, excerpt show what it answered. Fix that, often a missing secret or a redirect, then plan a new deployment."
|
|
16885
17308
|
],
|
|
16886
17309
|
runtime_candidate_rejected: [
|
|
16887
17310
|
"The runtime rejected the built Worker script.",
|
|
@@ -16923,7 +17346,7 @@ var PROBLEM_POLICY = {
|
|
|
16923
17346
|
github_connection_required: {
|
|
16924
17347
|
status: 409,
|
|
16925
17348
|
retryable: false,
|
|
16926
|
-
action: "Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for
|
|
17349
|
+
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
17350
|
},
|
|
16928
17351
|
github_connection_revoked: {
|
|
16929
17352
|
status: 409,
|
|
@@ -16997,7 +17420,11 @@ var PROBLEM_POLICY = {
|
|
|
16997
17420
|
retryable: false,
|
|
16998
17421
|
action: "Run 'ohmyhost organization list --json' and select one with 'ohmyhost organization use --organization ULID --json', or create the first workspace."
|
|
16999
17422
|
},
|
|
17000
|
-
resource_not_found: {
|
|
17423
|
+
resource_not_found: {
|
|
17424
|
+
status: 404,
|
|
17425
|
+
retryable: false,
|
|
17426
|
+
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."
|
|
17427
|
+
},
|
|
17001
17428
|
idempotency_key_reused: {
|
|
17002
17429
|
status: 409,
|
|
17003
17430
|
retryable: false,
|
|
@@ -17134,6 +17561,11 @@ var PROBLEM_POLICY = {
|
|
|
17134
17561
|
retryable: false,
|
|
17135
17562
|
action: "Finish database provisioning, then request the export again. No backup was accepted."
|
|
17136
17563
|
},
|
|
17564
|
+
powered_by_flag_required: {
|
|
17565
|
+
status: 409,
|
|
17566
|
+
retryable: false,
|
|
17567
|
+
action: "Keep the flag on, remove the custom domain with 'ohmyhost domain paid delete', or buy Paid, then switch the flag off."
|
|
17568
|
+
},
|
|
17137
17569
|
rate_limited: { status: 429, retryable: true, action: "Retry after the server delay." },
|
|
17138
17570
|
insufficient_organization_credits: {
|
|
17139
17571
|
status: 402,
|
|
@@ -17148,7 +17580,7 @@ var PROBLEM_POLICY = {
|
|
|
17148
17580
|
paid_plan_required: {
|
|
17149
17581
|
status: 402,
|
|
17150
17582
|
retryable: false,
|
|
17151
|
-
action: "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid."
|
|
17583
|
+
action: "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid. For a website domain alone, a Free workspace may instead show the powered-by flag on the project."
|
|
17152
17584
|
},
|
|
17153
17585
|
interactive_login_required: {
|
|
17154
17586
|
status: 403,
|
|
@@ -17366,6 +17798,18 @@ var parseDevAccessState = (value) => {
|
|
|
17366
17798
|
throw new ResponseContractError();
|
|
17367
17799
|
return input;
|
|
17368
17800
|
};
|
|
17801
|
+
var parsePoweredByFlag = (value) => {
|
|
17802
|
+
const input = exactRecord2(value, ["project_id", "enabled", "enabled_at"]);
|
|
17803
|
+
if (!isUlid(input["project_id"]) || typeof input["enabled"] !== "boolean" || (input["enabled"] ? !isIsoInstant(input["enabled_at"]) : input["enabled_at"] !== null))
|
|
17804
|
+
throw new ResponseContractError();
|
|
17805
|
+
return input;
|
|
17806
|
+
};
|
|
17807
|
+
var parseOrganizationReferral = (value) => {
|
|
17808
|
+
const input = exactRecord2(value, ["organization_id", "referral_url"]);
|
|
17809
|
+
if (!isUlid(input["organization_id"]) || input["referral_url"] !== `https://ohmyho.st/?r=ref-${String(input["organization_id"]).toLowerCase()}`)
|
|
17810
|
+
throw new ResponseContractError();
|
|
17811
|
+
return input;
|
|
17812
|
+
};
|
|
17369
17813
|
var parseDevAccessTicket = (value) => {
|
|
17370
17814
|
const input = exactRecord2(value, [
|
|
17371
17815
|
"ticket_id",
|
|
@@ -17488,7 +17932,7 @@ var parseAccountProfile = (value) => {
|
|
|
17488
17932
|
});
|
|
17489
17933
|
if (new Set(workspaces.map((workspace) => workspace.id)).size !== organizationIds.length)
|
|
17490
17934
|
throw new ResponseContractError();
|
|
17491
|
-
return workspaces;
|
|
17935
|
+
return { userId: input["user_id"], organizations: workspaces };
|
|
17492
17936
|
};
|
|
17493
17937
|
var parseProject = (value) => {
|
|
17494
17938
|
const input = exactRecord2(value, [
|
|
@@ -17825,6 +18269,7 @@ var parseSafeProblem = (value) => {
|
|
|
17825
18269
|
var parseOperation = (value) => {
|
|
17826
18270
|
const input = exactRecord2(value, [
|
|
17827
18271
|
"blocking_operation_id",
|
|
18272
|
+
"deployment_id",
|
|
17828
18273
|
"id",
|
|
17829
18274
|
"state",
|
|
17830
18275
|
"created_at",
|
|
@@ -17840,9 +18285,10 @@ var parseOperation = (value) => {
|
|
|
17840
18285
|
const updatedAt = input["updated_at"];
|
|
17841
18286
|
const result = input["result"];
|
|
17842
18287
|
const blockingOperationId = input["blocking_operation_id"];
|
|
17843
|
-
|
|
18288
|
+
const deploymentId = input["deployment_id"];
|
|
18289
|
+
if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3) || deploymentId !== void 0 && !isUlid(deploymentId))
|
|
17844
18290
|
throw new ResponseContractError();
|
|
17845
|
-
if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !
|
|
18291
|
+
if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !isRecord2(result)) {
|
|
17846
18292
|
throw new ResponseContractError();
|
|
17847
18293
|
}
|
|
17848
18294
|
let progress;
|
|
@@ -17887,6 +18333,7 @@ var parseOperation = (value) => {
|
|
|
17887
18333
|
created_at: createdAt,
|
|
17888
18334
|
updated_at: updatedAt,
|
|
17889
18335
|
...blockingOperationId === void 0 ? {} : { blocking_operation_id: blockingOperationId },
|
|
18336
|
+
...deploymentId === void 0 ? {} : { deployment_id: deploymentId },
|
|
17890
18337
|
...progress === void 0 ? {} : { progress },
|
|
17891
18338
|
...result === void 0 ? {} : { result },
|
|
17892
18339
|
...error === void 0 ? {} : { error },
|
|
@@ -17903,12 +18350,12 @@ function parseOperationFailure(value) {
|
|
|
17903
18350
|
return safe;
|
|
17904
18351
|
}
|
|
17905
18352
|
var exactRecord2 = (value, allowed) => {
|
|
17906
|
-
if (!
|
|
18353
|
+
if (!isRecord2(value) || Object.keys(value).some((key) => !allowed.includes(key))) {
|
|
17907
18354
|
throw new ResponseContractError();
|
|
17908
18355
|
}
|
|
17909
18356
|
return value;
|
|
17910
18357
|
};
|
|
17911
|
-
var
|
|
18358
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17912
18359
|
var boundedString = (value, minimum, maximum) => typeof value === "string" && value.length >= minimum && value.length <= maximum;
|
|
17913
18360
|
var isUlid = (value) => typeof value === "string" && /^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value);
|
|
17914
18361
|
var isIsoInstant = (value) => typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
@@ -17936,8 +18383,81 @@ var isPaidValidationRecord = (value) => {
|
|
|
17936
18383
|
};
|
|
17937
18384
|
var isHttpsUrlValue = (value) => typeof value === "string" && isHttpsUrl(value);
|
|
17938
18385
|
|
|
18386
|
+
// apps/product-cli/dist/workspace.js
|
|
18387
|
+
var InteractiveSessionRequiredError = class extends Error {
|
|
18388
|
+
constructor() {
|
|
18389
|
+
super("Workspace selection requires an interactive login");
|
|
18390
|
+
this.name = "InteractiveSessionRequiredError";
|
|
18391
|
+
}
|
|
18392
|
+
};
|
|
18393
|
+
var ProfileOrganizationFixedError = class extends Error {
|
|
18394
|
+
profile;
|
|
18395
|
+
organization;
|
|
18396
|
+
constructor(profile, organization) {
|
|
18397
|
+
super("The saved login is bound to another organization");
|
|
18398
|
+
this.profile = profile;
|
|
18399
|
+
this.organization = organization;
|
|
18400
|
+
this.name = "ProfileOrganizationFixedError";
|
|
18401
|
+
}
|
|
18402
|
+
};
|
|
18403
|
+
var AccountVerificationError = class extends Error {
|
|
18404
|
+
constructor() {
|
|
18405
|
+
super("The signed-in account does not match the requested one");
|
|
18406
|
+
this.name = "AccountVerificationError";
|
|
18407
|
+
}
|
|
18408
|
+
};
|
|
18409
|
+
var chooseWorkspace = (organizations, requested) => {
|
|
18410
|
+
if (requested !== void 0) {
|
|
18411
|
+
const match = organizations.find((organization) => organization.id === requested);
|
|
18412
|
+
return match === void 0 ? { outcome: "not_a_member", organizations } : { outcome: "selected", organization: match };
|
|
18413
|
+
}
|
|
18414
|
+
if (organizations.length === 0)
|
|
18415
|
+
return { outcome: "no_workspace" };
|
|
18416
|
+
const only = organizations[0];
|
|
18417
|
+
if (organizations.length === 1 && only !== void 0)
|
|
18418
|
+
return { outcome: "selected", organization: only };
|
|
18419
|
+
return { outcome: "choice_required", organizations };
|
|
18420
|
+
};
|
|
18421
|
+
var verifiedUserId = (identity) => {
|
|
18422
|
+
const actor = parseCurrentIdentity(identity).actor_id;
|
|
18423
|
+
if (!actor.startsWith("user:") || actor.length === 5)
|
|
18424
|
+
throw new AccountVerificationError();
|
|
18425
|
+
return actor.slice(5);
|
|
18426
|
+
};
|
|
18427
|
+
var assertVerifiedLogin = (identity, userId, organizationId) => {
|
|
18428
|
+
const organizations = parseCurrentIdentity(identity).organization_ids;
|
|
18429
|
+
if (verifiedUserId(identity) !== userId || (organizationId === null ? organizations.length !== 0 : organizations.length !== 1 || organizations[0] !== organizationId))
|
|
18430
|
+
throw new AccountVerificationError();
|
|
18431
|
+
};
|
|
18432
|
+
var selectWorkspace = async (session, organization, signal, identity) => {
|
|
18433
|
+
if (session.profile.organizationId === organization.id)
|
|
18434
|
+
return session.profile;
|
|
18435
|
+
if (session.profile.organizationId !== null)
|
|
18436
|
+
throw new ProfileOrganizationFixedError(session.profile, organization);
|
|
18437
|
+
const stored = await session.store.read();
|
|
18438
|
+
if (stored === void 0)
|
|
18439
|
+
throw new InteractiveSessionRequiredError();
|
|
18440
|
+
const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
|
|
18441
|
+
assertVerifiedLogin(await identity(refreshed.accessToken), session.profile.userId, organization.id);
|
|
18442
|
+
const bound = {
|
|
18443
|
+
...session.profile,
|
|
18444
|
+
organizationId: organization.id,
|
|
18445
|
+
organizationName: organization.name
|
|
18446
|
+
};
|
|
18447
|
+
await session.profiles.save(bound, { ...refreshed, workosOrganizationId: organization.workos_id, organizationId: organization.id }, session.profile);
|
|
18448
|
+
return bound;
|
|
18449
|
+
};
|
|
18450
|
+
var workspaceOf = (credential) => ({
|
|
18451
|
+
...credential.workosOrganizationId === void 0 ? {} : { workosOrganizationId: credential.workosOrganizationId },
|
|
18452
|
+
...credential.organizationId === void 0 ? {} : { organizationId: credential.organizationId }
|
|
18453
|
+
});
|
|
18454
|
+
var publicWorkspace = (organization) => ({
|
|
18455
|
+
id: organization.id,
|
|
18456
|
+
name: organization.name
|
|
18457
|
+
});
|
|
18458
|
+
|
|
17939
18459
|
// apps/product-cli/dist/cli.js
|
|
17940
|
-
import { open as
|
|
18460
|
+
import { open as open3 } from "node:fs/promises";
|
|
17941
18461
|
|
|
17942
18462
|
// packages/contracts/dist/database-access.js
|
|
17943
18463
|
function parseProjectDatabaseWrite(value) {
|
|
@@ -18089,7 +18609,7 @@ function exact2(v, keys) {
|
|
|
18089
18609
|
// apps/product-cli/package.json
|
|
18090
18610
|
var package_default = {
|
|
18091
18611
|
name: "@ohmyhost/product-cli",
|
|
18092
|
-
version: "0.1.
|
|
18612
|
+
version: "0.1.21",
|
|
18093
18613
|
private: true,
|
|
18094
18614
|
ohmyhost: {
|
|
18095
18615
|
deployment: "production",
|
|
@@ -18253,52 +18773,11 @@ function assertRecharge(value) {
|
|
|
18253
18773
|
"payment_required",
|
|
18254
18774
|
"monthly_limit",
|
|
18255
18775
|
"needs_reconciliation",
|
|
18256
|
-
"tax_required"
|
|
18257
|
-
].includes(v["status"]) || v["setup_url"] !== null && !stripeUrl(v["setup_url"], "checkout.stripe.com") || v["invoice_url"] !== null && !stripeUrl(v["invoice_url"], "invoice.stripe.com"))
|
|
18258
|
-
throw new Error("billing_response_invalid");
|
|
18259
|
-
assertBillingTaxIssue(v["billing_issue"]);
|
|
18260
|
-
}
|
|
18261
|
-
|
|
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
|
-
};
|
|
18776
|
+
"tax_required"
|
|
18777
|
+
].includes(v["status"]) || v["setup_url"] !== null && !stripeUrl(v["setup_url"], "checkout.stripe.com") || v["invoice_url"] !== null && !stripeUrl(v["invoice_url"], "invoice.stripe.com"))
|
|
18778
|
+
throw new Error("billing_response_invalid");
|
|
18779
|
+
assertBillingTaxIssue(v["billing_issue"]);
|
|
18780
|
+
}
|
|
18302
18781
|
|
|
18303
18782
|
// apps/product-cli/dist/device-token-expiry.js
|
|
18304
18783
|
var DeviceTokenExpiryError = class extends Error {
|
|
@@ -18420,14 +18899,16 @@ var PsqlUnavailableError = class extends Error {
|
|
|
18420
18899
|
var CLI_VERSION = package_default.version;
|
|
18421
18900
|
var HELP_USAGE = Object.freeze({
|
|
18422
18901
|
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",
|
|
18902
|
+
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)",
|
|
18903
|
+
logout: "ohmyhost logout [--profile-name NAME] [--revoke] --json (removes only the selected saved login)",
|
|
18904
|
+
whoami: "ohmyhost whoami [--profile-name NAME] --json (the effective user, organization and saved login)",
|
|
18905
|
+
"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
18906
|
"github connect": "ohmyhost github connect --organization ULID --idempotency-key KEY --json (connect once, then link covered repositories without another browser consent)",
|
|
18427
18907
|
"github status": "ohmyhost github status --organization ULID --json",
|
|
18428
18908
|
"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)",
|
|
18429
18909
|
"export get": "ohmyhost export get EXPORT_ULID --project ULID --json (poll the original job; signed ZIP download lasts 24 hours)",
|
|
18430
18910
|
"credits account": "ohmyhost credits account --organization ULID --json",
|
|
18911
|
+
"referral link": "ohmyhost referral link --organization ULID --json (the workspace's link to share; a new user who signs up through it starts with a free Paid month and 1,000 credits, and their first payment gives this workspace the same)",
|
|
18431
18912
|
"credits balance": "ohmyhost credits balance --organization ULID --json",
|
|
18432
18913
|
"billing recharge get": "ohmyhost billing recharge get --organization ULID --json",
|
|
18433
18914
|
"billing recharge set": "ohmyhost billing recharge set --organization ULID --enabled true|false --monthly-limit-minor CENTS --revision N --idempotency-key KEY [--consent off_session_v1] --json (explicit Owner consent required before enabling)",
|
|
@@ -18437,9 +18918,9 @@ var HELP_USAGE = Object.freeze({
|
|
|
18437
18918
|
"credits usage": "ohmyhost credits usage --organization ULID --month YYYY-MM [--cursor ULID] --json",
|
|
18438
18919
|
"budget get": "ohmyhost budget get --project ULID --json",
|
|
18439
18920
|
"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",
|
|
18921
|
+
"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)",
|
|
18922
|
+
"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)",
|
|
18923
|
+
"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
18924
|
"operation get": "ohmyhost operation get OPERATION_ULID --json",
|
|
18444
18925
|
"operation reconcile": "ohmyhost operation reconcile OPERATION_ULID --idempotency-key KEY --yes --json",
|
|
18445
18926
|
"token create": "ohmyhost token create --organization ULID --name NAME --idempotency-key KEY --out .env.local --json",
|
|
@@ -18457,6 +18938,8 @@ var HELP_USAGE = Object.freeze({
|
|
|
18457
18938
|
"project dev-share rotate": "ohmyhost project dev-share rotate --project ULID --idempotency-key KEY --yes --json",
|
|
18458
18939
|
"project dev-share revoke": "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
|
|
18459
18940
|
"project dev-access mode": "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
|
|
18941
|
+
"project flag status": "ohmyhost project flag status --project ULID --json",
|
|
18942
|
+
"project flag set": "ohmyhost project flag set --project ULID --enabled true|false --idempotency-key KEY --json (shows the small Powered by ohmyho.st flag on the production site; while it shows, a Free workspace may connect its own domain without the domain fee and each Paid period adds 250 credits)",
|
|
18460
18943
|
"project handle check": "ohmyhost project handle check --handle HANDLE --json (is this address free? answers with a reason and free alternatives; the address becomes HANDLE.check.omh.st)",
|
|
18461
18944
|
"project handle set": "ohmyhost project handle set --project ULID --handle HANDLE --if-match ETAG --idempotency-key KEY --json (moves the project to a free address; the old one stops working and anyone may claim it)",
|
|
18462
18945
|
"database compute set": "ohmyhost database compute set --project ULID --environment dev|prod --profile standard|performance --idempotency-key KEY --yes [--wait] --json\n standard: Free 0.25 CU/1 GB/60s idle, Paid 0.5 CU/2 GB/60s idle. performance: Paid 1 CU/4 GB/300s idle, 2.5x database compute credits per equal active minute; longer idle time also consumes credits.",
|
|
@@ -18489,7 +18972,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
18489
18972
|
delete: "ohmyhost delete --project ULID --if-match ETAG --confirmation-token TOKEN --idempotency-key KEY --yes --json",
|
|
18490
18973
|
"secret list": "ohmyhost secret list --project ULID --environment ENVIRONMENT_ULID --json",
|
|
18491
18974
|
"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`,
|
|
18975
|
+
"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
18976
|
"secret delete": "ohmyhost secret delete NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--wait] --json",
|
|
18494
18977
|
"mail setup": "ohmyhost mail setup --project ULID --environment ULID --domain DOMAIN --sending true --receiving false --idempotency-key KEY --json",
|
|
18495
18978
|
"mail status": "ohmyhost mail status --project ULID --environment ULID --json",
|
|
@@ -18507,8 +18990,8 @@ var HELP_COMMANDS = Object.freeze(Object.entries(HELP_USAGE).map(([command, usag
|
|
|
18507
18990
|
var cliMetadataResult = (kind, topic) => kind === "help" ? success("help", {
|
|
18508
18991
|
status: "succeeded",
|
|
18509
18992
|
cli_version: CLI_VERSION,
|
|
18510
|
-
usage: "ohmyhost <command> [options] --json",
|
|
18511
|
-
environment: "OHMYHOST_ENVIRONMENT=development|production (default: production)",
|
|
18993
|
+
usage: "ohmyhost <command> [options] [--profile-name NAME] --json",
|
|
18994
|
+
environment: "OHMYHOST_ENVIRONMENT=development|production (default: production); OHMYHOST_PROFILE=NAME binds this process to one saved login",
|
|
18512
18995
|
commands: HELP_COMMANDS.filter((entry) => !topic || entry.command === topic)
|
|
18513
18996
|
}) : success("version", { status: "succeeded", cli_version: CLI_VERSION });
|
|
18514
18997
|
var ProductCliFailure = class extends Error {
|
|
@@ -18529,8 +19012,11 @@ var ProductCliFailure = class extends Error {
|
|
|
18529
19012
|
};
|
|
18530
19013
|
var ProductCli = class {
|
|
18531
19014
|
dependencies;
|
|
19015
|
+
/** The saved login of this run; binding a login without organization replaces it. */
|
|
19016
|
+
#profile;
|
|
18532
19017
|
constructor(dependencies) {
|
|
18533
19018
|
this.dependencies = dependencies;
|
|
19019
|
+
this.#profile = dependencies.profile;
|
|
18534
19020
|
}
|
|
18535
19021
|
async run(argv, signal = new AbortController().signal) {
|
|
18536
19022
|
let command;
|
|
@@ -18560,8 +19046,10 @@ var ProductCli = class {
|
|
|
18560
19046
|
if (this.dependencies.apiToken !== void 0 && (command.kind === "organization-create" || command.kind === "organization-list" || command.kind === "organization-use")) {
|
|
18561
19047
|
throw new InteractiveSessionRequiredError();
|
|
18562
19048
|
}
|
|
19049
|
+
if (command.kind === "profile-list")
|
|
19050
|
+
return await this.listProfiles();
|
|
18563
19051
|
if (command.kind === "login")
|
|
18564
|
-
return await this.login(command.
|
|
19052
|
+
return await this.login(command.organizationId, command.userId, signal);
|
|
18565
19053
|
if (command.kind === "logout") {
|
|
18566
19054
|
return await this.logout(command.credentialStore, command.revoke, signal);
|
|
18567
19055
|
}
|
|
@@ -18634,6 +19122,11 @@ var ProductCli = class {
|
|
|
18634
19122
|
}
|
|
18635
19123
|
return success(name, { status: "succeeded", billing: result });
|
|
18636
19124
|
}
|
|
19125
|
+
if (command.kind === "referral-link") {
|
|
19126
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19127
|
+
const referral = parseOrganizationReferral(await api.getOrganizationReferral(command.organizationId));
|
|
19128
|
+
return success("referral link", { status: "succeeded", referral });
|
|
19129
|
+
}
|
|
18637
19130
|
if (command.kind === "credits-account") {
|
|
18638
19131
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18639
19132
|
const account = await api.getOrganizationAccount(command.organizationId);
|
|
@@ -18672,35 +19165,48 @@ var ProductCli = class {
|
|
|
18672
19165
|
...command.signupSource === void 0 ? {} : { signupSource: command.signupSource },
|
|
18673
19166
|
idempotencyKey: command.idempotencyKey
|
|
18674
19167
|
}));
|
|
18675
|
-
const selection =
|
|
19168
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, organization.id);
|
|
18676
19169
|
if (selection.outcome !== "selected")
|
|
18677
19170
|
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.`);
|
|
19171
|
+
const current = this.#profile;
|
|
19172
|
+
if (current !== void 0 && current.organizationId !== null && current.organizationId !== organization.id)
|
|
19173
|
+
return success("organization create", {
|
|
19174
|
+
status: "succeeded",
|
|
19175
|
+
organization,
|
|
19176
|
+
selected: current.organizationId,
|
|
19177
|
+
profile: publicProfile(current),
|
|
19178
|
+
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}.`
|
|
19179
|
+
});
|
|
19180
|
+
const profile = await this.bindOrganization(selection.organization, signal);
|
|
18678
19181
|
return success("organization create", {
|
|
18679
19182
|
status: "succeeded",
|
|
18680
19183
|
organization,
|
|
18681
19184
|
selected: organization.id,
|
|
19185
|
+
profile: publicProfile(profile),
|
|
18682
19186
|
next_action: `Run '${this.dependencies.commandPrefix} whoami --json' to confirm the selected workspace, then create a project.`,
|
|
18683
19187
|
...await this.githubNextAction(await this.authenticatedApi(command.credentialStore, signal), organization.id)
|
|
18684
19188
|
});
|
|
18685
19189
|
}
|
|
18686
19190
|
if (command.kind === "organization-list") {
|
|
18687
19191
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18688
|
-
const
|
|
19192
|
+
const account = await api.getAccountProfile();
|
|
18689
19193
|
return success("organization list", {
|
|
18690
19194
|
status: "listed",
|
|
18691
|
-
organizations: organizations.map(publicWorkspace),
|
|
18692
|
-
selected:
|
|
19195
|
+
organizations: account.organizations.map(publicWorkspace),
|
|
19196
|
+
selected: this.#profile?.organizationId ?? null
|
|
18693
19197
|
});
|
|
18694
19198
|
}
|
|
18695
19199
|
if (command.kind === "organization-use") {
|
|
18696
19200
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18697
|
-
const selection =
|
|
19201
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, command.organizationId);
|
|
18698
19202
|
if (selection.outcome !== "selected")
|
|
18699
19203
|
throw this.unknownWorkspace(selection);
|
|
19204
|
+
const profile = await this.bindOrganization(selection.organization, signal);
|
|
18700
19205
|
return success("organization use", {
|
|
18701
19206
|
status: "selected",
|
|
18702
19207
|
organization: publicWorkspace(selection.organization),
|
|
18703
19208
|
selected: selection.organization.id,
|
|
19209
|
+
profile: publicProfile(profile),
|
|
18704
19210
|
...await this.githubNextAction(await this.authenticatedApi(command.credentialStore, signal), selection.organization.id)
|
|
18705
19211
|
});
|
|
18706
19212
|
}
|
|
@@ -18782,13 +19288,18 @@ var ProductCli = class {
|
|
|
18782
19288
|
return await this.getProjectStatus(command, signal);
|
|
18783
19289
|
if (command.kind === "project-dev-access-create")
|
|
18784
19290
|
return await this.createDevAccessTicket(command, signal);
|
|
19291
|
+
if (command.kind === "project-flag-status" || command.kind === "project-flag-set") {
|
|
19292
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19293
|
+
const flag = parsePoweredByFlag(command.kind === "project-flag-status" ? await api.getPoweredByFlag(command.projectId) : await api.setPoweredByFlag(command.projectId, command.enabled, command.idempotencyKey));
|
|
19294
|
+
return success(command.kind === "project-flag-status" ? "project flag status" : "project flag set", { status: "succeeded", flag });
|
|
19295
|
+
}
|
|
18785
19296
|
if (command.kind === "project-dev-share-link" || command.kind === "project-dev-share-rotate" || command.kind === "project-dev-share-revoke" || command.kind === "project-dev-access-mode-set") {
|
|
18786
19297
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18787
19298
|
const state = parseDevAccessState(command.kind === "project-dev-share-link" ? await api.ensureDevShareLink(command.projectId) : command.kind === "project-dev-share-rotate" ? await api.rotateDevShareLink(command.projectId, command.idempotencyKey) : command.kind === "project-dev-share-revoke" ? await api.revokeDevShareLink(command.projectId, command.idempotencyKey) : await api.setDevAccessMode(command.projectId, command.mode, command.idempotencyKey));
|
|
18788
19299
|
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
19300
|
}
|
|
18790
19301
|
if (command.kind === "database-write") {
|
|
18791
|
-
const file = await
|
|
19302
|
+
const file = await open3(command.statementFile, "r"), buffer = Buffer.alloc(65537);
|
|
18792
19303
|
let statement;
|
|
18793
19304
|
try {
|
|
18794
19305
|
const stat3 = await file.stat();
|
|
@@ -18901,8 +19412,10 @@ var ProductCli = class {
|
|
|
18901
19412
|
return failure(name, 3, error.code, false, error.message);
|
|
18902
19413
|
if (error instanceof InteractiveSessionRequiredError)
|
|
18903
19414
|
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.`);
|
|
19415
|
+
if (error instanceof ProfileLimitError)
|
|
19416
|
+
return failure(name, 2, "profile_limit_reached", false, profileLimitAction(this.dependencies.commandPrefix));
|
|
18904
19417
|
this.recordDiagnostic(diagnosticDescriptor(error, signal));
|
|
18905
|
-
return classifyFailure(name, error, signal);
|
|
19418
|
+
return classifyFailure(name, error, signal, this.context());
|
|
18906
19419
|
}
|
|
18907
19420
|
}
|
|
18908
19421
|
githubConnectAction(organizationId) {
|
|
@@ -18925,22 +19438,53 @@ var ProductCli = class {
|
|
|
18925
19438
|
return success("whoami", {
|
|
18926
19439
|
status: "authenticated",
|
|
18927
19440
|
identity,
|
|
19441
|
+
context: this.context(),
|
|
18928
19442
|
...identity.organization_ids.length === 1 && identity.organization_ids[0] !== void 0 ? await this.githubNextAction(api, identity.organization_ids[0]) : {}
|
|
18929
19443
|
});
|
|
18930
|
-
const selection =
|
|
18931
|
-
if (selection.outcome === "selected")
|
|
19444
|
+
const selection = chooseWorkspace((await api.getAccountProfile()).organizations, void 0);
|
|
19445
|
+
if (selection.outcome === "selected") {
|
|
19446
|
+
await this.bindOrganization(selection.organization, signal);
|
|
18932
19447
|
return success("whoami", {
|
|
18933
19448
|
status: "authenticated",
|
|
18934
19449
|
identity: parseCurrentIdentity(await api.getCurrentIdentity()),
|
|
18935
19450
|
organization: publicWorkspace(selection.organization),
|
|
18936
19451
|
selected: selection.organization.id,
|
|
19452
|
+
context: this.context(),
|
|
18937
19453
|
...await this.githubNextAction(api, selection.organization.id)
|
|
18938
19454
|
});
|
|
19455
|
+
}
|
|
18939
19456
|
return success("whoami", {
|
|
18940
19457
|
status: "authenticated",
|
|
18941
19458
|
identity,
|
|
19459
|
+
context: this.context(),
|
|
18942
19460
|
organizations: selection.outcome === "choice_required" ? selection.organizations.map(publicWorkspace) : [],
|
|
18943
|
-
next_action:
|
|
19461
|
+
next_action: this.workspaceAction(selection)
|
|
19462
|
+
});
|
|
19463
|
+
}
|
|
19464
|
+
/** Which account this run acts as, without any credential. */
|
|
19465
|
+
context() {
|
|
19466
|
+
if (this.dependencies.apiToken !== void 0)
|
|
19467
|
+
return { credential: "environment_token" };
|
|
19468
|
+
return {
|
|
19469
|
+
credential: "profile",
|
|
19470
|
+
profile: this.#profile === void 0 ? null : publicProfile(this.#profile)
|
|
19471
|
+
};
|
|
19472
|
+
}
|
|
19473
|
+
/** The next step for a login that has no organization yet. */
|
|
19474
|
+
workspaceAction(selection) {
|
|
19475
|
+
const prefix = this.dependencies.commandPrefix;
|
|
19476
|
+
const named = this.#profile === void 0 ? "" : ` --profile-name ${this.#profile.name}`;
|
|
19477
|
+
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.`;
|
|
19478
|
+
}
|
|
19479
|
+
async listProfiles() {
|
|
19480
|
+
const profiles = this.dependencies.profiles;
|
|
19481
|
+
if (profiles === void 0)
|
|
19482
|
+
throw new ProviderCapabilityBlockedError();
|
|
19483
|
+
return success("profile list", {
|
|
19484
|
+
status: "listed",
|
|
19485
|
+
profiles: (await profiles.list()).map(publicProfile),
|
|
19486
|
+
bound: this.dependencies.profileBinding ?? null,
|
|
19487
|
+
environment_token: this.dependencies.apiToken !== void 0
|
|
18944
19488
|
});
|
|
18945
19489
|
}
|
|
18946
19490
|
recordDiagnostic(event) {
|
|
@@ -18953,7 +19497,11 @@ var ProductCli = class {
|
|
|
18953
19497
|
}
|
|
18954
19498
|
}
|
|
18955
19499
|
// The reviewed boundary proof parses this signature, so keep its parameters brace-free.
|
|
18956
|
-
async login(
|
|
19500
|
+
async login(organizationId, expectedUserId, signal) {
|
|
19501
|
+
const profiles = this.dependencies.profiles;
|
|
19502
|
+
const readIdentity = this.dependencies.currentIdentity;
|
|
19503
|
+
if (profiles === void 0 || readIdentity === void 0)
|
|
19504
|
+
throw new ProviderCapabilityBlockedError();
|
|
18957
19505
|
const signInDeadline = this.dependencies.now().getTime() + SIGN_IN_WINDOW_MS;
|
|
18958
19506
|
let session = await this.dependencies.deviceFlow.authorize();
|
|
18959
19507
|
await presentAuthorization(this.dependencies.deviceAuthorizationPresenter, session.authorization);
|
|
@@ -18990,53 +19538,112 @@ var ProductCli = class {
|
|
|
18990
19538
|
throw error;
|
|
18991
19539
|
throw new ProductCliFailure(4, "device_token_expiry_missing", false, "Run login again; the authorization response was incomplete.");
|
|
18992
19540
|
}
|
|
18993
|
-
|
|
19541
|
+
const signedIn = {
|
|
18994
19542
|
accessToken: result.tokens.accessToken,
|
|
18995
19543
|
refreshToken: result.tokens.refreshToken,
|
|
18996
19544
|
expiresAt
|
|
18997
|
-
}
|
|
18998
|
-
const
|
|
18999
|
-
|
|
19545
|
+
};
|
|
19546
|
+
const account = await this.dependencies.accountProfile(async () => signedIn.accessToken);
|
|
19547
|
+
if (!isProfileUserId(account.userId))
|
|
19548
|
+
throw this.unverifiedLogin();
|
|
19549
|
+
if (expectedUserId !== void 0 && account.userId !== expectedUserId)
|
|
19550
|
+
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.`);
|
|
19551
|
+
const selection = chooseWorkspace(account.organizations, organizationId);
|
|
19552
|
+
if (selection.outcome === "not_a_member")
|
|
19553
|
+
throw this.unknownWorkspace(selection);
|
|
19554
|
+
const credential = selection.outcome === "selected" ? await runBounded((bounded2) => this.dependencies.tokenRefresher.refresh(signedIn.refreshToken, bounded2, selection.organization.workos_id), signal, this.dependencies.lifecycleTimeoutMs) : signedIn;
|
|
19555
|
+
const identity = await readIdentity(async () => credential.accessToken);
|
|
19556
|
+
const scopedId = selection.outcome === "selected" ? selection.organization.id : parseCurrentIdentity(identity).organization_ids[0] ?? null;
|
|
19557
|
+
const organization = scopedId === null ? null : account.organizations.find((candidate) => candidate.id === scopedId);
|
|
19558
|
+
if (organization === void 0)
|
|
19559
|
+
throw this.unverifiedLogin();
|
|
19560
|
+
try {
|
|
19561
|
+
assertVerifiedLogin(identity, account.userId, scopedId);
|
|
19562
|
+
} catch (error) {
|
|
19563
|
+
if (error instanceof AccountVerificationError)
|
|
19564
|
+
throw this.unverifiedLogin();
|
|
19565
|
+
throw error;
|
|
19566
|
+
}
|
|
19567
|
+
const login = {
|
|
19568
|
+
userId: account.userId,
|
|
19569
|
+
organizationId: scopedId,
|
|
19570
|
+
organizationName: organization?.name ?? null
|
|
19571
|
+
};
|
|
19572
|
+
const profile = {
|
|
19573
|
+
name: this.dependencies.profileName ?? defaultProfileName(await profiles.list(), login),
|
|
19574
|
+
...login
|
|
19575
|
+
};
|
|
19576
|
+
try {
|
|
19577
|
+
await profiles.save(profile, organization === null ? credential : {
|
|
19578
|
+
...credential,
|
|
19579
|
+
workosOrganizationId: organization.workos_id,
|
|
19580
|
+
organizationId: organization.id
|
|
19581
|
+
});
|
|
19582
|
+
} catch (error) {
|
|
19583
|
+
if (!(error instanceof ProfileNameConflictError))
|
|
19584
|
+
throw error;
|
|
19585
|
+
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.`);
|
|
19586
|
+
}
|
|
19587
|
+
this.#profile = profile;
|
|
19000
19588
|
const readGithubConnection = this.dependencies.githubConnectionStatus;
|
|
19001
|
-
if (
|
|
19589
|
+
if (organization !== null)
|
|
19002
19590
|
return success("login", {
|
|
19003
19591
|
status: "authenticated",
|
|
19004
|
-
organization: publicWorkspace(
|
|
19592
|
+
organization: publicWorkspace(organization),
|
|
19593
|
+
profile: publicProfile(profile),
|
|
19005
19594
|
...readGithubConnection === void 0 ? {} : await this.githubNextAction({
|
|
19006
|
-
getGithubConnection: (selectedOrganizationId) => readGithubConnection(() =>
|
|
19007
|
-
},
|
|
19595
|
+
getGithubConnection: (selectedOrganizationId) => readGithubConnection(async () => credential.accessToken, selectedOrganizationId)
|
|
19596
|
+
}, organization.id)
|
|
19008
19597
|
});
|
|
19009
|
-
if (selection.outcome === "not_a_member")
|
|
19010
|
-
throw this.unknownWorkspace(selection);
|
|
19011
19598
|
return success("login", {
|
|
19012
19599
|
status: "authenticated",
|
|
19013
19600
|
organization: null,
|
|
19601
|
+
profile: publicProfile(profile),
|
|
19014
19602
|
organizations: selection.outcome === "choice_required" ? selection.organizations.map(publicWorkspace) : [],
|
|
19015
|
-
next_action:
|
|
19603
|
+
next_action: this.workspaceAction(selection)
|
|
19016
19604
|
});
|
|
19017
19605
|
}
|
|
19018
19606
|
}
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19607
|
+
/** The provider answered with another account than the one this login requested. */
|
|
19608
|
+
unverifiedLogin() {
|
|
19609
|
+
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.`);
|
|
19610
|
+
}
|
|
19611
|
+
/**
|
|
19612
|
+
* Bind the login of this run, which has no organization yet, to one of its user's organizations.
|
|
19613
|
+
* A login that already has one keeps it; another organization is its own login.
|
|
19614
|
+
*/
|
|
19615
|
+
async bindOrganization(organization, signal) {
|
|
19616
|
+
const profiles = this.dependencies.profiles;
|
|
19617
|
+
const profile = this.#profile;
|
|
19618
|
+
if (profiles === void 0 || profile === void 0)
|
|
19619
|
+
throw new InteractiveSessionRequiredError();
|
|
19620
|
+
let bound;
|
|
19621
|
+
try {
|
|
19622
|
+
bound = await runBounded((bounded2) => selectWorkspace({
|
|
19623
|
+
store: profiles.credential(profile),
|
|
19624
|
+
refresher: this.dependencies.tokenRefresher,
|
|
19625
|
+
profiles,
|
|
19626
|
+
profile
|
|
19627
|
+
}, organization, bounded2, (accessToken) => this.dependencies.productApi(async () => accessToken).getCurrentIdentity()), signal, this.dependencies.lifecycleTimeoutMs);
|
|
19628
|
+
} catch (error) {
|
|
19629
|
+
if (error instanceof AccountVerificationError)
|
|
19630
|
+
throw this.unverifiedLogin();
|
|
19631
|
+
if (!(error instanceof ProfileOrganizationFixedError))
|
|
19632
|
+
throw error;
|
|
19633
|
+
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.`);
|
|
19634
|
+
}
|
|
19635
|
+
this.#profile = bound;
|
|
19636
|
+
return bound;
|
|
19031
19637
|
}
|
|
19032
19638
|
unknownWorkspace(selection) {
|
|
19033
19639
|
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
19640
|
}
|
|
19035
19641
|
async logout(credentialStore, revoke, signal) {
|
|
19036
|
-
const store = this.
|
|
19642
|
+
const store = this.store(credentialStore);
|
|
19643
|
+
const removed = this.#profile === void 0 ? {} : { profile: publicProfile(this.#profile) };
|
|
19037
19644
|
if (!revoke) {
|
|
19038
|
-
await
|
|
19039
|
-
return success("logout", { status: "logged_out" });
|
|
19645
|
+
await this.forgetLogin(store);
|
|
19646
|
+
return success("logout", { status: "logged_out", ...removed });
|
|
19040
19647
|
}
|
|
19041
19648
|
const credential = await store.read();
|
|
19042
19649
|
let revocationError;
|
|
@@ -19047,7 +19654,7 @@ var ProductCli = class {
|
|
|
19047
19654
|
} catch (error) {
|
|
19048
19655
|
revocationError = error;
|
|
19049
19656
|
} finally {
|
|
19050
|
-
await
|
|
19657
|
+
await this.forgetLogin(store);
|
|
19051
19658
|
}
|
|
19052
19659
|
if (revocationError !== void 0) {
|
|
19053
19660
|
if (isCancellation(revocationError) && signal.aborted)
|
|
@@ -19063,7 +19670,29 @@ var ProductCli = class {
|
|
|
19063
19670
|
}
|
|
19064
19671
|
throw revocationError;
|
|
19065
19672
|
}
|
|
19066
|
-
return success("logout", { status: "logged_out" });
|
|
19673
|
+
return success("logout", { status: "logged_out", ...removed });
|
|
19674
|
+
}
|
|
19675
|
+
/** The credential store of this run's saved login; it follows a binding made during the run. */
|
|
19676
|
+
store(kind) {
|
|
19677
|
+
const profiles = this.dependencies.profiles;
|
|
19678
|
+
if (profiles !== void 0 && this.#profile !== void 0)
|
|
19679
|
+
return profiles.credential(this.#profile);
|
|
19680
|
+
if (this.dependencies.credentialStore === void 0)
|
|
19681
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19682
|
+
return this.dependencies.credentialStore(kind);
|
|
19683
|
+
}
|
|
19684
|
+
/** Removes only this run's saved login; every other saved login stays untouched. */
|
|
19685
|
+
async forgetLogin(store) {
|
|
19686
|
+
const profiles = this.dependencies.profiles;
|
|
19687
|
+
if (profiles !== void 0 && this.#profile !== void 0)
|
|
19688
|
+
await profiles.remove(this.#profile);
|
|
19689
|
+
else
|
|
19690
|
+
await store.clear();
|
|
19691
|
+
}
|
|
19692
|
+
/** How to sign this run's login in again, naming its organization and alias when known. */
|
|
19693
|
+
loginAction() {
|
|
19694
|
+
const profile = this.#profile;
|
|
19695
|
+
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
19696
|
}
|
|
19068
19697
|
async getOperation(command, signal) {
|
|
19069
19698
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19116,7 +19745,7 @@ var ProductCli = class {
|
|
|
19116
19745
|
async getProjectStatus(command, signal) {
|
|
19117
19746
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19118
19747
|
const project = parseProjectStatus(await api.getProjectStatus(command.projectId));
|
|
19119
|
-
return success("project status", { status: "succeeded", project });
|
|
19748
|
+
return success("project status", { status: "succeeded", project, context: this.context() });
|
|
19120
19749
|
}
|
|
19121
19750
|
async createDevAccessTicket(command, signal) {
|
|
19122
19751
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19259,14 +19888,18 @@ var ProductCli = class {
|
|
|
19259
19888
|
const source = project.source;
|
|
19260
19889
|
if (project.project_id !== command.projectId || source === null || source.status !== "ready" || source.repository_full_name.toLowerCase() !== `${command.repositoryOwner}/${command.repositoryName}`.toLowerCase())
|
|
19261
19890
|
throw new ResponseContractError();
|
|
19891
|
+
const [organizationId, ...others] = parseCurrentIdentity(await api.getCurrentIdentity()).organization_ids;
|
|
19892
|
+
if (organizationId === void 0 || others.length > 0)
|
|
19893
|
+
throw new ResponseContractError();
|
|
19262
19894
|
await this.dependencies.projectLinkStore.save({
|
|
19263
|
-
version:
|
|
19895
|
+
version: 2,
|
|
19264
19896
|
api_origin: this.dependencies.productApiOrigin,
|
|
19897
|
+
organization_id: organizationId,
|
|
19265
19898
|
project_id: command.projectId,
|
|
19266
19899
|
installation_id: source.installation_id,
|
|
19267
19900
|
repository_full_name: source.repository_full_name
|
|
19268
19901
|
});
|
|
19269
|
-
return success("link", { status: "completed", operation });
|
|
19902
|
+
return success("link", { status: "completed", operation, organization_id: organizationId });
|
|
19270
19903
|
}
|
|
19271
19904
|
async configureAutoDeploy(command, signal) {
|
|
19272
19905
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -19509,10 +20142,10 @@ var ProductCli = class {
|
|
|
19509
20142
|
async authenticatedToken(credentialStore, signal) {
|
|
19510
20143
|
if (this.dependencies.apiToken !== void 0)
|
|
19511
20144
|
return this.dependencies.apiToken;
|
|
19512
|
-
const store = this.
|
|
20145
|
+
const store = this.store(credentialStore);
|
|
19513
20146
|
const stored = await store.read();
|
|
19514
20147
|
if (stored === void 0) {
|
|
19515
|
-
throw new ProductCliFailure(5, "authentication_required", false,
|
|
20148
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19516
20149
|
}
|
|
19517
20150
|
let refresh;
|
|
19518
20151
|
try {
|
|
@@ -19520,8 +20153,8 @@ var ProductCli = class {
|
|
|
19520
20153
|
} catch (error) {
|
|
19521
20154
|
if (!(error instanceof TokenRefreshRejectedError))
|
|
19522
20155
|
throw error;
|
|
19523
|
-
await
|
|
19524
|
-
throw new ProductCliFailure(5, "authentication_required", false,
|
|
20156
|
+
await this.forgetLogin(store);
|
|
20157
|
+
throw new ProductCliFailure(5, "authentication_required", false, this.loginAction());
|
|
19525
20158
|
}
|
|
19526
20159
|
if (refresh.refreshed)
|
|
19527
20160
|
await store.replace(refresh.credential);
|
|
@@ -19587,7 +20220,16 @@ var redactVerificationUri = (value) => {
|
|
|
19587
20220
|
url.hash = "";
|
|
19588
20221
|
return url.toString();
|
|
19589
20222
|
};
|
|
19590
|
-
var
|
|
20223
|
+
var ACCOUNT_SENSITIVE_PROBLEMS = /* @__PURE__ */ new Set([
|
|
20224
|
+
"resource_not_found",
|
|
20225
|
+
"forbidden",
|
|
20226
|
+
"organization_required",
|
|
20227
|
+
"github_connection_required",
|
|
20228
|
+
"github_connection_revoked",
|
|
20229
|
+
"repository_not_installed"
|
|
20230
|
+
]);
|
|
20231
|
+
var classifyCliFailure = (command, error, signal) => classifyFailure(command, error, signal);
|
|
20232
|
+
var classifyFailure = (command, error, signal, actingAs) => {
|
|
19591
20233
|
if (signal.aborted || isCancellation(error)) {
|
|
19592
20234
|
return failure(command, 130, "command_cancelled", true, "Run the command again.");
|
|
19593
20235
|
}
|
|
@@ -19621,7 +20263,7 @@ var classifyFailure = (command, error, signal) => {
|
|
|
19621
20263
|
if (looksLikeProblem(error)) {
|
|
19622
20264
|
try {
|
|
19623
20265
|
const problem = parseSafeProblem(error);
|
|
19624
|
-
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl);
|
|
20266
|
+
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
20267
|
} catch {
|
|
19626
20268
|
return failure(command, 7, "response_contract_invalid", false, "Retry only after the client or server contract is repaired.");
|
|
19627
20269
|
}
|
|
@@ -19641,7 +20283,7 @@ var success = (command, body) => ({
|
|
|
19641
20283
|
`,
|
|
19642
20284
|
stderr: ""
|
|
19643
20285
|
});
|
|
19644
|
-
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl) => {
|
|
20286
|
+
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl, actingAs) => {
|
|
19645
20287
|
const document = {
|
|
19646
20288
|
version: 1,
|
|
19647
20289
|
command,
|
|
@@ -19654,7 +20296,8 @@ var failure = (command, exitCode, code, retryable, suggestedAction, operation, r
|
|
|
19654
20296
|
...retryAfterSeconds === void 0 ? {} : { retry_after_seconds: retryAfterSeconds },
|
|
19655
20297
|
...requestId === void 0 ? {} : { request_id: requestId },
|
|
19656
20298
|
...docsUrl === void 0 ? {} : { docs_url: docsUrl },
|
|
19657
|
-
...operation?.error === void 0 ? {} : { message: operation.error.message }
|
|
20299
|
+
...operation?.error === void 0 ? {} : { message: operation.error.message },
|
|
20300
|
+
...actingAs === void 0 ? {} : { acting_as: actingAs }
|
|
19658
20301
|
}
|
|
19659
20302
|
};
|
|
19660
20303
|
return { exitCode, stdout: `${JSON.stringify(document)}
|
|
@@ -19683,6 +20326,8 @@ var parsedCommandName = (command) => {
|
|
|
19683
20326
|
return "billing portal";
|
|
19684
20327
|
if (command.kind === "credits-account")
|
|
19685
20328
|
return "credits account";
|
|
20329
|
+
if (command.kind === "referral-link")
|
|
20330
|
+
return "referral link";
|
|
19686
20331
|
if (command.kind === "credits-balance")
|
|
19687
20332
|
return "credits balance";
|
|
19688
20333
|
if (command.kind === "credits-usage")
|
|
@@ -19733,6 +20378,10 @@ var parsedCommandName = (command) => {
|
|
|
19733
20378
|
return "project dev-share revoke";
|
|
19734
20379
|
if (command.kind === "project-dev-access-mode-set")
|
|
19735
20380
|
return "project dev-access mode";
|
|
20381
|
+
if (command.kind === "project-flag-status")
|
|
20382
|
+
return "project flag status";
|
|
20383
|
+
if (command.kind === "project-flag-set")
|
|
20384
|
+
return "project flag set";
|
|
19736
20385
|
if (command.kind === "project-handle-check")
|
|
19737
20386
|
return "project handle check";
|
|
19738
20387
|
if (command.kind === "project-handle-set")
|
|
@@ -19787,6 +20436,8 @@ var parsedCommandName = (command) => {
|
|
|
19787
20436
|
return "secret delete";
|
|
19788
20437
|
if (command.kind === "managed-mail")
|
|
19789
20438
|
return command.label;
|
|
20439
|
+
if (command.kind === "profile-list")
|
|
20440
|
+
return "profile list";
|
|
19790
20441
|
return command.kind;
|
|
19791
20442
|
};
|
|
19792
20443
|
var boundedSignal = (parent, timeoutMs) => AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
|
|
@@ -20773,6 +21424,23 @@ var getGithubOrganizationConnection = (parameters, options) => {
|
|
|
20773
21424
|
...params
|
|
20774
21425
|
});
|
|
20775
21426
|
};
|
|
21427
|
+
var getOrganizationReferral = (parameters, options) => {
|
|
21428
|
+
const params = buildClientParams([parameters], [
|
|
21429
|
+
{
|
|
21430
|
+
args: [
|
|
21431
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
21432
|
+
{ in: "path", key: "organization_id" }
|
|
21433
|
+
]
|
|
21434
|
+
}
|
|
21435
|
+
]);
|
|
21436
|
+
return (options?.client ?? client).get({
|
|
21437
|
+
responseStyle: "data",
|
|
21438
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
21439
|
+
url: "/v1/organizations/{organization_id}/referral",
|
|
21440
|
+
...options,
|
|
21441
|
+
...params
|
|
21442
|
+
});
|
|
21443
|
+
};
|
|
20776
21444
|
var getOrganizationAccount = (parameters, options) => {
|
|
20777
21445
|
const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "organization_id" }] }]);
|
|
20778
21446
|
return (options?.client ?? client).get({
|
|
@@ -21224,6 +21892,23 @@ var deleteProject = (parameters, options) => {
|
|
|
21224
21892
|
...params
|
|
21225
21893
|
});
|
|
21226
21894
|
};
|
|
21895
|
+
var getProject = (parameters, options) => {
|
|
21896
|
+
const params = buildClientParams([parameters], [
|
|
21897
|
+
{
|
|
21898
|
+
args: [
|
|
21899
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
21900
|
+
{ in: "path", key: "project_id" }
|
|
21901
|
+
]
|
|
21902
|
+
}
|
|
21903
|
+
]);
|
|
21904
|
+
return (options?.client ?? client).get({
|
|
21905
|
+
responseStyle: "data",
|
|
21906
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
21907
|
+
url: "/v1/projects/{project_id}",
|
|
21908
|
+
...options,
|
|
21909
|
+
...params
|
|
21910
|
+
});
|
|
21911
|
+
};
|
|
21227
21912
|
var getProjectContext = (parameters, options) => {
|
|
21228
21913
|
const params = buildClientParams([parameters], [
|
|
21229
21914
|
{
|
|
@@ -21377,6 +22062,47 @@ var setProjectDevAccessMode = (parameters, options) => {
|
|
|
21377
22062
|
}
|
|
21378
22063
|
});
|
|
21379
22064
|
};
|
|
22065
|
+
var getProjectPoweredByFlag = (parameters, options) => {
|
|
22066
|
+
const params = buildClientParams([parameters], [
|
|
22067
|
+
{
|
|
22068
|
+
args: [
|
|
22069
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
22070
|
+
{ in: "path", key: "project_id" }
|
|
22071
|
+
]
|
|
22072
|
+
}
|
|
22073
|
+
]);
|
|
22074
|
+
return (options?.client ?? client).get({
|
|
22075
|
+
responseStyle: "data",
|
|
22076
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
22077
|
+
url: "/v1/projects/{project_id}/powered-by-flag",
|
|
22078
|
+
...options,
|
|
22079
|
+
...params
|
|
22080
|
+
});
|
|
22081
|
+
};
|
|
22082
|
+
var setProjectPoweredByFlag = (parameters, options) => {
|
|
22083
|
+
const params = buildClientParams([parameters], [
|
|
22084
|
+
{
|
|
22085
|
+
args: [
|
|
22086
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
22087
|
+
{ in: "headers", key: "Idempotency-Key" },
|
|
22088
|
+
{ in: "path", key: "project_id" },
|
|
22089
|
+
{ key: "setPoweredByFlagRequest", map: "body" }
|
|
22090
|
+
]
|
|
22091
|
+
}
|
|
22092
|
+
]);
|
|
22093
|
+
return (options?.client ?? client).put({
|
|
22094
|
+
responseStyle: "data",
|
|
22095
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
22096
|
+
url: "/v1/projects/{project_id}/powered-by-flag",
|
|
22097
|
+
...options,
|
|
22098
|
+
...params,
|
|
22099
|
+
headers: {
|
|
22100
|
+
"Content-Type": "application/json",
|
|
22101
|
+
...options?.headers,
|
|
22102
|
+
...params.headers
|
|
22103
|
+
}
|
|
22104
|
+
});
|
|
22105
|
+
};
|
|
21380
22106
|
var createProjectDevAccessTicket = (parameters, options) => {
|
|
21381
22107
|
const params = buildClientParams([parameters], [
|
|
21382
22108
|
{
|
|
@@ -22568,6 +23294,9 @@ var GeneratedSdkProductApi = class {
|
|
|
22568
23294
|
async getAccountProfile() {
|
|
22569
23295
|
return parseAccountProfile(await getAccountProfile({ client: this.#client }));
|
|
22570
23296
|
}
|
|
23297
|
+
getOrganizationReferral(organizationId) {
|
|
23298
|
+
return getOrganizationReferral({ organization_id: organizationId }, { client: this.#client });
|
|
23299
|
+
}
|
|
22571
23300
|
getOrganizationAccount(organizationId) {
|
|
22572
23301
|
return getOrganizationAccount({ organization_id: organizationId }, { client: this.#client });
|
|
22573
23302
|
}
|
|
@@ -22741,6 +23470,16 @@ var GeneratedSdkProductApi = class {
|
|
|
22741
23470
|
setDevAccessModeRequest: { mode }
|
|
22742
23471
|
}, { client: this.#client });
|
|
22743
23472
|
}
|
|
23473
|
+
async getPoweredByFlag(projectId) {
|
|
23474
|
+
return getProjectPoweredByFlag({ project_id: projectId }, { client: this.#client });
|
|
23475
|
+
}
|
|
23476
|
+
async setPoweredByFlag(projectId, enabled, idempotencyKey) {
|
|
23477
|
+
return setProjectPoweredByFlag({
|
|
23478
|
+
project_id: projectId,
|
|
23479
|
+
"Idempotency-Key": idempotencyKey,
|
|
23480
|
+
setPoweredByFlagRequest: { enabled }
|
|
23481
|
+
}, { client: this.#client });
|
|
23482
|
+
}
|
|
22744
23483
|
async createDevAccessTicket(projectId) {
|
|
22745
23484
|
return createProjectDevAccessTicket({ project_id: projectId }, { client: this.#client });
|
|
22746
23485
|
}
|
|
@@ -22908,18 +23647,75 @@ var GeneratedSdkProductApi = class {
|
|
|
22908
23647
|
};
|
|
22909
23648
|
|
|
22910
23649
|
// apps/product-cli/dist/project-link-store.js
|
|
22911
|
-
import { appendFile, chmod, mkdir as
|
|
22912
|
-
import { join } from "node:path";
|
|
22913
|
-
|
|
23650
|
+
import { appendFile, chmod, mkdir as mkdir3, readFile as readFile2, readdir as readdir2, rename, stat, unlink as unlink2, writeFile } from "node:fs/promises";
|
|
23651
|
+
import { join as join2 } from "node:path";
|
|
23652
|
+
var ULID2 = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
23653
|
+
var LINKS = "links";
|
|
23654
|
+
var LEGACY_LINK = "link.json";
|
|
23655
|
+
async function readProjectLinks(directory, apiOrigin) {
|
|
23656
|
+
let names = [];
|
|
22914
23657
|
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;
|
|
23658
|
+
names = await readdir2(join2(directory, LINKS));
|
|
22920
23659
|
} catch {
|
|
22921
|
-
|
|
23660
|
+
names = [];
|
|
22922
23661
|
}
|
|
23662
|
+
const links = [];
|
|
23663
|
+
for (const name of names.sort()) {
|
|
23664
|
+
const organizationId = /^([0-7][0-9A-HJKMNP-TV-Z]{25})\.json$/u.exec(name)?.[1];
|
|
23665
|
+
if (organizationId === void 0)
|
|
23666
|
+
continue;
|
|
23667
|
+
const link2 = await readJson(join2(directory, LINKS, name));
|
|
23668
|
+
if (isRecord3(link2, [
|
|
23669
|
+
"version",
|
|
23670
|
+
"api_origin",
|
|
23671
|
+
"organization_id",
|
|
23672
|
+
"project_id",
|
|
23673
|
+
"installation_id",
|
|
23674
|
+
"repository_full_name"
|
|
23675
|
+
]) && 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")
|
|
23676
|
+
links.push(link2);
|
|
23677
|
+
}
|
|
23678
|
+
const legacy = await readJson(join2(directory, LEGACY_LINK));
|
|
23679
|
+
return {
|
|
23680
|
+
links,
|
|
23681
|
+
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" ? {
|
|
23682
|
+
project_id: legacy["project_id"],
|
|
23683
|
+
installation_id: legacy["installation_id"],
|
|
23684
|
+
repository_full_name: legacy["repository_full_name"]
|
|
23685
|
+
} : null
|
|
23686
|
+
};
|
|
23687
|
+
}
|
|
23688
|
+
function chooseLinkedProject(directory, organizationId) {
|
|
23689
|
+
if (organizationId !== void 0) {
|
|
23690
|
+
const link2 = directory.links.find((candidate) => candidate.organization_id === organizationId);
|
|
23691
|
+
if (link2 !== void 0)
|
|
23692
|
+
return { outcome: "linked", projectId: link2.project_id, organizationId };
|
|
23693
|
+
return directory.legacy === null ? { outcome: "none" } : { outcome: "linked", projectId: directory.legacy.project_id, organizationId: null };
|
|
23694
|
+
}
|
|
23695
|
+
const candidates = [
|
|
23696
|
+
...directory.links.map((link2) => ({
|
|
23697
|
+
projectId: link2.project_id,
|
|
23698
|
+
organizationId: link2.organization_id
|
|
23699
|
+
})),
|
|
23700
|
+
...directory.legacy === null ? [] : [{ projectId: directory.legacy.project_id, organizationId: null }]
|
|
23701
|
+
];
|
|
23702
|
+
const [only] = candidates;
|
|
23703
|
+
if (candidates.length === 1 && only !== void 0)
|
|
23704
|
+
return { outcome: "linked", ...only };
|
|
23705
|
+
if (candidates.length === 0)
|
|
23706
|
+
return { outcome: "none" };
|
|
23707
|
+
return {
|
|
23708
|
+
outcome: "ambiguous",
|
|
23709
|
+
organizationIds: directory.links.map((link2) => link2.organization_id)
|
|
23710
|
+
};
|
|
23711
|
+
}
|
|
23712
|
+
function linkedOrganization(directory, projectId) {
|
|
23713
|
+
const organizations = new Set(directory.links.filter((link2) => link2.project_id === projectId).map((link2) => link2.organization_id));
|
|
23714
|
+
const [organizationId] = organizations;
|
|
23715
|
+
return organizations.size === 1 ? organizationId : void 0;
|
|
23716
|
+
}
|
|
23717
|
+
async function removeLegacyProjectLink(directory) {
|
|
23718
|
+
await unlink2(join2(directory, LEGACY_LINK));
|
|
22923
23719
|
}
|
|
22924
23720
|
var OwnerOnlyProjectLinkStore = class {
|
|
22925
23721
|
directory;
|
|
@@ -22930,10 +23726,15 @@ var OwnerOnlyProjectLinkStore = class {
|
|
|
22930
23726
|
this.repositoryRoot = repositoryRoot;
|
|
22931
23727
|
}
|
|
22932
23728
|
async save(metadata) {
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
const
|
|
22936
|
-
const
|
|
23729
|
+
if (!ULID2.test(metadata.organization_id))
|
|
23730
|
+
throw new TypeError("Invalid linked organization");
|
|
23731
|
+
const links = join2(this.directory, LINKS);
|
|
23732
|
+
for (const directory of [this.directory, links]) {
|
|
23733
|
+
await mkdir3(directory, { mode: 448, recursive: true });
|
|
23734
|
+
await chmod(directory, 448);
|
|
23735
|
+
}
|
|
23736
|
+
const target = join2(links, `${metadata.organization_id}.json`);
|
|
23737
|
+
const temporary = join2(links, `.link-${process.pid}.tmp`);
|
|
22937
23738
|
await writeFile(temporary, `${JSON.stringify(metadata)}
|
|
22938
23739
|
`, { encoding: "utf8", mode: 384 });
|
|
22939
23740
|
await chmod(temporary, 384);
|
|
@@ -22943,16 +23744,25 @@ var OwnerOnlyProjectLinkStore = class {
|
|
|
22943
23744
|
await ignoreLinkDirectory(this.repositoryRoot);
|
|
22944
23745
|
}
|
|
22945
23746
|
};
|
|
23747
|
+
async function readJson(path) {
|
|
23748
|
+
try {
|
|
23749
|
+
const value = JSON.parse(await readFile2(path, "utf8"));
|
|
23750
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
23751
|
+
} catch {
|
|
23752
|
+
return null;
|
|
23753
|
+
}
|
|
23754
|
+
}
|
|
23755
|
+
var isRecord3 = (value, keys) => typeof value === "object" && value !== null && Object.keys(value).sort().join(",") === [...keys].sort().join(",");
|
|
22946
23756
|
async function ignoreLinkDirectory(root) {
|
|
22947
23757
|
try {
|
|
22948
|
-
await stat(
|
|
23758
|
+
await stat(join2(root, ".git"));
|
|
22949
23759
|
} catch {
|
|
22950
23760
|
return;
|
|
22951
23761
|
}
|
|
22952
|
-
const file =
|
|
23762
|
+
const file = join2(root, ".gitignore");
|
|
22953
23763
|
let current = "";
|
|
22954
23764
|
try {
|
|
22955
|
-
current = await
|
|
23765
|
+
current = await readFile2(file, "utf8");
|
|
22956
23766
|
} catch {
|
|
22957
23767
|
current = "";
|
|
22958
23768
|
}
|
|
@@ -22965,7 +23775,7 @@ async function ignoreLinkDirectory(root) {
|
|
|
22965
23775
|
|
|
22966
23776
|
// apps/product-cli/dist/repository-init.js
|
|
22967
23777
|
import { createHash } from "node:crypto";
|
|
22968
|
-
import { lstat as lstat2, open as
|
|
23778
|
+
import { lstat as lstat2, open as open4, readFile as readFile3, readdir as readdir3, stat as stat2 } from "node:fs/promises";
|
|
22969
23779
|
import { dirname as dirname2, posix, resolve as resolve2 } from "node:path";
|
|
22970
23780
|
|
|
22971
23781
|
// packages/contracts/dist/application-root.js
|
|
@@ -23933,7 +24743,7 @@ async function cronBlockers(framework, config, applicationDirectory, files) {
|
|
|
23933
24743
|
];
|
|
23934
24744
|
}
|
|
23935
24745
|
const modulePath = framework === "vite" ? "src/ohmyhost/companion.ts" : WORKER_MODULE_PATH;
|
|
23936
|
-
const source = files.includes(modulePath) ? await
|
|
24746
|
+
const source = files.includes(modulePath) ? await readFile3(resolve2(applicationDirectory, modulePath), "utf8") : null;
|
|
23937
24747
|
if (framework !== "functions" && source !== null && !DEFAULT_EXPORT_PATTERN.test(source)) {
|
|
23938
24748
|
return [defaultExportBlocker(modulePath)];
|
|
23939
24749
|
}
|
|
@@ -23957,7 +24767,7 @@ async function storageClientBlockers(config, applicationDirectory, files) {
|
|
|
23957
24767
|
const information = await stat2(absolute);
|
|
23958
24768
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
23959
24769
|
continue;
|
|
23960
|
-
if (callsStorageClient(await
|
|
24770
|
+
if (callsStorageClient(await readFile3(absolute, "utf8")))
|
|
23961
24771
|
return [];
|
|
23962
24772
|
}
|
|
23963
24773
|
return [
|
|
@@ -23978,7 +24788,7 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
|
|
|
23978
24788
|
const information = await stat2(absolute);
|
|
23979
24789
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
23980
24790
|
continue;
|
|
23981
|
-
const source = (await
|
|
24791
|
+
const source = (await readFile3(absolute, "utf8")).replaceAll(INERT_SOURCE_PATTERN, " ");
|
|
23982
24792
|
if (DATABASE_PRIVATE_BINDING_PATTERN.test(source)) {
|
|
23983
24793
|
blockers.push({
|
|
23984
24794
|
code: "database_binding_private",
|
|
@@ -23997,7 +24807,7 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
|
|
|
23997
24807
|
async function workerModuleBlockers(framework, applicationDirectory, files) {
|
|
23998
24808
|
if (framework !== "functions" || !files.includes(WORKER_MODULE_PATH))
|
|
23999
24809
|
return [];
|
|
24000
|
-
const source = await
|
|
24810
|
+
const source = await readFile3(resolve2(applicationDirectory, WORKER_MODULE_PATH), "utf8");
|
|
24001
24811
|
return DEFAULT_EXPORT_PATTERN.test(source) ? [] : [defaultExportBlocker(WORKER_MODULE_PATH)];
|
|
24002
24812
|
}
|
|
24003
24813
|
function migrationFilenameBlockers(files, configuredPath, provider) {
|
|
@@ -24024,7 +24834,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
24024
24834
|
}
|
|
24025
24835
|
const migrations = await Promise.all(migrationPaths.map(async (path) => Object.freeze({
|
|
24026
24836
|
path,
|
|
24027
|
-
body: new Uint8Array(await
|
|
24837
|
+
body: new Uint8Array(await readFile3(resolve2(applicationDirectory, root, path)))
|
|
24028
24838
|
})));
|
|
24029
24839
|
const blockers = [];
|
|
24030
24840
|
for (const migration of migrations) {
|
|
@@ -24063,7 +24873,7 @@ async function readExistingConfiguration(repositoryRoot, repositoryFiles, applic
|
|
|
24063
24873
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES) {
|
|
24064
24874
|
throw new RepositoryInitError("repository_configuration_invalid", "Existing ohmyhost.yaml must be one bounded regular file.");
|
|
24065
24875
|
}
|
|
24066
|
-
const configuration = await
|
|
24876
|
+
const configuration = await readFile3(configurationPath, "utf8");
|
|
24067
24877
|
let parsed;
|
|
24068
24878
|
try {
|
|
24069
24879
|
parsed = parseOhmyhostConfigYaml(configuration);
|
|
@@ -24191,7 +25001,7 @@ async function tryReadPackageJson(root) {
|
|
|
24191
25001
|
}
|
|
24192
25002
|
function hasDirectSafeBuildCommand(packageJson, framework) {
|
|
24193
25003
|
const scripts = packageJson["scripts"];
|
|
24194
|
-
if (!
|
|
25004
|
+
if (!isRecord4(scripts) || typeof scripts["build"] !== "string")
|
|
24195
25005
|
return false;
|
|
24196
25006
|
const command = scripts["build"];
|
|
24197
25007
|
if (command.includes("..") || /(?:^|\s)cd(?:\s|$)/u.test(command))
|
|
@@ -24239,7 +25049,7 @@ async function collectRepositoryFiles(root) {
|
|
|
24239
25049
|
const directory = directories.pop();
|
|
24240
25050
|
if (directory === void 0)
|
|
24241
25051
|
break;
|
|
24242
|
-
for (const entry of await
|
|
25052
|
+
for (const entry of await readdir3(resolve2(root, directory), { withFileTypes: true })) {
|
|
24243
25053
|
const relative = directory === "" ? entry.name : posix.join(directory, entry.name);
|
|
24244
25054
|
if (entry.isSymbolicLink()) {
|
|
24245
25055
|
throw new RepositoryInitError("repository_symlink_forbidden", "Repository symlinks are forbidden");
|
|
@@ -24272,8 +25082,8 @@ async function readPackageJson(root) {
|
|
|
24272
25082
|
throw new RepositoryInitError("package_manifest_invalid", "package.json is invalid");
|
|
24273
25083
|
}
|
|
24274
25084
|
try {
|
|
24275
|
-
const value = JSON.parse(await
|
|
24276
|
-
if (!
|
|
25085
|
+
const value = JSON.parse(await readFile3(path, "utf8"));
|
|
25086
|
+
if (!isRecord4(value))
|
|
24277
25087
|
throw new TypeError();
|
|
24278
25088
|
return value;
|
|
24279
25089
|
} catch {
|
|
@@ -24284,7 +25094,7 @@ function dependencyNames(packageJson) {
|
|
|
24284
25094
|
const names = /* @__PURE__ */ new Set();
|
|
24285
25095
|
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
24286
25096
|
const value = packageJson[field];
|
|
24287
|
-
if (!
|
|
25097
|
+
if (!isRecord4(value))
|
|
24288
25098
|
continue;
|
|
24289
25099
|
for (const [name, version2] of Object.entries(value)) {
|
|
24290
25100
|
if (typeof version2 === "string" && version2.length > 0)
|
|
@@ -24423,9 +25233,9 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
24423
25233
|
});
|
|
24424
25234
|
}
|
|
24425
25235
|
const scripts = packageJson["scripts"];
|
|
24426
|
-
const scriptRecord =
|
|
25236
|
+
const scriptRecord = isRecord4(scripts) ? scripts : {};
|
|
24427
25237
|
if (framework === "functions") {
|
|
24428
|
-
} else if (!
|
|
25238
|
+
} else if (!isRecord4(scripts) || typeof scripts["build"] !== "string" || scripts["build"].length === 0) {
|
|
24429
25239
|
blockers.push({ code: "build_script_missing", message: "Declare a non-empty build script." });
|
|
24430
25240
|
} else if (!hasDirectSafeBuildCommand(packageJson, framework)) {
|
|
24431
25241
|
blockers.push({
|
|
@@ -24441,7 +25251,7 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
24441
25251
|
});
|
|
24442
25252
|
}
|
|
24443
25253
|
if (framework === "nextjs") {
|
|
24444
|
-
const dependencies =
|
|
25254
|
+
const dependencies = isRecord4(packageJson["dependencies"]) ? packageJson["dependencies"] : {};
|
|
24445
25255
|
const nextBuild = scriptRecord["build"] === "next build" || scriptRecord["build"] === "next build --webpack";
|
|
24446
25256
|
const nextPackageConfigured = nextBuild && compatibility.frameworks[0]?.classification !== "unsupported" && exactVersion(dependencies["react"]) && exactVersion(dependencies["react-dom"]) && frameworkConfigs.next !== null;
|
|
24447
25257
|
const nextConfigured = nextPackageConfigured && frameworkConfigs.ambiguous.every((config) => config !== "open-next");
|
|
@@ -24466,7 +25276,7 @@ function exactVersion(value) {
|
|
|
24466
25276
|
function dependencyVersion(packageJson, name) {
|
|
24467
25277
|
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
24468
25278
|
const dependencies = packageJson[field];
|
|
24469
|
-
if (!
|
|
25279
|
+
if (!isRecord4(dependencies))
|
|
24470
25280
|
continue;
|
|
24471
25281
|
const version2 = dependencies[name];
|
|
24472
25282
|
if (typeof version2 === "string" && version2.length > 0)
|
|
@@ -24537,7 +25347,7 @@ async function detectNextStandaloneOutput(root, framework, nextConfigPath) {
|
|
|
24537
25347
|
const information = await stat2(path);
|
|
24538
25348
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
|
|
24539
25349
|
return false;
|
|
24540
|
-
return declaresStandaloneOutput(await
|
|
25350
|
+
return declaresStandaloneOutput(await readFile3(path, "utf8"));
|
|
24541
25351
|
}
|
|
24542
25352
|
function declaresStandaloneOutput(source) {
|
|
24543
25353
|
const tokens = sourceTokens(source);
|
|
@@ -24606,7 +25416,7 @@ async function detectTanStackRuntime(root, files, framework, packageJson, viteCo
|
|
|
24606
25416
|
const information = await stat2(path);
|
|
24607
25417
|
if (!information.isFile() || information.size > MAXIMUM_PACKAGE_BYTES)
|
|
24608
25418
|
return "ambiguous";
|
|
24609
|
-
const source = await
|
|
25419
|
+
const source = await readFile3(path, "utf8");
|
|
24610
25420
|
const tokens = sourceTokens(source);
|
|
24611
25421
|
if (isCompleteTanStackStatic(tokens))
|
|
24612
25422
|
return "static";
|
|
@@ -24678,10 +25488,10 @@ function directSourceObjectProperty(tokens, start, end, name) {
|
|
|
24678
25488
|
}
|
|
24679
25489
|
return null;
|
|
24680
25490
|
}
|
|
24681
|
-
function matchingSourceDelimiter(tokens, start,
|
|
25491
|
+
function matchingSourceDelimiter(tokens, start, open5, close) {
|
|
24682
25492
|
let depth = 0;
|
|
24683
25493
|
for (let index = start; index < tokens.length; index += 1) {
|
|
24684
|
-
if (tokens[index] ===
|
|
25494
|
+
if (tokens[index] === open5)
|
|
24685
25495
|
depth += 1;
|
|
24686
25496
|
if (tokens[index] === close)
|
|
24687
25497
|
depth -= 1;
|
|
@@ -24715,7 +25525,7 @@ async function detectR2Storage(root, files) {
|
|
|
24715
25525
|
const information = await stat2(path);
|
|
24716
25526
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
|
|
24717
25527
|
continue;
|
|
24718
|
-
const source = await
|
|
25528
|
+
const source = await readFile3(path, "utf8");
|
|
24719
25529
|
if (/\br2_buckets\b/u.test(source))
|
|
24720
25530
|
return true;
|
|
24721
25531
|
}
|
|
@@ -24757,7 +25567,7 @@ function viteCompanionScaffold(files, capabilities) {
|
|
|
24757
25567
|
async function createConfiguration(root, configuration) {
|
|
24758
25568
|
let handle;
|
|
24759
25569
|
try {
|
|
24760
|
-
handle = await
|
|
25570
|
+
handle = await open4(resolve2(root, "ohmyhost.yaml"), "wx", 384);
|
|
24761
25571
|
await handle.writeFile(configuration, "utf8");
|
|
24762
25572
|
await handle.sync();
|
|
24763
25573
|
} catch (error) {
|
|
@@ -24770,7 +25580,7 @@ async function createConfiguration(root, configuration) {
|
|
|
24770
25580
|
await handle?.close();
|
|
24771
25581
|
}
|
|
24772
25582
|
}
|
|
24773
|
-
function
|
|
25583
|
+
function isRecord4(value) {
|
|
24774
25584
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24775
25585
|
}
|
|
24776
25586
|
function isNodeError(value) {
|
|
@@ -24778,11 +25588,8 @@ function isNodeError(value) {
|
|
|
24778
25588
|
}
|
|
24779
25589
|
|
|
24780
25590
|
// 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;
|
|
25591
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
25592
|
+
import { join as join3 } from "node:path";
|
|
24786
25593
|
|
|
24787
25594
|
// packages/workos-auth-contracts/dist/device-flow.js
|
|
24788
25595
|
var WORKOS_AUTHKIT_DEVICE_AUTHORIZATION_ENDPOINT = "https://api.workos.com/user_management/authorize/device";
|
|
@@ -25012,37 +25819,99 @@ function environmentToken(environment = {}) {
|
|
|
25012
25819
|
throw new ProductApiAuthenticationError("invalid_environment_token");
|
|
25013
25820
|
return value;
|
|
25014
25821
|
}
|
|
25822
|
+
function environmentProfile(environment = {}) {
|
|
25823
|
+
const value = environment.OHMYHOST_PROFILE;
|
|
25824
|
+
if (value === void 0 || value === "")
|
|
25825
|
+
return void 0;
|
|
25826
|
+
if (!isProfileName(value))
|
|
25827
|
+
throw new ProductApiAuthenticationError("invalid_profile_binding");
|
|
25828
|
+
return value;
|
|
25829
|
+
}
|
|
25830
|
+
var profileRequest = (input, bound) => ({
|
|
25831
|
+
...input?.name === void 0 ? {} : { name: input.name },
|
|
25832
|
+
...bound === void 0 ? {} : { bound },
|
|
25833
|
+
...input?.organizationId === void 0 ? {} : { organizationId: input.organizationId },
|
|
25834
|
+
...input?.userId === void 0 ? {} : { userId: input.userId }
|
|
25835
|
+
});
|
|
25015
25836
|
var createProductionAuthenticatedProductApi = async (signal, overrides = {}) => {
|
|
25016
25837
|
const transport = await createAuthenticatedProductTransport(signal, overrides);
|
|
25017
25838
|
return new GeneratedSdkProductApi(transport.baseUrl, transport.accessToken, transport.fetch);
|
|
25018
25839
|
};
|
|
25019
25840
|
var createAuthenticatedProductTransport = async (signal, overrides = {}) => {
|
|
25020
|
-
const
|
|
25841
|
+
const platform = resolveProductCliEnvironment(overrides.environment);
|
|
25021
25842
|
const apiToken = environmentToken(overrides.environment);
|
|
25843
|
+
const bound = environmentProfile(overrides.environment);
|
|
25844
|
+
const baseUrl = overrides.productApiOrigin ?? platform.apiOrigin;
|
|
25022
25845
|
const boundedFetch = createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES);
|
|
25023
|
-
|
|
25846
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
25847
|
+
const binding = overrides.request?.bindOrganization === true;
|
|
25848
|
+
const request = profileRequest(overrides.request, bound);
|
|
25849
|
+
if (apiToken !== void 0) {
|
|
25850
|
+
await verifyEnvironmentTokenContext({
|
|
25851
|
+
token: apiToken,
|
|
25852
|
+
request: binding ? profileRequest({}, bound) : request,
|
|
25853
|
+
registry,
|
|
25854
|
+
baseUrl,
|
|
25855
|
+
fetch: boundedFetch
|
|
25856
|
+
});
|
|
25024
25857
|
return {
|
|
25025
|
-
baseUrl
|
|
25858
|
+
baseUrl,
|
|
25026
25859
|
accessToken: apiToken,
|
|
25027
25860
|
fetch: boundedFetch,
|
|
25028
|
-
|
|
25029
|
-
|
|
25861
|
+
workspace: void 0,
|
|
25862
|
+
context: { credential: "environment_token", profile: null }
|
|
25030
25863
|
};
|
|
25031
|
-
|
|
25032
|
-
const stored = await store.read();
|
|
25033
|
-
if (stored === void 0)
|
|
25034
|
-
throw new ProductApiAuthenticationError("authentication_required");
|
|
25864
|
+
}
|
|
25035
25865
|
const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
|
|
25036
25866
|
const refresher = overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now);
|
|
25037
|
-
const
|
|
25867
|
+
const profiles = await loadProfiles(registry, {
|
|
25868
|
+
baseUrl,
|
|
25869
|
+
fetch: boundedFetch,
|
|
25870
|
+
signal,
|
|
25871
|
+
now,
|
|
25872
|
+
refresher
|
|
25873
|
+
});
|
|
25874
|
+
const selection = binding && request.organizationId !== void 0 ? selectProfileForOrganization(profiles, {
|
|
25875
|
+
...request,
|
|
25876
|
+
organizationId: request.organizationId
|
|
25877
|
+
}) : selectProfile(profiles, request);
|
|
25878
|
+
if (selection.outcome !== "selected")
|
|
25879
|
+
throw profileSelectionError(selection, request, platform.commandPrefix, overrides.selector ?? "cli");
|
|
25880
|
+
const profile = selection.profile;
|
|
25881
|
+
const store = registry.credential(profile);
|
|
25882
|
+
const stored = await store.read();
|
|
25883
|
+
if (stored === void 0)
|
|
25884
|
+
throw missingLogin(profile, platform.commandPrefix);
|
|
25885
|
+
const credential = await currentCredential(stored, store, signal, now, refresher, () => registry.remove(profile));
|
|
25038
25886
|
return {
|
|
25039
|
-
baseUrl
|
|
25887
|
+
baseUrl,
|
|
25040
25888
|
accessToken: credential.accessToken,
|
|
25041
25889
|
fetch: boundedFetch,
|
|
25042
|
-
workspace: { store, refresher }
|
|
25890
|
+
workspace: { store, refresher, profiles: registry, profile },
|
|
25891
|
+
context: { credential: "profile", profile }
|
|
25892
|
+
};
|
|
25893
|
+
};
|
|
25894
|
+
var listSavedProfiles = async (signal, overrides = {}) => {
|
|
25895
|
+
const platform = resolveProductCliEnvironment(overrides.environment);
|
|
25896
|
+
const apiToken = environmentToken(overrides.environment);
|
|
25897
|
+
const bound = environmentProfile(overrides.environment);
|
|
25898
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
25899
|
+
const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
|
|
25900
|
+
const profiles = apiToken === void 0 ? await loadProfiles(registry, {
|
|
25901
|
+
baseUrl: overrides.productApiOrigin ?? platform.apiOrigin,
|
|
25902
|
+
fetch: createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES),
|
|
25903
|
+
signal,
|
|
25904
|
+
now,
|
|
25905
|
+
refresher: overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now)
|
|
25906
|
+
}) : await registry.list();
|
|
25907
|
+
return {
|
|
25908
|
+
profiles: profiles.map(publicProfile),
|
|
25909
|
+
bound: bound ?? null,
|
|
25910
|
+
environment_token: apiToken !== void 0
|
|
25043
25911
|
};
|
|
25044
25912
|
};
|
|
25045
|
-
var
|
|
25913
|
+
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'.`);
|
|
25914
|
+
var currentCredential = async (stored, store, signal, now, tokenRefresher, forget) => {
|
|
25046
25915
|
const expiresAt = stored.expiresAt === void 0 ? NaN : Date.parse(stored.expiresAt);
|
|
25047
25916
|
if (Number.isFinite(expiresAt) && expiresAt > now().getTime() + REQUEST_TIMEOUT_MS) {
|
|
25048
25917
|
return stored;
|
|
@@ -25058,119 +25927,350 @@ var currentCredential = async (stored, store, signal, now, tokenRefresher) => {
|
|
|
25058
25927
|
return carried;
|
|
25059
25928
|
} catch (error) {
|
|
25060
25929
|
if (error instanceof TokenRefreshRejectedError) {
|
|
25061
|
-
await
|
|
25930
|
+
await forget();
|
|
25062
25931
|
throw new ProductApiAuthenticationError("authentication_required");
|
|
25063
25932
|
}
|
|
25064
25933
|
throw new ProductApiAuthenticationError("token_refresh_failed");
|
|
25065
25934
|
}
|
|
25066
25935
|
};
|
|
25936
|
+
var loadProfiles = async (registry, context) => {
|
|
25937
|
+
const legacy = registry.legacy();
|
|
25938
|
+
const stored = await legacy.read();
|
|
25939
|
+
if (stored !== void 0)
|
|
25940
|
+
await migrateLegacyLogin(registry, legacy, stored, context);
|
|
25941
|
+
return registry.list();
|
|
25942
|
+
};
|
|
25943
|
+
var migrateLegacyLogin = async (registry, legacy, stored, context) => {
|
|
25944
|
+
let credential;
|
|
25945
|
+
try {
|
|
25946
|
+
credential = await currentCredential(stored, legacy, context.signal, context.now, context.refresher, () => legacy.clear());
|
|
25947
|
+
} catch (error) {
|
|
25948
|
+
if (error instanceof ProductApiAuthenticationError && error.code === "authentication_required")
|
|
25949
|
+
return;
|
|
25950
|
+
throw error;
|
|
25951
|
+
}
|
|
25952
|
+
const api = new GeneratedSdkProductApi(context.baseUrl, credential.accessToken, context.fetch);
|
|
25953
|
+
let identity;
|
|
25954
|
+
try {
|
|
25955
|
+
identity = await api.getCurrentIdentity();
|
|
25956
|
+
} catch (error) {
|
|
25957
|
+
if (!isProblem(error, "unauthenticated"))
|
|
25958
|
+
throw error;
|
|
25959
|
+
return;
|
|
25960
|
+
}
|
|
25961
|
+
const userId = verifiedUserId(identity);
|
|
25962
|
+
const organizationId = parseCurrentIdentity(identity).organization_ids[0] ?? null;
|
|
25963
|
+
const organizationName = organizationId === null ? null : (await api.getAccountProfile()).organizations.find((organization) => organization.id === organizationId)?.name ?? null;
|
|
25964
|
+
const login = { userId, organizationId, organizationName };
|
|
25965
|
+
await registry.save({ name: defaultProfileName(await registry.list(), login), ...login }, credential);
|
|
25966
|
+
await legacy.clear();
|
|
25967
|
+
};
|
|
25968
|
+
var isProblem = (error, code) => typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
25969
|
+
var verifyEnvironmentTokenContext = async (input) => {
|
|
25970
|
+
const { request } = input;
|
|
25971
|
+
const named = request.name ?? request.bound;
|
|
25972
|
+
if (named === void 0 && request.organizationId === void 0 && request.userId === void 0)
|
|
25973
|
+
return null;
|
|
25974
|
+
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.`);
|
|
25975
|
+
if (request.name !== void 0 && request.bound !== void 0 && request.name !== request.bound)
|
|
25976
|
+
throw refuse(`This call names profile ${request.name}, but the process is bound to OHMYHOST_PROFILE=${request.bound}.`);
|
|
25977
|
+
const profile = named === void 0 ? void 0 : (await input.registry.list()).find((saved) => saved.name === named);
|
|
25978
|
+
if (named !== void 0 && profile === void 0)
|
|
25979
|
+
throw refuse(`OHMYHOST_TOKEN overrides saved logins in this process, and no saved login is named ${named}.`);
|
|
25980
|
+
const { userId, organizationId } = await readTokenIdentity(input);
|
|
25981
|
+
const owner = `OHMYHOST_TOKEN belongs to user ${userId} in organization ${organizationId ?? "none"},`;
|
|
25982
|
+
if (profile !== void 0 && (profile.userId !== userId || profile.organizationId !== organizationId))
|
|
25983
|
+
throw refuse(`${owner} but this call names ${describeProfile(profile)}.`);
|
|
25984
|
+
if (request.organizationId !== void 0 && request.organizationId !== organizationId)
|
|
25985
|
+
throw refuse(`${owner} but this call targets organization ${request.organizationId}.`);
|
|
25986
|
+
if (request.userId !== void 0 && request.userId !== userId)
|
|
25987
|
+
throw refuse(`${owner} but this call targets user ${request.userId}.`);
|
|
25988
|
+
return { userId, organizationId };
|
|
25989
|
+
};
|
|
25990
|
+
var readTokenIdentity = async (input) => {
|
|
25991
|
+
const identity = parseCurrentIdentity(await new GeneratedSdkProductApi(input.baseUrl, input.token, input.fetch).getCurrentIdentity());
|
|
25992
|
+
return {
|
|
25993
|
+
userId: identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : identity.actor_id,
|
|
25994
|
+
organizationId: identity.organization_ids[0] ?? null
|
|
25995
|
+
};
|
|
25996
|
+
};
|
|
25997
|
+
var commandOrganizationId = (command) => {
|
|
25998
|
+
if (command.kind === "organization-use" || command.kind === "login")
|
|
25999
|
+
return void 0;
|
|
26000
|
+
if (command.kind === "feedback-submit")
|
|
26001
|
+
return command.report.organization_id;
|
|
26002
|
+
return "organizationId" in command && typeof command.organizationId === "string" ? command.organizationId : void 0;
|
|
26003
|
+
};
|
|
26004
|
+
var commandProjectId = (command) => {
|
|
26005
|
+
if (command.kind === "managed-mail")
|
|
26006
|
+
return command.request.projectId;
|
|
26007
|
+
return "projectId" in command && typeof command.projectId === "string" ? command.projectId : void 0;
|
|
26008
|
+
};
|
|
25067
26009
|
var runProductionCli = async (argv, environment, io, signal, overrides = {}) => {
|
|
25068
26010
|
const diagnostics = new StderrDiagnosticSink(io);
|
|
26011
|
+
const written = (result) => {
|
|
26012
|
+
io.writeStdout(result.stdout);
|
|
26013
|
+
if (result.stderr.length > 0)
|
|
26014
|
+
io.writeStderr(result.stderr);
|
|
26015
|
+
return result.exitCode;
|
|
26016
|
+
};
|
|
26017
|
+
let args;
|
|
26018
|
+
let profileName;
|
|
26019
|
+
try {
|
|
26020
|
+
const extracted = extractProfileName(argv);
|
|
26021
|
+
args = extracted.argv;
|
|
26022
|
+
profileName = extracted.profileName;
|
|
26023
|
+
} catch {
|
|
26024
|
+
return written(invalidCommandFailure(recognizedCommandLabel(argv)));
|
|
26025
|
+
}
|
|
26026
|
+
const workingDirectory = overrides.workingDirectory ?? process.cwd();
|
|
25069
26027
|
let command;
|
|
26028
|
+
let linked;
|
|
26029
|
+
let links = { links: [], legacy: null };
|
|
25070
26030
|
try {
|
|
25071
|
-
const parsed = await parseWithLinkedProject(
|
|
25072
|
-
|
|
25073
|
-
|
|
26031
|
+
const parsed = await parseWithLinkedProject(args, async () => {
|
|
26032
|
+
let platform2;
|
|
26033
|
+
try {
|
|
26034
|
+
platform2 = resolveProductCliEnvironment(environment);
|
|
26035
|
+
} catch {
|
|
26036
|
+
return { outcome: "none" };
|
|
26037
|
+
}
|
|
26038
|
+
links = await readProjectLinks(join3(workingDirectory, platform2.linkDirectory), platform2.apiOrigin);
|
|
26039
|
+
let named = profileName;
|
|
26040
|
+
try {
|
|
26041
|
+
named ??= environmentProfile(environment);
|
|
26042
|
+
} catch {
|
|
26043
|
+
}
|
|
26044
|
+
const profiles = named === void 0 ? [] : await (overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform2.credentialService)).list();
|
|
26045
|
+
return chooseLinkedProject(links, profiles.find((profile2) => profile2.name === named)?.organizationId ?? void 0);
|
|
25074
26046
|
});
|
|
25075
26047
|
command = parsed.command;
|
|
25076
|
-
|
|
25077
|
-
|
|
26048
|
+
linked = parsed.linked;
|
|
26049
|
+
if (linked !== null)
|
|
26050
|
+
io.writeStderr(`ohmyhost: using linked project ${linked.projectId}
|
|
25078
26051
|
`);
|
|
25079
26052
|
} catch (error) {
|
|
25080
|
-
|
|
26053
|
+
if (error instanceof LinkedProjectAmbiguousError)
|
|
26054
|
+
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.`));
|
|
26055
|
+
if (error instanceof CredentialStoreUnavailableError)
|
|
26056
|
+
return written(errorDocument(recognizedCommandLabel(args), 3, "credential_store_unavailable", true, "Unlock or repair the selected credential store and retry."));
|
|
26057
|
+
const result = error instanceof InvalidCommandError ? invalidCommandFailure(recognizedCommandLabel(args)) : errorDocument("unknown", 1, "internal_error", false, "Retry the command.");
|
|
25081
26058
|
if (!(error instanceof InvalidCommandError)) {
|
|
25082
26059
|
diagnostics.record({
|
|
25083
|
-
correlationId:
|
|
26060
|
+
correlationId: randomUUID2(),
|
|
25084
26061
|
category: "internal",
|
|
25085
26062
|
subsystem: "parser",
|
|
25086
26063
|
errorKind: "unexpected"
|
|
25087
26064
|
});
|
|
25088
26065
|
}
|
|
25089
|
-
|
|
25090
|
-
return result2.exitCode;
|
|
26066
|
+
return written(result);
|
|
25091
26067
|
}
|
|
25092
26068
|
if (command.kind === "init") {
|
|
26069
|
+
if (profileName !== void 0)
|
|
26070
|
+
return written(invalidCommandFailure("init"));
|
|
25093
26071
|
return runOfflineInit(command, io, diagnostics);
|
|
25094
26072
|
}
|
|
25095
26073
|
if (command.kind === "help" || command.kind === "version") {
|
|
25096
|
-
|
|
25097
|
-
io.writeStdout(result2.stdout);
|
|
25098
|
-
return result2.exitCode;
|
|
26074
|
+
return written(cliMetadataResult(command.kind, command.kind === "help" ? command.topic : void 0));
|
|
25099
26075
|
}
|
|
25100
|
-
let
|
|
26076
|
+
let platform;
|
|
25101
26077
|
try {
|
|
25102
|
-
|
|
26078
|
+
platform = resolveProductCliEnvironment(environment);
|
|
25103
26079
|
} catch {
|
|
25104
|
-
|
|
25105
|
-
io.writeStdout(result2.stdout);
|
|
25106
|
-
return result2.exitCode;
|
|
26080
|
+
return written(errorDocument(recognizedCommandLabel(args), 2, "invalid_environment", false, "Set OHMYHOST_ENVIRONMENT to development or production."));
|
|
25107
26081
|
}
|
|
25108
26082
|
let apiToken;
|
|
25109
26083
|
try {
|
|
25110
26084
|
apiToken = environmentToken(environment);
|
|
25111
26085
|
} catch {
|
|
25112
|
-
|
|
25113
|
-
io.writeStdout(result2.stdout);
|
|
25114
|
-
return result2.exitCode;
|
|
25115
|
-
}
|
|
25116
|
-
if (apiToken !== void 0 && (command.kind === "login" || command.kind === "logout")) {
|
|
25117
|
-
const result2 = errorDocument(recognizedCommandLabel(argv), 2, "environment_token_active", false, "Unset OHMYHOST_TOKEN before changing interactive login credentials. Logout does not revoke a user API key.");
|
|
25118
|
-
io.writeStdout(result2.stdout);
|
|
25119
|
-
return result2.exitCode;
|
|
26086
|
+
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."));
|
|
25120
26087
|
}
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25124
|
-
}
|
|
25125
|
-
|
|
25126
|
-
return runOfflineLogout(io, diagnostics, selectedOverrides);
|
|
26088
|
+
let bound;
|
|
26089
|
+
try {
|
|
26090
|
+
bound = environmentProfile(environment);
|
|
26091
|
+
} catch {
|
|
26092
|
+
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
26093
|
}
|
|
25128
|
-
|
|
26094
|
+
if (apiToken !== void 0 && (command.kind === "login" || command.kind === "logout")) {
|
|
26095
|
+
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."));
|
|
26096
|
+
}
|
|
26097
|
+
const requiredToken = command.kind === "secret-set" ? command.environmentToken : void 0;
|
|
26098
|
+
const handedAccount2 = command.kind === "secret-set" ? requiredToken ?? command.profileAccount : void 0;
|
|
26099
|
+
if (requiredToken !== void 0 && apiToken === void 0)
|
|
26100
|
+
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.`));
|
|
26101
|
+
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
26102
|
+
const linkDirectory = join3(workingDirectory, platform.linkDirectory);
|
|
26103
|
+
const projectId = commandProjectId(command);
|
|
26104
|
+
const organizationId = handedAccount2?.organizationId ?? commandOrganizationId(command) ?? linked?.organizationId ?? (projectId === void 0 ? void 0 : linkedOrganization(links, projectId));
|
|
26105
|
+
const request = profileRequest({
|
|
26106
|
+
...profileName === void 0 ? {} : { name: profileName },
|
|
26107
|
+
...organizationId === void 0 || organizationId === null ? {} : { organizationId },
|
|
26108
|
+
...handedAccount2 === void 0 ? {} : { userId: handedAccount2.userId }
|
|
26109
|
+
}, bound);
|
|
25129
26110
|
const transport = new FetchWorkosPublicTransport(signal, REQUEST_TIMEOUT_MS);
|
|
26111
|
+
const now = () => /* @__PURE__ */ new Date();
|
|
26112
|
+
const refresher = new PublicWorkosTokenRefresher(environment);
|
|
26113
|
+
const boundedFetch = () => createBoundedFetch(fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES);
|
|
26114
|
+
let profile;
|
|
26115
|
+
try {
|
|
26116
|
+
if (apiToken !== void 0) {
|
|
26117
|
+
if (command.kind !== "profile-list")
|
|
26118
|
+
await verifyEnvironmentTokenContext({
|
|
26119
|
+
token: apiToken,
|
|
26120
|
+
request,
|
|
26121
|
+
registry,
|
|
26122
|
+
baseUrl: platform.apiOrigin,
|
|
26123
|
+
fetch: boundedFetch()
|
|
26124
|
+
});
|
|
26125
|
+
if (linked !== null && linked.organizationId === null)
|
|
26126
|
+
await adoptLegacyLink({
|
|
26127
|
+
links,
|
|
26128
|
+
directory: linkDirectory,
|
|
26129
|
+
repositoryRoot: workingDirectory,
|
|
26130
|
+
apiOrigin: platform.apiOrigin,
|
|
26131
|
+
organizationId: (await readTokenIdentity({
|
|
26132
|
+
token: apiToken,
|
|
26133
|
+
baseUrl: platform.apiOrigin,
|
|
26134
|
+
fetch: boundedFetch()
|
|
26135
|
+
})).organizationId,
|
|
26136
|
+
accessToken: apiToken,
|
|
26137
|
+
fetch: boundedFetch(),
|
|
26138
|
+
actor: "OHMYHOST_TOKEN",
|
|
26139
|
+
commandPrefix: platform.commandPrefix
|
|
26140
|
+
});
|
|
26141
|
+
} else if (command.kind === "logout" && !command.revoke) {
|
|
26142
|
+
return await runOfflineLogout(registry, request, platform.commandPrefix, io, diagnostics);
|
|
26143
|
+
} else if (command.kind !== "login") {
|
|
26144
|
+
const profiles = await loadProfiles(registry, {
|
|
26145
|
+
baseUrl: platform.apiOrigin,
|
|
26146
|
+
fetch: boundedFetch(),
|
|
26147
|
+
signal,
|
|
26148
|
+
now,
|
|
26149
|
+
refresher
|
|
26150
|
+
});
|
|
26151
|
+
if (command.kind !== "profile-list") {
|
|
26152
|
+
const selection = command.kind === "organization-use" ? selectProfileForOrganization(profiles, {
|
|
26153
|
+
...request,
|
|
26154
|
+
organizationId: command.organizationId
|
|
26155
|
+
}) : selectProfile(profiles, request);
|
|
26156
|
+
if (selection.outcome !== "selected")
|
|
26157
|
+
throw profileSelectionError(selection, request, platform.commandPrefix, "cli");
|
|
26158
|
+
profile = selection.profile;
|
|
26159
|
+
io.writeStderr(`ohmyhost: acting as ${describeProfile(profile)}
|
|
26160
|
+
`);
|
|
26161
|
+
if (linked !== null && linked.organizationId === null) {
|
|
26162
|
+
const selected2 = await createAuthenticatedProductTransport(signal, {
|
|
26163
|
+
environment,
|
|
26164
|
+
profiles: registry,
|
|
26165
|
+
request: { name: profile.name }
|
|
26166
|
+
});
|
|
26167
|
+
await adoptLegacyLink({
|
|
26168
|
+
links,
|
|
26169
|
+
directory: linkDirectory,
|
|
26170
|
+
repositoryRoot: workingDirectory,
|
|
26171
|
+
apiOrigin: platform.apiOrigin,
|
|
26172
|
+
organizationId: profile.organizationId,
|
|
26173
|
+
accessToken: selected2.accessToken,
|
|
26174
|
+
fetch: selected2.fetch,
|
|
26175
|
+
actor: `profile ${profile.name}`,
|
|
26176
|
+
commandPrefix: platform.commandPrefix
|
|
26177
|
+
});
|
|
26178
|
+
}
|
|
26179
|
+
}
|
|
26180
|
+
}
|
|
26181
|
+
} catch (error) {
|
|
26182
|
+
return written(compositionFailure(recognizedCommandLabel(args), error, signal, platform.commandPrefix));
|
|
26183
|
+
}
|
|
26184
|
+
const selected = profile;
|
|
26185
|
+
const productApiOrigin = platform.apiOrigin;
|
|
25130
26186
|
const lifecycle = new GeneratedSdkSessionRevoker({
|
|
25131
26187
|
environment,
|
|
25132
|
-
|
|
26188
|
+
profiles: registry,
|
|
26189
|
+
...selected === void 0 ? {} : { request: { name: selected.name } }
|
|
25133
26190
|
});
|
|
25134
26191
|
const cli = new ProductCli({
|
|
25135
26192
|
...apiToken === void 0 ? {} : { apiToken },
|
|
25136
|
-
commandPrefix:
|
|
25137
|
-
|
|
26193
|
+
commandPrefix: platform.commandPrefix,
|
|
26194
|
+
profiles: registry,
|
|
26195
|
+
...selected === void 0 ? {} : { profile: selected },
|
|
26196
|
+
// A login in a process bound to one saved login renews exactly that one.
|
|
26197
|
+
...(profileName ?? bound) === void 0 ? {} : { profileName: profileName ?? bound },
|
|
26198
|
+
...bound === void 0 ? {} : { profileBinding: bound },
|
|
25138
26199
|
deviceFlow: new WorkosDeviceFlowClient({
|
|
25139
|
-
clientId:
|
|
26200
|
+
clientId: platform.clientId,
|
|
25140
26201
|
scope: "openid offline_access",
|
|
25141
|
-
verificationUri:
|
|
25142
|
-
}, transport, { now
|
|
26202
|
+
verificationUri: platform.verificationUri
|
|
26203
|
+
}, transport, { now }),
|
|
25143
26204
|
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
|
-
|
|
26205
|
+
productApi: (accessToken) => new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()),
|
|
26206
|
+
// Login receives read-only identity, workspace and GitHub queries instead of the product surface.
|
|
26207
|
+
accountProfile: async (accessToken) => await new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()).getAccountProfile(),
|
|
26208
|
+
currentIdentity: async (accessToken) => await new GeneratedSdkProductApi(productApiOrigin, accessToken, boundedFetch()).getCurrentIdentity(),
|
|
26209
|
+
githubConnectionStatus: (accessToken, organizationId2) => getGithubOrganizationConnection({ organization_id: organizationId2 }, {
|
|
25148
26210
|
client: createClient({
|
|
25149
26211
|
baseUrl: productApiOrigin,
|
|
25150
26212
|
auth: accessToken,
|
|
25151
26213
|
throwOnError: true,
|
|
25152
|
-
fetch:
|
|
26214
|
+
fetch: boundedFetch()
|
|
25153
26215
|
}),
|
|
25154
26216
|
signal
|
|
25155
26217
|
}),
|
|
25156
|
-
projectLinkStore: new OwnerOnlyProjectLinkStore(
|
|
25157
|
-
productApiOrigin
|
|
25158
|
-
tokenRefresher:
|
|
26218
|
+
projectLinkStore: new OwnerOnlyProjectLinkStore(linkDirectory, workingDirectory),
|
|
26219
|
+
productApiOrigin,
|
|
26220
|
+
tokenRefresher: refresher,
|
|
25159
26221
|
tokenRevoker: lifecycle,
|
|
25160
|
-
now
|
|
26222
|
+
now,
|
|
25161
26223
|
waitUntil,
|
|
25162
26224
|
lifecycleTimeoutMs: REQUEST_TIMEOUT_MS,
|
|
25163
|
-
createCorrelationId:
|
|
26225
|
+
createCorrelationId: randomUUID2,
|
|
25164
26226
|
diagnosticSink: diagnostics,
|
|
25165
26227
|
operationEventSink: { write: (value) => io.writeStdout(value) },
|
|
25166
26228
|
readSecretValue: overrides.readSecretValue ?? ((readSignal) => readSecretValueFromStandardInput(process.stdin, readSignal)),
|
|
25167
26229
|
spawnPsql: runLocalPsql
|
|
25168
26230
|
});
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
26231
|
+
return written(await cli.runCommand(command, signal));
|
|
26232
|
+
};
|
|
26233
|
+
var adoptLegacyLink = async (input) => {
|
|
26234
|
+
const legacy = input.links.legacy;
|
|
26235
|
+
if (legacy === null)
|
|
26236
|
+
return;
|
|
26237
|
+
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.`);
|
|
26238
|
+
let organizationId;
|
|
26239
|
+
try {
|
|
26240
|
+
const project = await getProject({ project_id: legacy.project_id }, {
|
|
26241
|
+
client: createClient({
|
|
26242
|
+
baseUrl: input.apiOrigin,
|
|
26243
|
+
auth: input.accessToken,
|
|
26244
|
+
fetch: input.fetch,
|
|
26245
|
+
throwOnError: true
|
|
26246
|
+
})
|
|
26247
|
+
});
|
|
26248
|
+
organizationId = project.organization_id;
|
|
26249
|
+
} catch (error) {
|
|
26250
|
+
if (isProblem(error, "resource_not_found") || isProblem(error, "forbidden"))
|
|
26251
|
+
throw refuse();
|
|
26252
|
+
throw error;
|
|
26253
|
+
}
|
|
26254
|
+
if (organizationId !== input.organizationId)
|
|
26255
|
+
throw refuse();
|
|
26256
|
+
await new OwnerOnlyProjectLinkStore(input.directory, input.repositoryRoot).save({
|
|
26257
|
+
version: 2,
|
|
26258
|
+
api_origin: input.apiOrigin,
|
|
26259
|
+
organization_id: organizationId,
|
|
26260
|
+
project_id: legacy.project_id,
|
|
26261
|
+
installation_id: legacy.installation_id,
|
|
26262
|
+
repository_full_name: legacy.repository_full_name
|
|
26263
|
+
});
|
|
26264
|
+
await removeLegacyProjectLink(input.directory);
|
|
26265
|
+
};
|
|
26266
|
+
var compositionFailure = (command, error, signal, commandPrefix) => {
|
|
26267
|
+
if (error instanceof ProfileSelectionError)
|
|
26268
|
+
return errorDocument(command, error.exitCode, error.code, false, error.suggestedAction);
|
|
26269
|
+
if (error instanceof ProfileLimitError)
|
|
26270
|
+
return errorDocument(command, 2, "profile_limit_reached", false, profileLimitAction(commandPrefix));
|
|
26271
|
+
if (error instanceof ProductApiAuthenticationError)
|
|
26272
|
+
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.`);
|
|
26273
|
+
return classifyCliFailure(command, error, signal);
|
|
25174
26274
|
};
|
|
25175
26275
|
var runOfflineInit = async (command, io, diagnostics) => {
|
|
25176
26276
|
try {
|
|
@@ -25208,7 +26308,7 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
25208
26308
|
} catch (error) {
|
|
25209
26309
|
const known = error instanceof RepositoryInitError;
|
|
25210
26310
|
diagnostics.record({
|
|
25211
|
-
correlationId:
|
|
26311
|
+
correlationId: randomUUID2(),
|
|
25212
26312
|
category: known ? "configuration" : "internal",
|
|
25213
26313
|
subsystem: "runtime",
|
|
25214
26314
|
errorKind: known ? "invalid_response" : "unexpected"
|
|
@@ -25329,7 +26429,7 @@ var GeneratedSdkSessionRevoker = class {
|
|
|
25329
26429
|
fetch: transport.fetch,
|
|
25330
26430
|
throwOnError: true
|
|
25331
26431
|
});
|
|
25332
|
-
const result = await revokeCurrentSession({ "Idempotency-Key":
|
|
26432
|
+
const result = await revokeCurrentSession({ "Idempotency-Key": randomUUID2() }, { client: client2, signal });
|
|
25333
26433
|
if (result.revoked !== true)
|
|
25334
26434
|
throw new LifecycleTransportError();
|
|
25335
26435
|
} catch (error) {
|
|
@@ -25339,25 +26439,27 @@ var GeneratedSdkSessionRevoker = class {
|
|
|
25339
26439
|
}
|
|
25340
26440
|
}
|
|
25341
26441
|
};
|
|
25342
|
-
|
|
26442
|
+
var LinkedProjectAmbiguousError = class extends Error {
|
|
26443
|
+
organizationIds;
|
|
26444
|
+
constructor(organizationIds) {
|
|
26445
|
+
super("Several organizations' links apply");
|
|
26446
|
+
this.organizationIds = organizationIds;
|
|
26447
|
+
this.name = "LinkedProjectAmbiguousError";
|
|
26448
|
+
}
|
|
26449
|
+
};
|
|
26450
|
+
async function parseWithLinkedProject(argv, linkedProject) {
|
|
25343
26451
|
try {
|
|
25344
|
-
return { command: parseProductCliCommand(argv),
|
|
26452
|
+
return { command: parseProductCliCommand(argv), linked: null };
|
|
25345
26453
|
} catch (error) {
|
|
25346
26454
|
if (!(error instanceof InvalidCommandError) || argv.includes("--project"))
|
|
25347
26455
|
throw error;
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
projectId = null;
|
|
25353
|
-
}
|
|
25354
|
-
if (projectId === null)
|
|
26456
|
+
const linked = await linkedProject();
|
|
26457
|
+
if (linked.outcome === "ambiguous")
|
|
26458
|
+
throw new LinkedProjectAmbiguousError(linked.organizationIds);
|
|
26459
|
+
if (linked.outcome === "none")
|
|
25355
26460
|
throw error;
|
|
25356
26461
|
try {
|
|
25357
|
-
return {
|
|
25358
|
-
command: parseProductCliCommand([...argv, "--project", projectId]),
|
|
25359
|
-
linkedProjectId: projectId
|
|
25360
|
-
};
|
|
26462
|
+
return { command: parseProductCliCommand([...argv, "--project", linked.projectId]), linked };
|
|
25361
26463
|
} catch {
|
|
25362
26464
|
throw error;
|
|
25363
26465
|
}
|
|
@@ -25485,14 +26587,25 @@ var waitUntil = async (instant, signal) => {
|
|
|
25485
26587
|
};
|
|
25486
26588
|
var boundedSignal2 = (parent, timeoutMs) => AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
|
|
25487
26589
|
var isAbortOrTimeout = (error) => error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
25488
|
-
var runOfflineLogout = async (io, diagnostics
|
|
26590
|
+
var runOfflineLogout = async (registry, request, commandPrefix, io, diagnostics) => {
|
|
25489
26591
|
let result;
|
|
25490
26592
|
try {
|
|
25491
|
-
|
|
25492
|
-
|
|
26593
|
+
const selection = selectProfile(await registry.list(), request);
|
|
26594
|
+
if (selection.outcome === "selected") {
|
|
26595
|
+
await registry.remove(selection.profile);
|
|
26596
|
+
result = resultDocument("logout", 0, "logged_out", {
|
|
26597
|
+
profile: publicProfile(selection.profile)
|
|
26598
|
+
});
|
|
26599
|
+
} else if (selection.outcome === "none" && request.name === void 0 && request.bound === void 0) {
|
|
26600
|
+
await registry.legacy().clear();
|
|
26601
|
+
result = resultDocument("logout", 0, "logged_out");
|
|
26602
|
+
} else {
|
|
26603
|
+
const failure2 = profileSelectionError(selection, request, commandPrefix, "cli");
|
|
26604
|
+
result = errorDocument("logout", failure2.exitCode, failure2.code, false, failure2.suggestedAction);
|
|
26605
|
+
}
|
|
25493
26606
|
} catch {
|
|
25494
26607
|
diagnostics.record({
|
|
25495
|
-
correlationId:
|
|
26608
|
+
correlationId: randomUUID2(),
|
|
25496
26609
|
category: "credential_store",
|
|
25497
26610
|
subsystem: "credential_store",
|
|
25498
26611
|
errorKind: "unavailable"
|
|
@@ -25533,9 +26646,9 @@ var readSecretValueFromStandardInput = async (input, signal) => {
|
|
|
25533
26646
|
}
|
|
25534
26647
|
};
|
|
25535
26648
|
var invalidCommandFailure = (command) => errorDocument(command, 2, "invalid_command", false, "Check 'ohmyhost --help'.");
|
|
25536
|
-
var resultDocument = (command, exitCode, status) => ({
|
|
26649
|
+
var resultDocument = (command, exitCode, status, body = {}) => ({
|
|
25537
26650
|
exitCode,
|
|
25538
|
-
stdout: `${JSON.stringify({ version: 1, command, status })}
|
|
26651
|
+
stdout: `${JSON.stringify({ version: 1, command, status, ...body })}
|
|
25539
26652
|
`,
|
|
25540
26653
|
stderr: ""
|
|
25541
26654
|
});
|
|
@@ -25558,26 +26671,48 @@ var errorDocument = (command, exitCode, code, retryable, suggestedAction) => ({
|
|
|
25558
26671
|
export {
|
|
25559
26672
|
UserTokenFileError,
|
|
25560
26673
|
prepareUserTokenFile,
|
|
26674
|
+
ProfileNameConflictError,
|
|
26675
|
+
ProfileLimitError,
|
|
26676
|
+
isProfileName,
|
|
26677
|
+
isProfileUserId,
|
|
26678
|
+
sameLogin,
|
|
26679
|
+
profileIndexDirectory,
|
|
26680
|
+
NativeKeyringProfileRegistry,
|
|
26681
|
+
ProfileIndexFiles,
|
|
26682
|
+
selectProfile,
|
|
26683
|
+
selectProfileForOrganization,
|
|
26684
|
+
defaultProfileName,
|
|
26685
|
+
publicProfile,
|
|
26686
|
+
profileLimitAction,
|
|
26687
|
+
ProfileSelectionError,
|
|
26688
|
+
profileSelectionError,
|
|
26689
|
+
describeProfile,
|
|
26690
|
+
extractProfileName,
|
|
25561
26691
|
InvalidCommandError,
|
|
25562
26692
|
recognizedCommandLabel,
|
|
25563
26693
|
parseProductCliCommand,
|
|
25564
|
-
InteractiveSessionRequiredError,
|
|
25565
|
-
chooseWorkspace,
|
|
25566
|
-
selectWorkspace,
|
|
25567
|
-
selectedWorkspaceId,
|
|
25568
|
-
workspaceOf,
|
|
25569
|
-
publicWorkspace,
|
|
25570
26694
|
parsePaidDomainPlan,
|
|
25571
26695
|
parsePaidDomain,
|
|
25572
26696
|
parseCloudflareDnsAuthorization,
|
|
25573
26697
|
parseCloudflareDnsAuthorizationStatus,
|
|
25574
26698
|
parseDevAccessState,
|
|
26699
|
+
parsePoweredByFlag,
|
|
26700
|
+
parseOrganizationReferral,
|
|
25575
26701
|
parseDevAccessTicket,
|
|
25576
26702
|
parseCurrentIdentity,
|
|
25577
26703
|
parseAccountProfile,
|
|
25578
26704
|
parseOperationEvent,
|
|
25579
26705
|
parseSafeProblem,
|
|
25580
26706
|
parseOperation,
|
|
26707
|
+
InteractiveSessionRequiredError,
|
|
26708
|
+
ProfileOrganizationFixedError,
|
|
26709
|
+
AccountVerificationError,
|
|
26710
|
+
chooseWorkspace,
|
|
26711
|
+
verifiedUserId,
|
|
26712
|
+
assertVerifiedLogin,
|
|
26713
|
+
selectWorkspace,
|
|
26714
|
+
workspaceOf,
|
|
26715
|
+
publicWorkspace,
|
|
25581
26716
|
NetworkTransportError,
|
|
25582
26717
|
LifecycleTransportError,
|
|
25583
26718
|
TokenRefreshRejectedError,
|
|
@@ -25587,8 +26722,13 @@ export {
|
|
|
25587
26722
|
PsqlUnavailableError,
|
|
25588
26723
|
cliMetadataResult,
|
|
25589
26724
|
ProductCli,
|
|
26725
|
+
ACCOUNT_SENSITIVE_PROBLEMS,
|
|
26726
|
+
classifyCliFailure,
|
|
25590
26727
|
GeneratedSdkProductApi,
|
|
25591
|
-
|
|
26728
|
+
readProjectLinks,
|
|
26729
|
+
chooseLinkedProject,
|
|
26730
|
+
linkedOrganization,
|
|
26731
|
+
removeLegacyProjectLink,
|
|
25592
26732
|
OwnerOnlyProjectLinkStore,
|
|
25593
26733
|
RepositoryInitError,
|
|
25594
26734
|
RepositoryInitBlockedError,
|
|
@@ -25598,10 +26738,13 @@ export {
|
|
|
25598
26738
|
resolveProductCliEnvironment,
|
|
25599
26739
|
createProductionAuthenticatedProductApi,
|
|
25600
26740
|
createAuthenticatedProductTransport,
|
|
26741
|
+
listSavedProfiles,
|
|
26742
|
+
loadProfiles,
|
|
25601
26743
|
runProductionCli,
|
|
25602
26744
|
StderrDeviceAuthorizationPresenter,
|
|
25603
26745
|
PublicWorkosTokenRefresher,
|
|
25604
26746
|
GeneratedSdkSessionRevoker,
|
|
26747
|
+
LinkedProjectAmbiguousError,
|
|
25605
26748
|
parseWithLinkedProject,
|
|
25606
26749
|
readBoundedResponseBody,
|
|
25607
26750
|
createBoundedFetch,
|