everything-dev 2.0.0-rc.8 → 2.0.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/upload/src/client.cjs +7 -1
- package/dist/apps/upload/src/client.cjs.map +1 -1
- package/dist/apps/upload/src/client.mjs +7 -1
- package/dist/apps/upload/src/client.mjs.map +1 -1
- package/dist/auth-login.cjs +24 -1
- package/dist/auth-login.cjs.map +1 -1
- package/dist/auth-login.mjs +24 -1
- package/dist/auth-login.mjs.map +1 -1
- package/dist/auth-session.cjs +4 -2
- package/dist/auth-session.cjs.map +1 -1
- package/dist/auth-session.mjs +4 -2
- package/dist/auth-session.mjs.map +1 -1
- package/dist/cdn-deploy.cjs +11 -7
- package/dist/cdn-deploy.cjs.map +1 -1
- package/dist/cdn-deploy.mjs +11 -7
- package/dist/cdn-deploy.mjs.map +1 -1
- package/dist/cli/import.cjs +1140 -0
- package/dist/cli/import.cjs.map +1 -0
- package/dist/cli/import.mjs +1126 -0
- package/dist/cli/import.mjs.map +1 -0
- package/dist/cli/init.cjs +11 -4
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +8 -2
- package/dist/cli/init.d.mts +8 -2
- package/dist/cli/init.mjs +11 -5
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli.cjs +55 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +55 -0
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/import.cjs +49 -0
- package/dist/commands/import.cjs.map +1 -0
- package/dist/commands/import.mjs +46 -0
- package/dist/commands/import.mjs.map +1 -0
- package/dist/commands/keys.cjs +24 -3
- package/dist/commands/keys.cjs.map +1 -1
- package/dist/commands/keys.mjs +24 -3
- package/dist/commands/keys.mjs.map +1 -1
- package/dist/config.d.cts +1 -0
- package/dist/config.d.mts +1 -0
- package/dist/contract.cjs +89 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +190 -1
- package/dist/contract.d.mts +190 -1
- package/dist/contract.meta.cjs +16 -0
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +26 -0
- package/dist/contract.meta.d.mts +26 -0
- package/dist/contract.meta.mjs +16 -0
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +84 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/custody-signer.cjs +70 -0
- package/dist/custody-signer.cjs.map +1 -0
- package/dist/custody-signer.mjs +68 -0
- package/dist/custody-signer.mjs.map +1 -0
- package/dist/descriptor/schema.cjs +8 -5
- package/dist/descriptor/schema.cjs.map +1 -1
- package/dist/descriptor/schema.d.cts +1 -0
- package/dist/descriptor/schema.d.mts +1 -0
- package/dist/descriptor/schema.mjs +8 -5
- package/dist/descriptor/schema.mjs.map +1 -1
- package/dist/descriptor/serialize.cjs +1 -1
- package/dist/descriptor/serialize.cjs.map +1 -1
- package/dist/descriptor/serialize.mjs +1 -1
- package/dist/descriptor/serialize.mjs.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/outlayer-login.cjs +54 -0
- package/dist/outlayer-login.cjs.map +1 -0
- package/dist/outlayer-login.mjs +54 -0
- package/dist/outlayer-login.mjs.map +1 -0
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +74 -0
- package/dist/plugin.d.mts +74 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/publish.cjs +145 -22
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +147 -24
- package/dist/publish.mjs.map +1 -1
- package/dist/source-archive.cjs +51 -0
- package/dist/source-archive.cjs.map +1 -0
- package/dist/source-archive.mjs +50 -0
- package/dist/source-archive.mjs.map +1 -0
- package/dist/storage-upload.cjs +6 -1
- package/dist/storage-upload.cjs.map +1 -1
- package/dist/storage-upload.mjs +6 -1
- package/dist/storage-upload.mjs.map +1 -1
- package/dist/types.cjs +5 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +5 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/workspace-subtree.cjs +37 -0
- package/dist/utils/workspace-subtree.cjs.map +1 -0
- package/dist/utils/workspace-subtree.mjs +36 -0
- package/dist/utils/workspace-subtree.mjs.map +1 -0
- package/dist/version-manifest-deploy.cjs +4 -2
- package/dist/version-manifest-deploy.cjs.map +1 -1
- package/dist/version-manifest-deploy.mjs +4 -2
- package/dist/version-manifest-deploy.mjs.map +1 -1
- package/dist/version-manifest-resolve.cjs +2 -1
- package/dist/version-manifest-resolve.cjs.map +1 -1
- package/dist/version-manifest-resolve.d.cts +3 -0
- package/dist/version-manifest-resolve.d.mts +3 -0
- package/dist/version-manifest-resolve.mjs +2 -1
- package/dist/version-manifest-resolve.mjs.map +1 -1
- package/package.json +3 -2
- package/skills/cli-reference/SKILL.md +17 -1
- package/skills/init-upgrade/SKILL.md +76 -3
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { MISSING_CONFIG_MESSAGE, findConfigPath } from "../config.mjs";
|
|
2
|
+
import { importWorkspace } from "../cli/import.mjs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
|
|
7
|
+
//#region src/commands/import.ts
|
|
8
|
+
function registerImport(builder) {
|
|
9
|
+
return { import: builder.import.handler(async ({ input }) => {
|
|
10
|
+
const configPath = findConfigPath();
|
|
11
|
+
if (!configPath) return {
|
|
12
|
+
status: "error",
|
|
13
|
+
workspace: input.workspace,
|
|
14
|
+
from: input.from ?? "",
|
|
15
|
+
ref: "",
|
|
16
|
+
added: [],
|
|
17
|
+
updated: [],
|
|
18
|
+
unchanged: [],
|
|
19
|
+
preservedLocal: [],
|
|
20
|
+
merged: [],
|
|
21
|
+
conflicts: [],
|
|
22
|
+
heldBackMigrations: [],
|
|
23
|
+
migrationCollisions: [],
|
|
24
|
+
warnings: [],
|
|
25
|
+
error: `${MISSING_CONFIG_MESSAGE} in current directory`
|
|
26
|
+
};
|
|
27
|
+
const options = input;
|
|
28
|
+
const projectDir = dirname(configPath);
|
|
29
|
+
const confirm = !options.yes && !options.dryRun && process.stdin.isTTY && process.stdout.isTTY ? async (info) => {
|
|
30
|
+
const collisionNote = info.migrationCollisionCount > 0 ? ` + ${info.migrationCollisionCount} unresolvable migration collision(s)` : "";
|
|
31
|
+
const heldNote = info.heldCount > 0 ? `; ${info.heldCount} migration-chain file(s) will be held back` : "";
|
|
32
|
+
const appendedNote = info.appendedCount > 0 ? `; ${info.appendedCount} upstream migration(s) will be renumbered and appended to the local chain` : "";
|
|
33
|
+
const answer = await p.confirm({
|
|
34
|
+
message: info.conflictCount > 0 || info.migrationCollisionCount > 0 ? `${info.conflictCount} conflicting file(s)${collisionNote} in ${info.workspace} — apply upstream over conflicting files (local copies backed up)${heldNote}?` : `Migration chain reconciliation in ${info.workspace}: apply upstream migrations${appendedNote}${heldNote}?`,
|
|
35
|
+
initialValue: false
|
|
36
|
+
});
|
|
37
|
+
if (p.isCancel(answer)) return false;
|
|
38
|
+
return answer === true;
|
|
39
|
+
} : void 0;
|
|
40
|
+
return importWorkspace(projectDir, options, { confirm });
|
|
41
|
+
}) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { registerImport };
|
|
46
|
+
//# sourceMappingURL=import.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.mjs","names":[],"sources":["../../src/commands/import.ts"],"sourcesContent":["import { dirname } from \"node:path\";\nimport process from \"node:process\";\nimport * as p from \"@clack/prompts\";\nimport type { ImportDeps } from \"../cli/import\";\nimport { importWorkspace } from \"../cli/import\";\nimport { findConfigPath, MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { ImportOptions, ImportResult } from \"../contract\";\nimport type { BosBuilder } from \"./shared\";\n\nexport function registerImport(builder: BosBuilder) {\n return {\n import: builder.import.handler(async ({ input }): Promise<ImportResult> => {\n const configPath = findConfigPath();\n if (!configPath) {\n return {\n status: \"error\",\n workspace: input.workspace,\n from: input.from ?? \"\",\n ref: \"\",\n added: [],\n updated: [],\n unchanged: [],\n preservedLocal: [],\n merged: [],\n conflicts: [],\n heldBackMigrations: [],\n migrationCollisions: [],\n warnings: [],\n error: `${MISSING_CONFIG_MESSAGE} in current directory`,\n };\n }\n\n const options = input as ImportOptions;\n const projectDir = dirname(configPath);\n const interactive =\n !options.yes && !options.dryRun && process.stdin.isTTY && process.stdout.isTTY;\n\n const confirm = interactive\n ? async (info: Parameters<NonNullable<ImportDeps[\"confirm\"]>>[0]): Promise<boolean> => {\n const collisionNote =\n info.migrationCollisionCount > 0\n ? ` + ${info.migrationCollisionCount} unresolvable migration collision(s)`\n : \"\";\n const heldNote =\n info.heldCount > 0\n ? `; ${info.heldCount} migration-chain file(s) will be held back`\n : \"\";\n const appendedNote =\n info.appendedCount > 0\n ? `; ${info.appendedCount} upstream migration(s) will be renumbered and appended to the local chain`\n : \"\";\n const answer = await p.confirm({\n message:\n info.conflictCount > 0 || info.migrationCollisionCount > 0\n ? `${info.conflictCount} conflicting file(s)${collisionNote} in ${info.workspace} — apply upstream over conflicting files (local copies backed up)${heldNote}?`\n : `Migration chain reconciliation in ${info.workspace}: apply upstream migrations${appendedNote}${heldNote}?`,\n initialValue: false,\n });\n if (p.isCancel(answer)) return false;\n return answer === true;\n }\n : undefined;\n\n return importWorkspace(projectDir, options, { confirm });\n }),\n };\n}\n"],"mappings":";;;;;;;AASA,SAAgB,eAAe,SAAqB;CAClD,OAAO,EACL,QAAQ,QAAQ,OAAO,QAAQ,OAAO,EAAE,YAAmC;EACzE,MAAM,aAAa,eAAe;EAClC,IAAI,CAAC,YACH,OAAO;GACL,QAAQ;GACR,WAAW,MAAM;GACjB,MAAM,MAAM,QAAQ;GACpB,KAAK;GACL,OAAO,CAAC;GACR,SAAS,CAAC;GACV,WAAW,CAAC;GACZ,gBAAgB,CAAC;GACjB,QAAQ,CAAC;GACT,WAAW,CAAC;GACZ,oBAAoB,CAAC;GACrB,qBAAqB,CAAC;GACtB,UAAU,CAAC;GACX,OAAO,GAAG,uBAAuB;EACnC;EAGF,MAAM,UAAU;EAChB,MAAM,aAAa,QAAQ,UAAU;EAIrC,MAAM,UAFJ,CAAC,QAAQ,OAAO,CAAC,QAAQ,UAAU,QAAQ,MAAM,SAAS,QAAQ,OAAO,QAGvE,OAAO,SAA8E;GACnF,MAAM,gBACJ,KAAK,0BAA0B,IAC3B,MAAM,KAAK,wBAAwB,wCACnC;GACN,MAAM,WACJ,KAAK,YAAY,IACb,KAAK,KAAK,UAAU,8CACpB;GACN,MAAM,eACJ,KAAK,gBAAgB,IACjB,KAAK,KAAK,cAAc,6EACxB;GACN,MAAM,SAAS,MAAM,EAAE,QAAQ;IAC7B,SACE,KAAK,gBAAgB,KAAK,KAAK,0BAA0B,IACrD,GAAG,KAAK,cAAc,sBAAsB,cAAc,MAAM,KAAK,UAAU,mEAAmE,SAAS,KAC3J,qCAAqC,KAAK,UAAU,6BAA6B,eAAe,SAAS;IAC/G,cAAc;GAChB,CAAC;GACD,IAAI,EAAE,SAAS,MAAM,GAAG,OAAO;GAC/B,OAAO,WAAW;EACpB,IACA;EAEJ,OAAO,gBAAgB,YAAY,SAAS,EAAE,QAAQ,CAAC;CACzD,CAAC,EACH;AACF"}
|
package/dist/commands/keys.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const require_shared = require('./shared.cjs');
|
|
|
7
7
|
const require_auth_session = require('../auth-session.cjs');
|
|
8
8
|
const require_auth_login = require('../auth-login.cjs');
|
|
9
9
|
const require_near_cli = require('../near-cli.cjs');
|
|
10
|
+
const require_outlayer_login = require('../outlayer-login.cjs');
|
|
10
11
|
let node_path = require("node:path");
|
|
11
12
|
let effect = require("effect");
|
|
12
13
|
let node_process = require("node:process");
|
|
@@ -92,6 +93,7 @@ function registerKeys(builder) {
|
|
|
92
93
|
siteUrl,
|
|
93
94
|
device: input.device,
|
|
94
95
|
account: account || void 0,
|
|
96
|
+
custody: input.custody ? { network: require_network.getNetworkIdForAccount(account) } : void 0,
|
|
95
97
|
expiresIn: input.expiresIn
|
|
96
98
|
});
|
|
97
99
|
console.log();
|
|
@@ -109,7 +111,9 @@ function registerKeys(builder) {
|
|
|
109
111
|
error: "Login approved but no CLI credential was created"
|
|
110
112
|
};
|
|
111
113
|
const expiresAt = new Date(Date.now() + input.expiresIn * 1e3).toISOString();
|
|
112
|
-
|
|
114
|
+
const configDir = deps.session?.root ?? node_process.default.cwd();
|
|
115
|
+
const existingSession = require_auth_session.readSessionHandle(configDir);
|
|
116
|
+
require_auth_session.writeSessionHandle(configDir, {
|
|
113
117
|
version: 1,
|
|
114
118
|
credential: {
|
|
115
119
|
kind: "session",
|
|
@@ -122,16 +126,26 @@ function registerKeys(builder) {
|
|
|
122
126
|
expiresAt
|
|
123
127
|
},
|
|
124
128
|
publishKey: null,
|
|
125
|
-
delegateKey: null
|
|
129
|
+
delegateKey: null,
|
|
130
|
+
custodyWallet: approval.custodyWallet ?? existingSession?.custodyWallet ?? null
|
|
126
131
|
});
|
|
127
132
|
let warning = null;
|
|
128
|
-
if (
|
|
133
|
+
if (input.custody && !approval.custodyWallet) warning = "Custody wallet credential could not be fetched after approval. Re-run `bos login --custody` — the wallet is kept server-side.";
|
|
134
|
+
if (account && approval.accountId && approval.accountId !== account) warning = warning ? `${warning} Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.` : `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;
|
|
129
135
|
let publishKey = null;
|
|
130
136
|
if (input.key) try {
|
|
131
137
|
publishKey = await exportPublishKey(account, input.registry);
|
|
132
138
|
} catch (error) {
|
|
133
139
|
warning = `Session stored, but publish-key export failed: ${error instanceof Error ? error.message : "unknown error"}`;
|
|
134
140
|
}
|
|
141
|
+
let outlayerCli = null;
|
|
142
|
+
if (input.custody && approval.custodyWallet) {
|
|
143
|
+
outlayerCli = await require_outlayer_login.loginOutlayerCli(approval.custodyWallet);
|
|
144
|
+
if (outlayerCli.status === "error") {
|
|
145
|
+
const failure = `OutLayer CLI login failed: ${outlayerCli.detail}`;
|
|
146
|
+
warning = warning ? `${warning} ${failure}` : failure;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
135
149
|
return {
|
|
136
150
|
status: "logged-in",
|
|
137
151
|
siteUrl,
|
|
@@ -139,6 +153,13 @@ function registerKeys(builder) {
|
|
|
139
153
|
expiresAt,
|
|
140
154
|
loginUrl: login.verificationUrl,
|
|
141
155
|
publishKey,
|
|
156
|
+
custodyWallet: approval.custodyWallet ? {
|
|
157
|
+
walletId: approval.custodyWallet.walletId,
|
|
158
|
+
nearAccountId: approval.custodyWallet.nearAccountId,
|
|
159
|
+
network: approval.custodyWallet.network,
|
|
160
|
+
handoffUrl: approval.custodyWallet.handoffUrl
|
|
161
|
+
} : null,
|
|
162
|
+
outlayerCli,
|
|
142
163
|
warning
|
|
143
164
|
};
|
|
144
165
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.cjs","names":["Context","BosDepsTag","MISSING_CONFIG_MESSAGE","getNetworkIdForAccount","getRegistryNamespaceForAccount","Effect","ensureNearCli","listPublishKeys","addFunctionCallAccessKey","colors","deleteAccessKeys","startDeviceLogin","openInBrowser","process","readSessionHandle","generateNearKeyPair","join","nearCredentialsPath","parseKey"],"sources":["../../src/commands/keys.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport process from \"node:process\";\nimport { Context, Effect } from \"effect\";\nimport { type KeyPair, parseKey } from \"near-kit\";\nimport { openInBrowser, startDeviceLogin } from \"../auth-login\";\nimport {\n deleteSessionHandle,\n nearCredentialsPath,\n readSessionHandle,\n removePublishedKeyFile,\n type SessionCredential,\n writeSessionHandle,\n} from \"../auth-session\";\nimport { MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { LoginResult } from \"../contract\";\nimport { getRegistryNamespaceForAccount } from \"../fastkv\";\nimport {\n addFunctionCallAccessKey,\n deleteAccessKeys,\n ensureNearCli,\n generateNearKeyPair,\n listPublishKeys,\n} from \"../near-cli\";\nimport { getNetworkIdForAccount } from \"../network\";\nimport type { ResolutionSession } from \"../resolution/session\";\nimport { colors } from \"../utils/theme\";\nimport { type BosBuilder, BosDepsTag } from \"./shared\";\n\nconst PUBLISH_FUNCTION_NAMES = [\"__fastdata_kv\"];\n\nexport function registerKeys(builder: BosBuilder) {\n return {\n keyPublish: builder.keyPublish.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n account: \"\",\n network: \"mainnet\" as const,\n env: input.env,\n contract: \"\",\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const account =\n input.env === \"staging\"\n ? (session.config.staging?.account ?? session.config.account)\n : session.config.account;\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, input.registry);\n try {\n await Effect.runPromise(ensureNearCli);\n\n const oldKeys = await listPublishKeys({ account, contract, network });\n\n const keyPair = await addFunctionCallAccessKey({\n account,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n });\n\n if (oldKeys.length > 0) {\n console.log();\n console.log(\n ` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? \"s\" : \"\"}:`,\n );\n for (const k of oldKeys) {\n console.log(` ${colors.dim(k)}`);\n }\n\n if (input.removeOldKeys === true) {\n try {\n await deleteAccessKeys(account, oldKeys, network);\n console.log(\n ` ${colors.green(\"✓\")} Removed ${oldKeys.length} old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n }`,\n );\n } catch {\n console.log(\n ` ${colors.yellow(\"⚠\")} Failed to remove old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n } (new key still active)`,\n );\n }\n } else {\n console.log(` ${colors.dim(\"Old key(s) retained.\")}`);\n }\n }\n\n return {\n status: \"published\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n login: builder.login.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const staging = input.env === \"staging\";\n const account = staging\n ? (deps.session?.config?.staging?.account ?? deps.session?.config?.account ?? \"\")\n : (deps.session?.config?.account ?? \"\");\n\n try {\n const siteUrl = resolveLoginSiteUrl(input.site, deps.session, staging);\n const login = await startDeviceLogin({\n siteUrl,\n device: input.device,\n account: account || undefined,\n expiresIn: input.expiresIn,\n });\n\n console.log();\n console.log(` One-time code: ${colors.cyan(login.userCode)}`);\n await openInBrowser(login.verificationUrl).catch((error: unknown) => {\n console.log(colors.yellow(` ⚠ ${(error as Error).message}`));\n });\n console.log();\n console.log(` Waiting for approval at ${colors.dim(login.verificationUrl)}…`);\n\n const approval = await login.waitForApproval();\n\n if (!approval.apiKey) {\n return {\n status: \"error\" as const,\n siteUrl,\n loginUrl: login.verificationUrl,\n error: \"Login approved but no CLI credential was created\",\n };\n }\n\n const expiresAt = new Date(Date.now() + input.expiresIn * 1000).toISOString();\n writeSessionHandle(deps.session?.root ?? process.cwd(), {\n version: 1,\n credential: {\n kind: \"session\",\n apiKey: approval.apiKey.key,\n apiKeyId: approval.apiKey.id,\n accountId: approval.accountId,\n label: input.device ?? siteUrl,\n siteUrl,\n createdAt: new Date().toISOString(),\n expiresAt,\n },\n publishKey: null,\n delegateKey: null,\n });\n\n let warning: string | null = null;\n if (account && approval.accountId && approval.accountId !== account) {\n warning = `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;\n }\n\n let publishKey: LoginResult[\"publishKey\"] = null;\n if (input.key) {\n try {\n publishKey = await exportPublishKey(account, input.registry);\n } catch (error) {\n warning = `Session stored, but publish-key export failed: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`;\n }\n }\n\n return {\n status: \"logged-in\" as const,\n siteUrl,\n accountId: approval.accountId,\n expiresAt,\n loginUrl: login.verificationUrl,\n publishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n siteUrl: input.site ?? \"\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n logout: builder.logout.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const configDir = input.configDir ?? deps.session?.root ?? process.cwd();\n\n try {\n const session = readSessionHandle(configDir);\n let revokedApiKey = false;\n let removedPublishKey = false;\n let warning: string | null = null;\n\n if (session?.credential) {\n revokedApiKey = await revokeApiKey(session.credential).catch(() => false);\n if (!revokedApiKey) {\n warning =\n \"Could not revoke the API key remotely — revoke it manually under Settings → API keys.\";\n }\n }\n\n if (session?.publishKey) {\n const account = session.credential?.accountId ?? deps.session?.config?.account ?? \"\";\n removePublishedKeyFile(session.publishKey.network, account, session.publishKey.publicKey);\n removedPublishKey = true;\n warning = warning\n ? `${warning} Re-run ${colors.cyan(\"bos login --key\")} to re-export a publish key.`\n : \"Removed the exported publish key. Re-run bos login --key to re-export one.\";\n }\n\n deleteSessionHandle(configDir);\n return {\n status: \"logged-out\" as const,\n revokedApiKey,\n removedPublishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n revokedApiKey: false,\n removedPublishKey: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n };\n}\n\nfunction resolveLoginSiteUrl(\n siteInput: string | undefined,\n session: ResolutionSession | null,\n staging: boolean,\n): string {\n if (siteInput) {\n return siteInput.replace(/\\/+$/, \"\");\n }\n if (staging) {\n const stagingDomain = session?.config?.staging?.domain ?? session?.config?.domain;\n return stagingDomain ? `https://${stagingDomain}` : (session?.runtime?.ui?.url ?? \"\");\n }\n const devUiUrl = session?.runtime?.ui?.url;\n if (devUiUrl && /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)/.test(devUiUrl)) {\n return devUiUrl.replace(/\\/+$/, \"\");\n }\n const domain = session?.config?.domain;\n return domain ? `https://${domain}` : \"\";\n}\n\nasync function exportPublishKey(\n account: string,\n registry: string | undefined,\n): Promise<NonNullable<LoginResult[\"publishKey\"]>> {\n if (!account) {\n throw new Error(\"the authored config has no account to export a publish key for\");\n }\n\n await Effect.runPromise(ensureNearCli);\n\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, registry);\n await listPublishKeys({ account, contract, network });\n\n const keyPair = generateNearKeyPair();\n await addFunctionCallAccessKey({\n account,\n contract,\n allowance: \"1NEAR\",\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n keyPair,\n });\n\n const { FileKeyStore } = await import(\"near-kit/keys/file\");\n const keyStore = new FileKeyStore(\"~/.near-credentials\", network);\n await keyStore.add(account, parseNearPrivateKey(keyPair.privateKey));\n try {\n const { statSync, chmodSync } = await import(\"node:fs\");\n const { homedir } = await import(\"node:os\");\n const credentialPath = join(\n homedir(),\n \".near-credentials\",\n network === \"mainnet\" ? \"mainnet\" : network,\n `${account}.json`,\n );\n if (statSync(credentialPath).mode & 0o077) {\n chmodSync(credentialPath, 0o600);\n }\n } catch {}\n\n return {\n publicKey: keyPair.publicKey,\n network,\n contract,\n exportedTo: nearCredentialsPath(network, account),\n };\n}\nfunction parseNearPrivateKey(privateKey: string): KeyPair {\n return parseKey(privateKey) as KeyPair;\n}\n\nasync function revokeApiKey(credential: SessionCredential): Promise<boolean> {\n if (!credential.apiKey || !credential.siteUrl) return false;\n try {\n const response = await fetch(`${credential.siteUrl}/api/auth/api-key/delete`, {\n method: \"POST\",\n headers: {\n \"x-api-key\": credential.apiKey,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ keyId: credential.apiKeyId }),\n signal: AbortSignal.timeout(10_000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keys.cjs","names":["Context","BosDepsTag","MISSING_CONFIG_MESSAGE","getNetworkIdForAccount","getRegistryNamespaceForAccount","Effect","ensureNearCli","listPublishKeys","addFunctionCallAccessKey","colors","deleteAccessKeys","startDeviceLogin","openInBrowser","process","readSessionHandle","loginOutlayerCli","generateNearKeyPair","join","nearCredentialsPath","parseKey"],"sources":["../../src/commands/keys.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport process from \"node:process\";\nimport { Context, Effect } from \"effect\";\nimport { type KeyPair, parseKey } from \"near-kit\";\nimport { openInBrowser, startDeviceLogin } from \"../auth-login\";\nimport {\n deleteSessionHandle,\n nearCredentialsPath,\n readSessionHandle,\n removePublishedKeyFile,\n type SessionCredential,\n writeSessionHandle,\n} from \"../auth-session\";\nimport { MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { LoginResult } from \"../contract\";\nimport { getRegistryNamespaceForAccount } from \"../fastkv\";\nimport {\n addFunctionCallAccessKey,\n deleteAccessKeys,\n ensureNearCli,\n generateNearKeyPair,\n listPublishKeys,\n} from \"../near-cli\";\nimport { getNetworkIdForAccount } from \"../network\";\nimport { loginOutlayerCli } from \"../outlayer-login\";\nimport type { ResolutionSession } from \"../resolution/session\";\nimport { colors } from \"../utils/theme\";\nimport { type BosBuilder, BosDepsTag } from \"./shared\";\n\nconst PUBLISH_FUNCTION_NAMES = [\"__fastdata_kv\"];\n\nexport function registerKeys(builder: BosBuilder) {\n return {\n keyPublish: builder.keyPublish.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n account: \"\",\n network: \"mainnet\" as const,\n env: input.env,\n contract: \"\",\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const account =\n input.env === \"staging\"\n ? (session.config.staging?.account ?? session.config.account)\n : session.config.account;\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, input.registry);\n try {\n await Effect.runPromise(ensureNearCli);\n\n const oldKeys = await listPublishKeys({ account, contract, network });\n\n const keyPair = await addFunctionCallAccessKey({\n account,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n });\n\n if (oldKeys.length > 0) {\n console.log();\n console.log(\n ` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? \"s\" : \"\"}:`,\n );\n for (const k of oldKeys) {\n console.log(` ${colors.dim(k)}`);\n }\n\n if (input.removeOldKeys === true) {\n try {\n await deleteAccessKeys(account, oldKeys, network);\n console.log(\n ` ${colors.green(\"✓\")} Removed ${oldKeys.length} old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n }`,\n );\n } catch {\n console.log(\n ` ${colors.yellow(\"⚠\")} Failed to remove old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n } (new key still active)`,\n );\n }\n } else {\n console.log(` ${colors.dim(\"Old key(s) retained.\")}`);\n }\n }\n\n return {\n status: \"published\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n login: builder.login.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const staging = input.env === \"staging\";\n const account = staging\n ? (deps.session?.config?.staging?.account ?? deps.session?.config?.account ?? \"\")\n : (deps.session?.config?.account ?? \"\");\n\n try {\n const siteUrl = resolveLoginSiteUrl(input.site, deps.session, staging);\n const login = await startDeviceLogin({\n siteUrl,\n device: input.device,\n account: account || undefined,\n custody: input.custody ? { network: getNetworkIdForAccount(account) } : undefined,\n expiresIn: input.expiresIn,\n });\n\n console.log();\n console.log(` One-time code: ${colors.cyan(login.userCode)}`);\n await openInBrowser(login.verificationUrl).catch((error: unknown) => {\n console.log(colors.yellow(` ⚠ ${(error as Error).message}`));\n });\n console.log();\n console.log(` Waiting for approval at ${colors.dim(login.verificationUrl)}…`);\n\n const approval = await login.waitForApproval();\n\n if (!approval.apiKey) {\n return {\n status: \"error\" as const,\n siteUrl,\n loginUrl: login.verificationUrl,\n error: \"Login approved but no CLI credential was created\",\n };\n }\n\n const expiresAt = new Date(Date.now() + input.expiresIn * 1000).toISOString();\n const configDir = deps.session?.root ?? process.cwd();\n const existingSession = readSessionHandle(configDir);\n writeSessionHandle(configDir, {\n version: 1,\n credential: {\n kind: \"session\",\n apiKey: approval.apiKey.key,\n apiKeyId: approval.apiKey.id,\n accountId: approval.accountId,\n label: input.device ?? siteUrl,\n siteUrl,\n createdAt: new Date().toISOString(),\n expiresAt,\n },\n publishKey: null,\n delegateKey: null,\n custodyWallet: approval.custodyWallet ?? existingSession?.custodyWallet ?? null,\n });\n\n let warning: string | null = null;\n if (input.custody && !approval.custodyWallet) {\n warning =\n \"Custody wallet credential could not be fetched after approval. Re-run `bos login --custody` — the wallet is kept server-side.\";\n }\n if (account && approval.accountId && approval.accountId !== account) {\n warning = warning\n ? `${warning} Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`\n : `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;\n }\n\n let publishKey: LoginResult[\"publishKey\"] = null;\n if (input.key) {\n try {\n publishKey = await exportPublishKey(account, input.registry);\n } catch (error) {\n warning = `Session stored, but publish-key export failed: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`;\n }\n }\n\n let outlayerCli: LoginResult[\"outlayerCli\"] = null;\n if (input.custody && approval.custodyWallet) {\n outlayerCli = await loginOutlayerCli(approval.custodyWallet);\n if (outlayerCli.status === \"error\") {\n const failure = `OutLayer CLI login failed: ${outlayerCli.detail}`;\n warning = warning ? `${warning} ${failure}` : failure;\n }\n }\n\n return {\n status: \"logged-in\" as const,\n siteUrl,\n accountId: approval.accountId,\n expiresAt,\n loginUrl: login.verificationUrl,\n publishKey,\n custodyWallet: approval.custodyWallet\n ? {\n walletId: approval.custodyWallet.walletId,\n nearAccountId: approval.custodyWallet.nearAccountId,\n network: approval.custodyWallet.network,\n handoffUrl: approval.custodyWallet.handoffUrl,\n }\n : null,\n outlayerCli,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n siteUrl: input.site ?? \"\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n logout: builder.logout.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const configDir = input.configDir ?? deps.session?.root ?? process.cwd();\n\n try {\n const session = readSessionHandle(configDir);\n let revokedApiKey = false;\n let removedPublishKey = false;\n let warning: string | null = null;\n\n if (session?.credential) {\n revokedApiKey = await revokeApiKey(session.credential).catch(() => false);\n if (!revokedApiKey) {\n warning =\n \"Could not revoke the API key remotely — revoke it manually under Settings → API keys.\";\n }\n }\n\n if (session?.publishKey) {\n const account = session.credential?.accountId ?? deps.session?.config?.account ?? \"\";\n removePublishedKeyFile(session.publishKey.network, account, session.publishKey.publicKey);\n removedPublishKey = true;\n warning = warning\n ? `${warning} Re-run ${colors.cyan(\"bos login --key\")} to re-export a publish key.`\n : \"Removed the exported publish key. Re-run bos login --key to re-export one.\";\n }\n\n deleteSessionHandle(configDir);\n return {\n status: \"logged-out\" as const,\n revokedApiKey,\n removedPublishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n revokedApiKey: false,\n removedPublishKey: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n };\n}\n\nfunction resolveLoginSiteUrl(\n siteInput: string | undefined,\n session: ResolutionSession | null,\n staging: boolean,\n): string {\n if (siteInput) {\n return siteInput.replace(/\\/+$/, \"\");\n }\n if (staging) {\n const stagingDomain = session?.config?.staging?.domain ?? session?.config?.domain;\n return stagingDomain ? `https://${stagingDomain}` : (session?.runtime?.ui?.url ?? \"\");\n }\n const devUiUrl = session?.runtime?.ui?.url;\n if (devUiUrl && /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)/.test(devUiUrl)) {\n return devUiUrl.replace(/\\/+$/, \"\");\n }\n const domain = session?.config?.domain;\n return domain ? `https://${domain}` : \"\";\n}\n\nasync function exportPublishKey(\n account: string,\n registry: string | undefined,\n): Promise<NonNullable<LoginResult[\"publishKey\"]>> {\n if (!account) {\n throw new Error(\"the authored config has no account to export a publish key for\");\n }\n\n await Effect.runPromise(ensureNearCli);\n\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, registry);\n await listPublishKeys({ account, contract, network });\n\n const keyPair = generateNearKeyPair();\n await addFunctionCallAccessKey({\n account,\n contract,\n allowance: \"1NEAR\",\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n keyPair,\n });\n\n const { FileKeyStore } = await import(\"near-kit/keys/file\");\n const keyStore = new FileKeyStore(\"~/.near-credentials\", network);\n await keyStore.add(account, parseNearPrivateKey(keyPair.privateKey));\n try {\n const { statSync, chmodSync } = await import(\"node:fs\");\n const { homedir } = await import(\"node:os\");\n const credentialPath = join(\n homedir(),\n \".near-credentials\",\n network === \"mainnet\" ? \"mainnet\" : network,\n `${account}.json`,\n );\n if (statSync(credentialPath).mode & 0o077) {\n chmodSync(credentialPath, 0o600);\n }\n } catch {}\n\n return {\n publicKey: keyPair.publicKey,\n network,\n contract,\n exportedTo: nearCredentialsPath(network, account),\n };\n}\nfunction parseNearPrivateKey(privateKey: string): KeyPair {\n return parseKey(privateKey) as KeyPair;\n}\n\nasync function revokeApiKey(credential: SessionCredential): Promise<boolean> {\n if (!credential.apiKey || !credential.siteUrl) return false;\n try {\n const response = await fetch(`${credential.siteUrl}/api/auth/api-key/delete`, {\n method: \"POST\",\n headers: {\n \"x-api-key\": credential.apiKey,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ keyId: credential.apiKeyId }),\n signal: AbortSignal.timeout(10_000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
package/dist/commands/keys.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { BosDepsTag } from "./shared.mjs";
|
|
|
6
6
|
import { deleteSessionHandle, nearCredentialsPath, readSessionHandle, removePublishedKeyFile, writeSessionHandle } from "../auth-session.mjs";
|
|
7
7
|
import { openInBrowser, startDeviceLogin } from "../auth-login.mjs";
|
|
8
8
|
import { addFunctionCallAccessKey, deleteAccessKeys, ensureNearCli, generateNearKeyPair, listPublishKeys } from "../near-cli.mjs";
|
|
9
|
+
import { loginOutlayerCli } from "../outlayer-login.mjs";
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
import { Context, Effect } from "effect";
|
|
11
12
|
import process from "node:process";
|
|
@@ -90,6 +91,7 @@ function registerKeys(builder) {
|
|
|
90
91
|
siteUrl,
|
|
91
92
|
device: input.device,
|
|
92
93
|
account: account || void 0,
|
|
94
|
+
custody: input.custody ? { network: getNetworkIdForAccount(account) } : void 0,
|
|
93
95
|
expiresIn: input.expiresIn
|
|
94
96
|
});
|
|
95
97
|
console.log();
|
|
@@ -107,7 +109,9 @@ function registerKeys(builder) {
|
|
|
107
109
|
error: "Login approved but no CLI credential was created"
|
|
108
110
|
};
|
|
109
111
|
const expiresAt = new Date(Date.now() + input.expiresIn * 1e3).toISOString();
|
|
110
|
-
|
|
112
|
+
const configDir = deps.session?.root ?? process.cwd();
|
|
113
|
+
const existingSession = readSessionHandle(configDir);
|
|
114
|
+
writeSessionHandle(configDir, {
|
|
111
115
|
version: 1,
|
|
112
116
|
credential: {
|
|
113
117
|
kind: "session",
|
|
@@ -120,16 +124,26 @@ function registerKeys(builder) {
|
|
|
120
124
|
expiresAt
|
|
121
125
|
},
|
|
122
126
|
publishKey: null,
|
|
123
|
-
delegateKey: null
|
|
127
|
+
delegateKey: null,
|
|
128
|
+
custodyWallet: approval.custodyWallet ?? existingSession?.custodyWallet ?? null
|
|
124
129
|
});
|
|
125
130
|
let warning = null;
|
|
126
|
-
if (
|
|
131
|
+
if (input.custody && !approval.custodyWallet) warning = "Custody wallet credential could not be fetched after approval. Re-run `bos login --custody` — the wallet is kept server-side.";
|
|
132
|
+
if (account && approval.accountId && approval.accountId !== account) warning = warning ? `${warning} Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.` : `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;
|
|
127
133
|
let publishKey = null;
|
|
128
134
|
if (input.key) try {
|
|
129
135
|
publishKey = await exportPublishKey(account, input.registry);
|
|
130
136
|
} catch (error) {
|
|
131
137
|
warning = `Session stored, but publish-key export failed: ${error instanceof Error ? error.message : "unknown error"}`;
|
|
132
138
|
}
|
|
139
|
+
let outlayerCli = null;
|
|
140
|
+
if (input.custody && approval.custodyWallet) {
|
|
141
|
+
outlayerCli = await loginOutlayerCli(approval.custodyWallet);
|
|
142
|
+
if (outlayerCli.status === "error") {
|
|
143
|
+
const failure = `OutLayer CLI login failed: ${outlayerCli.detail}`;
|
|
144
|
+
warning = warning ? `${warning} ${failure}` : failure;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
133
147
|
return {
|
|
134
148
|
status: "logged-in",
|
|
135
149
|
siteUrl,
|
|
@@ -137,6 +151,13 @@ function registerKeys(builder) {
|
|
|
137
151
|
expiresAt,
|
|
138
152
|
loginUrl: login.verificationUrl,
|
|
139
153
|
publishKey,
|
|
154
|
+
custodyWallet: approval.custodyWallet ? {
|
|
155
|
+
walletId: approval.custodyWallet.walletId,
|
|
156
|
+
nearAccountId: approval.custodyWallet.nearAccountId,
|
|
157
|
+
network: approval.custodyWallet.network,
|
|
158
|
+
handoffUrl: approval.custodyWallet.handoffUrl
|
|
159
|
+
} : null,
|
|
160
|
+
outlayerCli,
|
|
140
161
|
warning
|
|
141
162
|
};
|
|
142
163
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.mjs","names":[],"sources":["../../src/commands/keys.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport process from \"node:process\";\nimport { Context, Effect } from \"effect\";\nimport { type KeyPair, parseKey } from \"near-kit\";\nimport { openInBrowser, startDeviceLogin } from \"../auth-login\";\nimport {\n deleteSessionHandle,\n nearCredentialsPath,\n readSessionHandle,\n removePublishedKeyFile,\n type SessionCredential,\n writeSessionHandle,\n} from \"../auth-session\";\nimport { MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { LoginResult } from \"../contract\";\nimport { getRegistryNamespaceForAccount } from \"../fastkv\";\nimport {\n addFunctionCallAccessKey,\n deleteAccessKeys,\n ensureNearCli,\n generateNearKeyPair,\n listPublishKeys,\n} from \"../near-cli\";\nimport { getNetworkIdForAccount } from \"../network\";\nimport type { ResolutionSession } from \"../resolution/session\";\nimport { colors } from \"../utils/theme\";\nimport { type BosBuilder, BosDepsTag } from \"./shared\";\n\nconst PUBLISH_FUNCTION_NAMES = [\"__fastdata_kv\"];\n\nexport function registerKeys(builder: BosBuilder) {\n return {\n keyPublish: builder.keyPublish.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n account: \"\",\n network: \"mainnet\" as const,\n env: input.env,\n contract: \"\",\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const account =\n input.env === \"staging\"\n ? (session.config.staging?.account ?? session.config.account)\n : session.config.account;\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, input.registry);\n try {\n await Effect.runPromise(ensureNearCli);\n\n const oldKeys = await listPublishKeys({ account, contract, network });\n\n const keyPair = await addFunctionCallAccessKey({\n account,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n });\n\n if (oldKeys.length > 0) {\n console.log();\n console.log(\n ` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? \"s\" : \"\"}:`,\n );\n for (const k of oldKeys) {\n console.log(` ${colors.dim(k)}`);\n }\n\n if (input.removeOldKeys === true) {\n try {\n await deleteAccessKeys(account, oldKeys, network);\n console.log(\n ` ${colors.green(\"✓\")} Removed ${oldKeys.length} old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n }`,\n );\n } catch {\n console.log(\n ` ${colors.yellow(\"⚠\")} Failed to remove old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n } (new key still active)`,\n );\n }\n } else {\n console.log(` ${colors.dim(\"Old key(s) retained.\")}`);\n }\n }\n\n return {\n status: \"published\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n login: builder.login.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const staging = input.env === \"staging\";\n const account = staging\n ? (deps.session?.config?.staging?.account ?? deps.session?.config?.account ?? \"\")\n : (deps.session?.config?.account ?? \"\");\n\n try {\n const siteUrl = resolveLoginSiteUrl(input.site, deps.session, staging);\n const login = await startDeviceLogin({\n siteUrl,\n device: input.device,\n account: account || undefined,\n expiresIn: input.expiresIn,\n });\n\n console.log();\n console.log(` One-time code: ${colors.cyan(login.userCode)}`);\n await openInBrowser(login.verificationUrl).catch((error: unknown) => {\n console.log(colors.yellow(` ⚠ ${(error as Error).message}`));\n });\n console.log();\n console.log(` Waiting for approval at ${colors.dim(login.verificationUrl)}…`);\n\n const approval = await login.waitForApproval();\n\n if (!approval.apiKey) {\n return {\n status: \"error\" as const,\n siteUrl,\n loginUrl: login.verificationUrl,\n error: \"Login approved but no CLI credential was created\",\n };\n }\n\n const expiresAt = new Date(Date.now() + input.expiresIn * 1000).toISOString();\n writeSessionHandle(deps.session?.root ?? process.cwd(), {\n version: 1,\n credential: {\n kind: \"session\",\n apiKey: approval.apiKey.key,\n apiKeyId: approval.apiKey.id,\n accountId: approval.accountId,\n label: input.device ?? siteUrl,\n siteUrl,\n createdAt: new Date().toISOString(),\n expiresAt,\n },\n publishKey: null,\n delegateKey: null,\n });\n\n let warning: string | null = null;\n if (account && approval.accountId && approval.accountId !== account) {\n warning = `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;\n }\n\n let publishKey: LoginResult[\"publishKey\"] = null;\n if (input.key) {\n try {\n publishKey = await exportPublishKey(account, input.registry);\n } catch (error) {\n warning = `Session stored, but publish-key export failed: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`;\n }\n }\n\n return {\n status: \"logged-in\" as const,\n siteUrl,\n accountId: approval.accountId,\n expiresAt,\n loginUrl: login.verificationUrl,\n publishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n siteUrl: input.site ?? \"\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n logout: builder.logout.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const configDir = input.configDir ?? deps.session?.root ?? process.cwd();\n\n try {\n const session = readSessionHandle(configDir);\n let revokedApiKey = false;\n let removedPublishKey = false;\n let warning: string | null = null;\n\n if (session?.credential) {\n revokedApiKey = await revokeApiKey(session.credential).catch(() => false);\n if (!revokedApiKey) {\n warning =\n \"Could not revoke the API key remotely — revoke it manually under Settings → API keys.\";\n }\n }\n\n if (session?.publishKey) {\n const account = session.credential?.accountId ?? deps.session?.config?.account ?? \"\";\n removePublishedKeyFile(session.publishKey.network, account, session.publishKey.publicKey);\n removedPublishKey = true;\n warning = warning\n ? `${warning} Re-run ${colors.cyan(\"bos login --key\")} to re-export a publish key.`\n : \"Removed the exported publish key. Re-run bos login --key to re-export one.\";\n }\n\n deleteSessionHandle(configDir);\n return {\n status: \"logged-out\" as const,\n revokedApiKey,\n removedPublishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n revokedApiKey: false,\n removedPublishKey: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n };\n}\n\nfunction resolveLoginSiteUrl(\n siteInput: string | undefined,\n session: ResolutionSession | null,\n staging: boolean,\n): string {\n if (siteInput) {\n return siteInput.replace(/\\/+$/, \"\");\n }\n if (staging) {\n const stagingDomain = session?.config?.staging?.domain ?? session?.config?.domain;\n return stagingDomain ? `https://${stagingDomain}` : (session?.runtime?.ui?.url ?? \"\");\n }\n const devUiUrl = session?.runtime?.ui?.url;\n if (devUiUrl && /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)/.test(devUiUrl)) {\n return devUiUrl.replace(/\\/+$/, \"\");\n }\n const domain = session?.config?.domain;\n return domain ? `https://${domain}` : \"\";\n}\n\nasync function exportPublishKey(\n account: string,\n registry: string | undefined,\n): Promise<NonNullable<LoginResult[\"publishKey\"]>> {\n if (!account) {\n throw new Error(\"the authored config has no account to export a publish key for\");\n }\n\n await Effect.runPromise(ensureNearCli);\n\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, registry);\n await listPublishKeys({ account, contract, network });\n\n const keyPair = generateNearKeyPair();\n await addFunctionCallAccessKey({\n account,\n contract,\n allowance: \"1NEAR\",\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n keyPair,\n });\n\n const { FileKeyStore } = await import(\"near-kit/keys/file\");\n const keyStore = new FileKeyStore(\"~/.near-credentials\", network);\n await keyStore.add(account, parseNearPrivateKey(keyPair.privateKey));\n try {\n const { statSync, chmodSync } = await import(\"node:fs\");\n const { homedir } = await import(\"node:os\");\n const credentialPath = join(\n homedir(),\n \".near-credentials\",\n network === \"mainnet\" ? \"mainnet\" : network,\n `${account}.json`,\n );\n if (statSync(credentialPath).mode & 0o077) {\n chmodSync(credentialPath, 0o600);\n }\n } catch {}\n\n return {\n publicKey: keyPair.publicKey,\n network,\n contract,\n exportedTo: nearCredentialsPath(network, account),\n };\n}\nfunction parseNearPrivateKey(privateKey: string): KeyPair {\n return parseKey(privateKey) as KeyPair;\n}\n\nasync function revokeApiKey(credential: SessionCredential): Promise<boolean> {\n if (!credential.apiKey || !credential.siteUrl) return false;\n try {\n const response = await fetch(`${credential.siteUrl}/api/auth/api-key/delete`, {\n method: \"POST\",\n headers: {\n \"x-api-key\": credential.apiKey,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ keyId: credential.apiKeyId }),\n signal: AbortSignal.timeout(10_000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keys.mjs","names":[],"sources":["../../src/commands/keys.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport process from \"node:process\";\nimport { Context, Effect } from \"effect\";\nimport { type KeyPair, parseKey } from \"near-kit\";\nimport { openInBrowser, startDeviceLogin } from \"../auth-login\";\nimport {\n deleteSessionHandle,\n nearCredentialsPath,\n readSessionHandle,\n removePublishedKeyFile,\n type SessionCredential,\n writeSessionHandle,\n} from \"../auth-session\";\nimport { MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { LoginResult } from \"../contract\";\nimport { getRegistryNamespaceForAccount } from \"../fastkv\";\nimport {\n addFunctionCallAccessKey,\n deleteAccessKeys,\n ensureNearCli,\n generateNearKeyPair,\n listPublishKeys,\n} from \"../near-cli\";\nimport { getNetworkIdForAccount } from \"../network\";\nimport { loginOutlayerCli } from \"../outlayer-login\";\nimport type { ResolutionSession } from \"../resolution/session\";\nimport { colors } from \"../utils/theme\";\nimport { type BosBuilder, BosDepsTag } from \"./shared\";\n\nconst PUBLISH_FUNCTION_NAMES = [\"__fastdata_kv\"];\n\nexport function registerKeys(builder: BosBuilder) {\n return {\n keyPublish: builder.keyPublish.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n account: \"\",\n network: \"mainnet\" as const,\n env: input.env,\n contract: \"\",\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const account =\n input.env === \"staging\"\n ? (session.config.staging?.account ?? session.config.account)\n : session.config.account;\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, input.registry);\n try {\n await Effect.runPromise(ensureNearCli);\n\n const oldKeys = await listPublishKeys({ account, contract, network });\n\n const keyPair = await addFunctionCallAccessKey({\n account,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n });\n\n if (oldKeys.length > 0) {\n console.log();\n console.log(\n ` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? \"s\" : \"\"}:`,\n );\n for (const k of oldKeys) {\n console.log(` ${colors.dim(k)}`);\n }\n\n if (input.removeOldKeys === true) {\n try {\n await deleteAccessKeys(account, oldKeys, network);\n console.log(\n ` ${colors.green(\"✓\")} Removed ${oldKeys.length} old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n }`,\n );\n } catch {\n console.log(\n ` ${colors.yellow(\"⚠\")} Failed to remove old key${\n oldKeys.length > 1 ? \"s\" : \"\"\n } (new key still active)`,\n );\n }\n } else {\n console.log(` ${colors.dim(\"Old key(s) retained.\")}`);\n }\n }\n\n return {\n status: \"published\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n account,\n network,\n env: input.env,\n contract,\n allowance: input.allowance,\n functionNames: PUBLISH_FUNCTION_NAMES,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n login: builder.login.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const staging = input.env === \"staging\";\n const account = staging\n ? (deps.session?.config?.staging?.account ?? deps.session?.config?.account ?? \"\")\n : (deps.session?.config?.account ?? \"\");\n\n try {\n const siteUrl = resolveLoginSiteUrl(input.site, deps.session, staging);\n const login = await startDeviceLogin({\n siteUrl,\n device: input.device,\n account: account || undefined,\n custody: input.custody ? { network: getNetworkIdForAccount(account) } : undefined,\n expiresIn: input.expiresIn,\n });\n\n console.log();\n console.log(` One-time code: ${colors.cyan(login.userCode)}`);\n await openInBrowser(login.verificationUrl).catch((error: unknown) => {\n console.log(colors.yellow(` ⚠ ${(error as Error).message}`));\n });\n console.log();\n console.log(` Waiting for approval at ${colors.dim(login.verificationUrl)}…`);\n\n const approval = await login.waitForApproval();\n\n if (!approval.apiKey) {\n return {\n status: \"error\" as const,\n siteUrl,\n loginUrl: login.verificationUrl,\n error: \"Login approved but no CLI credential was created\",\n };\n }\n\n const expiresAt = new Date(Date.now() + input.expiresIn * 1000).toISOString();\n const configDir = deps.session?.root ?? process.cwd();\n const existingSession = readSessionHandle(configDir);\n writeSessionHandle(configDir, {\n version: 1,\n credential: {\n kind: \"session\",\n apiKey: approval.apiKey.key,\n apiKeyId: approval.apiKey.id,\n accountId: approval.accountId,\n label: input.device ?? siteUrl,\n siteUrl,\n createdAt: new Date().toISOString(),\n expiresAt,\n },\n publishKey: null,\n delegateKey: null,\n custodyWallet: approval.custodyWallet ?? existingSession?.custodyWallet ?? null,\n });\n\n let warning: string | null = null;\n if (input.custody && !approval.custodyWallet) {\n warning =\n \"Custody wallet credential could not be fetched after approval. Re-run `bos login --custody` — the wallet is kept server-side.\";\n }\n if (account && approval.accountId && approval.accountId !== account) {\n warning = warning\n ? `${warning} Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`\n : `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;\n }\n\n let publishKey: LoginResult[\"publishKey\"] = null;\n if (input.key) {\n try {\n publishKey = await exportPublishKey(account, input.registry);\n } catch (error) {\n warning = `Session stored, but publish-key export failed: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`;\n }\n }\n\n let outlayerCli: LoginResult[\"outlayerCli\"] = null;\n if (input.custody && approval.custodyWallet) {\n outlayerCli = await loginOutlayerCli(approval.custodyWallet);\n if (outlayerCli.status === \"error\") {\n const failure = `OutLayer CLI login failed: ${outlayerCli.detail}`;\n warning = warning ? `${warning} ${failure}` : failure;\n }\n }\n\n return {\n status: \"logged-in\" as const,\n siteUrl,\n accountId: approval.accountId,\n expiresAt,\n loginUrl: login.verificationUrl,\n publishKey,\n custodyWallet: approval.custodyWallet\n ? {\n walletId: approval.custodyWallet.walletId,\n nearAccountId: approval.custodyWallet.nearAccountId,\n network: approval.custodyWallet.network,\n handoffUrl: approval.custodyWallet.handoffUrl,\n }\n : null,\n outlayerCli,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n siteUrl: input.site ?? \"\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n\n logout: builder.logout.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const configDir = input.configDir ?? deps.session?.root ?? process.cwd();\n\n try {\n const session = readSessionHandle(configDir);\n let revokedApiKey = false;\n let removedPublishKey = false;\n let warning: string | null = null;\n\n if (session?.credential) {\n revokedApiKey = await revokeApiKey(session.credential).catch(() => false);\n if (!revokedApiKey) {\n warning =\n \"Could not revoke the API key remotely — revoke it manually under Settings → API keys.\";\n }\n }\n\n if (session?.publishKey) {\n const account = session.credential?.accountId ?? deps.session?.config?.account ?? \"\";\n removePublishedKeyFile(session.publishKey.network, account, session.publishKey.publicKey);\n removedPublishKey = true;\n warning = warning\n ? `${warning} Re-run ${colors.cyan(\"bos login --key\")} to re-export a publish key.`\n : \"Removed the exported publish key. Re-run bos login --key to re-export one.\";\n }\n\n deleteSessionHandle(configDir);\n return {\n status: \"logged-out\" as const,\n revokedApiKey,\n removedPublishKey,\n warning,\n };\n } catch (error) {\n return {\n status: \"error\" as const,\n revokedApiKey: false,\n removedPublishKey: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }),\n };\n}\n\nfunction resolveLoginSiteUrl(\n siteInput: string | undefined,\n session: ResolutionSession | null,\n staging: boolean,\n): string {\n if (siteInput) {\n return siteInput.replace(/\\/+$/, \"\");\n }\n if (staging) {\n const stagingDomain = session?.config?.staging?.domain ?? session?.config?.domain;\n return stagingDomain ? `https://${stagingDomain}` : (session?.runtime?.ui?.url ?? \"\");\n }\n const devUiUrl = session?.runtime?.ui?.url;\n if (devUiUrl && /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)/.test(devUiUrl)) {\n return devUiUrl.replace(/\\/+$/, \"\");\n }\n const domain = session?.config?.domain;\n return domain ? `https://${domain}` : \"\";\n}\n\nasync function exportPublishKey(\n account: string,\n registry: string | undefined,\n): Promise<NonNullable<LoginResult[\"publishKey\"]>> {\n if (!account) {\n throw new Error(\"the authored config has no account to export a publish key for\");\n }\n\n await Effect.runPromise(ensureNearCli);\n\n const network = getNetworkIdForAccount(account);\n const contract = getRegistryNamespaceForAccount(account, registry);\n await listPublishKeys({ account, contract, network });\n\n const keyPair = generateNearKeyPair();\n await addFunctionCallAccessKey({\n account,\n contract,\n allowance: \"1NEAR\",\n functionNames: PUBLISH_FUNCTION_NAMES,\n network,\n keyPair,\n });\n\n const { FileKeyStore } = await import(\"near-kit/keys/file\");\n const keyStore = new FileKeyStore(\"~/.near-credentials\", network);\n await keyStore.add(account, parseNearPrivateKey(keyPair.privateKey));\n try {\n const { statSync, chmodSync } = await import(\"node:fs\");\n const { homedir } = await import(\"node:os\");\n const credentialPath = join(\n homedir(),\n \".near-credentials\",\n network === \"mainnet\" ? \"mainnet\" : network,\n `${account}.json`,\n );\n if (statSync(credentialPath).mode & 0o077) {\n chmodSync(credentialPath, 0o600);\n }\n } catch {}\n\n return {\n publicKey: keyPair.publicKey,\n network,\n contract,\n exportedTo: nearCredentialsPath(network, account),\n };\n}\nfunction parseNearPrivateKey(privateKey: string): KeyPair {\n return parseKey(privateKey) as KeyPair;\n}\n\nasync function revokeApiKey(credential: SessionCredential): Promise<boolean> {\n if (!credential.apiKey || !credential.siteUrl) return false;\n try {\n const response = await fetch(`${credential.siteUrl}/api/auth/api-key/delete`, {\n method: \"POST\",\n headers: {\n \"x-api-key\": credential.apiKey,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ keyId: credential.apiKeyId }),\n signal: AbortSignal.timeout(10_000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
package/dist/config.d.cts
CHANGED
|
@@ -152,6 +152,7 @@ declare const buildRuntimeConfigEffect: (config: {
|
|
|
152
152
|
repository?: string | undefined;
|
|
153
153
|
publish?: {
|
|
154
154
|
auth?: "custody" | "key" | "session" | undefined;
|
|
155
|
+
sourcePublish?: boolean | undefined;
|
|
155
156
|
} | undefined;
|
|
156
157
|
ci?: {
|
|
157
158
|
image?: string | undefined;
|
package/dist/config.d.mts
CHANGED
|
@@ -152,6 +152,7 @@ declare const buildRuntimeConfigEffect: (config: {
|
|
|
152
152
|
repository?: string | undefined;
|
|
153
153
|
publish?: {
|
|
154
154
|
auth?: "custody" | "key" | "session" | undefined;
|
|
155
|
+
sourcePublish?: boolean | undefined;
|
|
155
156
|
} | undefined;
|
|
156
157
|
ci?: {
|
|
157
158
|
image?: string | undefined;
|
package/dist/contract.cjs
CHANGED
|
@@ -297,6 +297,7 @@ const OverrideSectionSchema = zod.enum([
|
|
|
297
297
|
const DEFAULT_LOGIN_EXPIRES_IN_SECONDS = 7776e3;
|
|
298
298
|
const LoginOptionsSchema = zod.object({
|
|
299
299
|
key: zod.boolean().default(false),
|
|
300
|
+
custody: zod.boolean().default(false),
|
|
300
301
|
site: zod.string().optional(),
|
|
301
302
|
expiresIn: zod.number().int().positive().default(DEFAULT_LOGIN_EXPIRES_IN_SECONDS),
|
|
302
303
|
device: zod.string().optional(),
|
|
@@ -315,6 +316,21 @@ const LoginResultSchema = zod.object({
|
|
|
315
316
|
contract: zod.string(),
|
|
316
317
|
exportedTo: zod.string()
|
|
317
318
|
}).nullish(),
|
|
319
|
+
custodyWallet: zod.object({
|
|
320
|
+
walletId: zod.string(),
|
|
321
|
+
nearAccountId: zod.string(),
|
|
322
|
+
network: zod.enum(["mainnet", "testnet"]),
|
|
323
|
+
handoffUrl: zod.string().nullish()
|
|
324
|
+
}).nullish(),
|
|
325
|
+
outlayerCli: zod.object({
|
|
326
|
+
status: zod.enum([
|
|
327
|
+
"linked",
|
|
328
|
+
"cli-missing",
|
|
329
|
+
"error"
|
|
330
|
+
]),
|
|
331
|
+
network: zod.enum(["mainnet", "testnet"]),
|
|
332
|
+
detail: zod.string().nullish()
|
|
333
|
+
}).nullish(),
|
|
318
334
|
warning: zod.string().nullish(),
|
|
319
335
|
error: zod.string().optional()
|
|
320
336
|
});
|
|
@@ -382,6 +398,68 @@ const SyncResultSchema = zod.object({
|
|
|
382
398
|
backupDir: zod.string().optional(),
|
|
383
399
|
error: zod.string().optional()
|
|
384
400
|
});
|
|
401
|
+
const ImportOptionsSchema = zod.object({
|
|
402
|
+
workspace: zod.string(),
|
|
403
|
+
/** omitted → resolve the upstream from the authored config's extends chain */
|
|
404
|
+
from: zod.string().optional(),
|
|
405
|
+
yes: zod.boolean().default(false),
|
|
406
|
+
dryRun: zod.boolean().default(false),
|
|
407
|
+
noInstall: zod.boolean().default(false),
|
|
408
|
+
json: zod.boolean().default(false)
|
|
409
|
+
});
|
|
410
|
+
const ImportRouteDeltaSchema = zod.object({
|
|
411
|
+
oursOnly: zod.array(zod.string()),
|
|
412
|
+
theirsOnly: zod.array(zod.string())
|
|
413
|
+
});
|
|
414
|
+
const ImportMigrationCollisionSchema = zod.object({
|
|
415
|
+
kind: zod.literal("tag-mismatch"),
|
|
416
|
+
idx: zod.number(),
|
|
417
|
+
localTag: zod.string().optional(),
|
|
418
|
+
upstreamTag: zod.string().optional(),
|
|
419
|
+
message: zod.string()
|
|
420
|
+
});
|
|
421
|
+
const ImportMigrationAppendSchema = zod.object({
|
|
422
|
+
idx: zod.number(),
|
|
423
|
+
fromTag: zod.string(),
|
|
424
|
+
toTag: zod.string()
|
|
425
|
+
});
|
|
426
|
+
const ImportMigrationReconciliationSchema = zod.object({
|
|
427
|
+
/** local journal tags kept verbatim (already applied, never renumbered) */
|
|
428
|
+
kept: zod.array(zod.string()),
|
|
429
|
+
/** upstream migrations renumbered and appended after the local tail */
|
|
430
|
+
appended: zod.array(ImportMigrationAppendSchema),
|
|
431
|
+
/** upstream migrations matched to local entries by SQL content hash */
|
|
432
|
+
contentMatched: zod.array(zod.string())
|
|
433
|
+
});
|
|
434
|
+
const ImportResultSchema = zod.object({
|
|
435
|
+
status: zod.enum([
|
|
436
|
+
"imported",
|
|
437
|
+
"dry-run",
|
|
438
|
+
"conflicts",
|
|
439
|
+
"error"
|
|
440
|
+
]),
|
|
441
|
+
workspace: zod.string(),
|
|
442
|
+
from: zod.string(),
|
|
443
|
+
ref: zod.string(),
|
|
444
|
+
resolvedSha: zod.string().optional(),
|
|
445
|
+
/** the dereferenced upstream repository when the source was chain-resolved */
|
|
446
|
+
sourceRepository: zod.string().optional(),
|
|
447
|
+
/** resolution notes (e.g. HEAD fallback when no git sha is pinned) */
|
|
448
|
+
warnings: zod.array(zod.string()).default([]),
|
|
449
|
+
added: zod.array(zod.string()),
|
|
450
|
+
updated: zod.array(zod.string()),
|
|
451
|
+
unchanged: zod.array(zod.string()).default([]),
|
|
452
|
+
preservedLocal: zod.array(zod.string()).default([]),
|
|
453
|
+
merged: zod.array(zod.string()).default([]),
|
|
454
|
+
conflicts: zod.array(zod.string()).default([]),
|
|
455
|
+
heldBackMigrations: zod.array(zod.string()).default([]),
|
|
456
|
+
routeDelta: ImportRouteDeltaSchema.optional(),
|
|
457
|
+
migrationCollisions: zod.array(ImportMigrationCollisionSchema).default([]),
|
|
458
|
+
migrationReconciliation: ImportMigrationReconciliationSchema.optional(),
|
|
459
|
+
backupDir: zod.string().optional(),
|
|
460
|
+
reportPath: zod.string().optional(),
|
|
461
|
+
error: zod.string().optional()
|
|
462
|
+
});
|
|
385
463
|
const UpgradeOptionsSchema = zod.object({
|
|
386
464
|
dryRun: zod.boolean().default(false),
|
|
387
465
|
noInstall: zod.boolean().default(false),
|
|
@@ -620,6 +698,7 @@ const commandOptionSchemas = {
|
|
|
620
698
|
logout: LogoutOptionsSchema,
|
|
621
699
|
init: InitOptionsSchema,
|
|
622
700
|
sync: SyncOptionsSchema,
|
|
701
|
+
import: ImportOptionsSchema,
|
|
623
702
|
upgrade: UpgradeOptionsSchema,
|
|
624
703
|
typecheck: TypecheckOptionsSchema,
|
|
625
704
|
mfCheck: MfCheckOptionsSchema,
|
|
@@ -703,6 +782,10 @@ const bosContract = _orpc_contract.oc.router({
|
|
|
703
782
|
method: "POST",
|
|
704
783
|
path: "/sync"
|
|
705
784
|
}).input(SyncOptionsSchema).output(SyncResultSchema),
|
|
785
|
+
import: _orpc_contract.oc.route({
|
|
786
|
+
method: "POST",
|
|
787
|
+
path: "/import"
|
|
788
|
+
}).input(ImportOptionsSchema).output(ImportResultSchema),
|
|
706
789
|
upgrade: _orpc_contract.oc.route({
|
|
707
790
|
method: "POST",
|
|
708
791
|
path: "/upgrade"
|
|
@@ -770,6 +853,12 @@ exports.DeployResultSchema = DeployResultSchema;
|
|
|
770
853
|
exports.DeployedVersionStatusSchema = DeployedVersionStatusSchema;
|
|
771
854
|
exports.DevOptionsSchema = DevOptionsSchema;
|
|
772
855
|
exports.DevResultSchema = DevResultSchema;
|
|
856
|
+
exports.ImportMigrationAppendSchema = ImportMigrationAppendSchema;
|
|
857
|
+
exports.ImportMigrationCollisionSchema = ImportMigrationCollisionSchema;
|
|
858
|
+
exports.ImportMigrationReconciliationSchema = ImportMigrationReconciliationSchema;
|
|
859
|
+
exports.ImportOptionsSchema = ImportOptionsSchema;
|
|
860
|
+
exports.ImportResultSchema = ImportResultSchema;
|
|
861
|
+
exports.ImportRouteDeltaSchema = ImportRouteDeltaSchema;
|
|
773
862
|
exports.InfraExportOptionsSchema = InfraExportOptionsSchema;
|
|
774
863
|
exports.InfraExportResultSchema = InfraExportResultSchema;
|
|
775
864
|
exports.InfraExportServiceSchema = InfraExportServiceSchema;
|