everything-dev 2.0.0-rc.7 → 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.
Files changed (142) hide show
  1. package/dist/api/auth-middleware.cjs +14 -1
  2. package/dist/api/auth-middleware.cjs.map +1 -1
  3. package/dist/api/auth-middleware.d.cts +9 -0
  4. package/dist/api/auth-middleware.d.mts +9 -0
  5. package/dist/api/auth-middleware.mjs +14 -1
  6. package/dist/api/auth-middleware.mjs.map +1 -1
  7. package/dist/apps/upload/src/client.cjs +7 -1
  8. package/dist/apps/upload/src/client.cjs.map +1 -1
  9. package/dist/apps/upload/src/client.mjs +7 -1
  10. package/dist/apps/upload/src/client.mjs.map +1 -1
  11. package/dist/auth-login.cjs +24 -1
  12. package/dist/auth-login.cjs.map +1 -1
  13. package/dist/auth-login.mjs +24 -1
  14. package/dist/auth-login.mjs.map +1 -1
  15. package/dist/auth-session.cjs +4 -2
  16. package/dist/auth-session.cjs.map +1 -1
  17. package/dist/auth-session.mjs +4 -2
  18. package/dist/auth-session.mjs.map +1 -1
  19. package/dist/cdn-deploy.cjs +11 -7
  20. package/dist/cdn-deploy.cjs.map +1 -1
  21. package/dist/cdn-deploy.mjs +11 -7
  22. package/dist/cdn-deploy.mjs.map +1 -1
  23. package/dist/cli/import.cjs +1140 -0
  24. package/dist/cli/import.cjs.map +1 -0
  25. package/dist/cli/import.mjs +1126 -0
  26. package/dist/cli/import.mjs.map +1 -0
  27. package/dist/cli/init.cjs +33 -14
  28. package/dist/cli/init.cjs.map +1 -1
  29. package/dist/cli/init.d.cts +8 -2
  30. package/dist/cli/init.d.mts +8 -2
  31. package/dist/cli/init.mjs +33 -15
  32. package/dist/cli/init.mjs.map +1 -1
  33. package/dist/cli.cjs +59 -3
  34. package/dist/cli.cjs.map +1 -1
  35. package/dist/cli.mjs +59 -3
  36. package/dist/cli.mjs.map +1 -1
  37. package/dist/commands/import.cjs +49 -0
  38. package/dist/commands/import.cjs.map +1 -0
  39. package/dist/commands/import.mjs +46 -0
  40. package/dist/commands/import.mjs.map +1 -0
  41. package/dist/commands/keys.cjs +25 -4
  42. package/dist/commands/keys.cjs.map +1 -1
  43. package/dist/commands/keys.mjs +25 -4
  44. package/dist/commands/keys.mjs.map +1 -1
  45. package/dist/config.d.cts +1 -0
  46. package/dist/config.d.mts +1 -0
  47. package/dist/contract.cjs +89 -0
  48. package/dist/contract.cjs.map +1 -1
  49. package/dist/contract.d.cts +190 -1
  50. package/dist/contract.d.mts +190 -1
  51. package/dist/contract.meta.cjs +17 -1
  52. package/dist/contract.meta.cjs.map +1 -1
  53. package/dist/contract.meta.d.cts +27 -1
  54. package/dist/contract.meta.d.mts +27 -1
  55. package/dist/contract.meta.mjs +17 -1
  56. package/dist/contract.meta.mjs.map +1 -1
  57. package/dist/contract.mjs +84 -1
  58. package/dist/contract.mjs.map +1 -1
  59. package/dist/custody-signer.cjs +70 -0
  60. package/dist/custody-signer.cjs.map +1 -0
  61. package/dist/custody-signer.mjs +68 -0
  62. package/dist/custody-signer.mjs.map +1 -0
  63. package/dist/db/core.cjs +22 -4
  64. package/dist/db/core.cjs.map +1 -1
  65. package/dist/db/core.mjs +22 -4
  66. package/dist/db/core.mjs.map +1 -1
  67. package/dist/descriptor/schema.cjs +8 -5
  68. package/dist/descriptor/schema.cjs.map +1 -1
  69. package/dist/descriptor/schema.d.cts +1 -0
  70. package/dist/descriptor/schema.d.mts +1 -0
  71. package/dist/descriptor/schema.mjs +8 -5
  72. package/dist/descriptor/schema.mjs.map +1 -1
  73. package/dist/descriptor/serialize.cjs +1 -1
  74. package/dist/descriptor/serialize.cjs.map +1 -1
  75. package/dist/descriptor/serialize.mjs +1 -1
  76. package/dist/descriptor/serialize.mjs.map +1 -1
  77. package/dist/http-client.cjs +14 -4
  78. package/dist/http-client.cjs.map +1 -1
  79. package/dist/http-client.mjs +14 -4
  80. package/dist/http-client.mjs.map +1 -1
  81. package/dist/index.cjs +6 -0
  82. package/dist/index.d.cts +2 -2
  83. package/dist/index.d.mts +2 -2
  84. package/dist/index.mjs +2 -2
  85. package/dist/infra/planner.cjs +6 -2
  86. package/dist/infra/planner.cjs.map +1 -1
  87. package/dist/infra/planner.mjs +6 -2
  88. package/dist/infra/planner.mjs.map +1 -1
  89. package/dist/outlayer-login.cjs +54 -0
  90. package/dist/outlayer-login.cjs.map +1 -0
  91. package/dist/outlayer-login.mjs +54 -0
  92. package/dist/outlayer-login.mjs.map +1 -0
  93. package/dist/plugin.cjs +2 -0
  94. package/dist/plugin.cjs.map +1 -1
  95. package/dist/plugin.d.cts +74 -0
  96. package/dist/plugin.d.mts +74 -0
  97. package/dist/plugin.mjs +2 -0
  98. package/dist/plugin.mjs.map +1 -1
  99. package/dist/publish.cjs +145 -22
  100. package/dist/publish.cjs.map +1 -1
  101. package/dist/publish.mjs +147 -24
  102. package/dist/publish.mjs.map +1 -1
  103. package/dist/source-archive.cjs +51 -0
  104. package/dist/source-archive.cjs.map +1 -0
  105. package/dist/source-archive.mjs +50 -0
  106. package/dist/source-archive.mjs.map +1 -0
  107. package/dist/storage-upload.cjs +6 -1
  108. package/dist/storage-upload.cjs.map +1 -1
  109. package/dist/storage-upload.mjs +6 -1
  110. package/dist/storage-upload.mjs.map +1 -1
  111. package/dist/types.cjs +5 -1
  112. package/dist/types.cjs.map +1 -1
  113. package/dist/types.d.cts +2 -0
  114. package/dist/types.d.mts +2 -0
  115. package/dist/types.mjs +5 -1
  116. package/dist/types.mjs.map +1 -1
  117. package/dist/ui/auth.d.cts +217 -123
  118. package/dist/ui/auth.d.mts +217 -123
  119. package/dist/ui/router-client.cjs +2 -1
  120. package/dist/ui/router-client.cjs.map +1 -1
  121. package/dist/ui/router-client.mjs +2 -1
  122. package/dist/ui/router-client.mjs.map +1 -1
  123. package/dist/utils/workspace-subtree.cjs +37 -0
  124. package/dist/utils/workspace-subtree.cjs.map +1 -0
  125. package/dist/utils/workspace-subtree.mjs +36 -0
  126. package/dist/utils/workspace-subtree.mjs.map +1 -0
  127. package/dist/version-manifest-deploy.cjs +4 -2
  128. package/dist/version-manifest-deploy.cjs.map +1 -1
  129. package/dist/version-manifest-deploy.mjs +4 -2
  130. package/dist/version-manifest-deploy.mjs.map +1 -1
  131. package/dist/version-manifest-resolve.cjs +2 -1
  132. package/dist/version-manifest-resolve.cjs.map +1 -1
  133. package/dist/version-manifest-resolve.d.cts +3 -0
  134. package/dist/version-manifest-resolve.d.mts +3 -0
  135. package/dist/version-manifest-resolve.mjs +2 -1
  136. package/dist/version-manifest-resolve.mjs.map +1 -1
  137. package/package.json +11 -10
  138. package/skills/api-and-auth/SKILL.md +12 -9
  139. package/skills/api-and-auth/references/middleware.md +38 -0
  140. package/skills/cli-reference/SKILL.md +38 -6
  141. package/skills/init-upgrade/SKILL.md +76 -3
  142. package/skills/talk-to-the-app/SKILL.md +32 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.cjs","names":["findConfigPath","MISSING_CONFIG_MESSAGE","dirname","process","p","importWorkspace"],"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,aAAaA,8BAAe;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,GAAGC,sCAAuB;EACnC;EAGF,MAAM,UAAU;EAChB,MAAM,iBAAaC,mBAAQ,UAAU;EAIrC,MAAM,UAFJ,CAAC,QAAQ,OAAO,CAAC,QAAQ,UAAUC,qBAAQ,MAAM,SAASA,qBAAQ,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,MAAMC,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,IAAIA,eAAE,SAAS,MAAM,GAAG,OAAO;GAC/B,OAAO,WAAW;EACpB,IACA;EAEJ,OAAOC,+BAAgB,YAAY,SAAS,EAAE,QAAQ,CAAC;CACzD,CAAC,EACH;AACF"}
@@ -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"}
@@ -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");
@@ -50,7 +51,7 @@ function registerKeys(builder) {
50
51
  console.log();
51
52
  console.log(` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? "s" : ""}:`);
52
53
  for (const k of oldKeys) console.log(` ${require_theme.colors.dim(k)}`);
53
- if (input.removeOldKeys !== false) try {
54
+ if (input.removeOldKeys === true) try {
54
55
  await require_near_cli.deleteAccessKeys(account, oldKeys, network);
55
56
  console.log(` ${require_theme.colors.green("✓")} Removed ${oldKeys.length} old key${oldKeys.length > 1 ? "s" : ""}`);
56
57
  } catch {
@@ -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
- require_auth_session.writeSessionHandle(deps.session?.root ?? node_process.default.cwd(), {
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 (account && approval.accountId && approval.accountId !== account) warning = `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;
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 !== false) {\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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"}
@@ -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";
@@ -48,7 +49,7 @@ function registerKeys(builder) {
48
49
  console.log();
49
50
  console.log(` Found ${oldKeys.length} existing publish key${oldKeys.length > 1 ? "s" : ""}:`);
50
51
  for (const k of oldKeys) console.log(` ${colors.dim(k)}`);
51
- if (input.removeOldKeys !== false) try {
52
+ if (input.removeOldKeys === true) try {
52
53
  await deleteAccessKeys(account, oldKeys, network);
53
54
  console.log(` ${colors.green("✓")} Removed ${oldKeys.length} old key${oldKeys.length > 1 ? "s" : ""}`);
54
55
  } catch {
@@ -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
- writeSessionHandle(deps.session?.root ?? process.cwd(), {
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 (account && approval.accountId && approval.accountId !== account) warning = `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`;
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 !== false) {\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;