@helios-lang/contract-utils 0.3.23 → 0.3.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,16 @@ Credentials are stored in a versioned `debugger.json` file:
26
26
 
27
27
  Set `HELIOS_CONFIG_HOME` to override the directory. Unix directories/files use modes 700/600. Windows uses a current-user-only ACL. The JSON has `{version:1,selected,profiles}`, where `selected` is a project ID and `profiles` maps project IDs to `{id,name,created_at,apiKey,wallet,endpoint}`. Names need not be unique. Re-running login refreshes that wallet's snapshot and removes revoked projects while preserving other wallets. It keeps the selected project when available and otherwise selects the oldest returned project. No signatures, browser session cookies, or temporary login tokens are persisted.
28
28
 
29
- API keys in this file are secrets: do not commit or share it. They authorize both capture uploads and reads for their project. Login never prints key values. The console's ordinary metadata endpoints do not return them; key recovery uses an explicitly approved, short-lived CLI request.
29
+ Project API keys can be shared with collaborators. They authorize capture uploads and reads for their project. The project page in the Console always displays its key in a yellow box.
30
+
31
+ To import a key from another project owner without wallet login:
32
+
33
+ ```sh
34
+ helios import-key hdbg_<key-from-project-page>
35
+ helios compile --project "Shared project" --help
36
+ ```
37
+
38
+ Import validates the key, resolves the project name automatically, and merges it into the same configuration used by compilation and VS Code. Reimporting updates that project without duplicating it or removing other projects. Wallet login preserves imported projects belonging to other wallets.
30
39
 
31
40
  For development, run `pnpm test`. `node scripts/check-global-cli.mjs` packs the package, installs it into an isolated npm global prefix, and checks executable discovery and equivalent compilation. The CLI portability workflow runs these checks on Windows, macOS, and Linux with Node 22 and 24. Publish the package only after the website and Worker with CLI-login support have been deployed.
32
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helios-lang/contract-utils",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "description": "Convenience and type-safety utilities for using Helios validators from within Typescript",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -102,11 +102,47 @@ export async function installProjects(
102
102
  endpoint,
103
103
  path = configPath()
104
104
  ) {
105
+ if (!/^[a-f0-9]{56}$/.test(wallet))
106
+ throw new Error("Invalid wallet response")
107
+ validateProjects(projects)
108
+ const data = await readConfig(path)
109
+ for (const [id, profile] of Object.entries(data.profiles))
110
+ if (profile.wallet === wallet && profile.endpoint === endpoint)
111
+ delete data.profiles[id]
112
+ for (const project of projects)
113
+ data.profiles[project.id] = { ...project, wallet, endpoint }
114
+ if (!data.profiles[data.selected])
115
+ data.selected = [...projects].sort(
116
+ (a, b) => a.created_at - b.created_at || a.id.localeCompare(b.id)
117
+ )[0].id
118
+ return writeConfig(data, path)
119
+ }
120
+ export async function installSharedProject(
121
+ project,
122
+ endpoint,
123
+ path = configPath()
124
+ ) {
125
+ validateProjects([project])
126
+ const data = await readConfig(path)
127
+ const previous = data.profiles[project.id]
128
+ if (previous && previous.endpoint !== endpoint)
129
+ throw new Error(
130
+ "Project ID already exists for another debugger service"
131
+ )
105
132
  if (
106
- !/^[a-f0-9]{56}$/.test(wallet) ||
107
- !Array.isArray(projects) ||
108
- !projects.length
133
+ previous &&
134
+ previous.apiKey === project.apiKey &&
135
+ previous.name === project.name &&
136
+ previous.created_at === project.created_at
109
137
  )
138
+ return { path, alreadyExists: true }
139
+ data.profiles[project.id] = { ...previous, ...project, endpoint }
140
+ if (!data.profiles[data.selected]) data.selected = project.id
141
+ await writeConfig(data, path)
142
+ return { path, alreadyExists: false }
143
+ }
144
+ function validateProjects(projects) {
145
+ if (!Array.isArray(projects) || !projects.length)
110
146
  throw new Error("Invalid or empty project response")
111
147
  const ids = new Set()
112
148
  for (const p of projects) {
@@ -122,16 +158,8 @@ export async function installProjects(
122
158
  throw new Error("Invalid project response")
123
159
  ids.add(p.id)
124
160
  }
125
- const data = await readConfig(path)
126
- for (const [id, profile] of Object.entries(data.profiles))
127
- if (profile.wallet === wallet && profile.endpoint === endpoint)
128
- delete data.profiles[id]
129
- for (const project of projects)
130
- data.profiles[project.id] = { ...project, wallet, endpoint }
131
- if (!data.profiles[data.selected])
132
- data.selected = [...projects].sort(
133
- (a, b) => a.created_at - b.created_at || a.id.localeCompare(b.id)
134
- )[0].id
161
+ }
162
+ async function writeConfig(data, path) {
135
163
  const dir = dirname(path)
136
164
  const existed = await inspect(dir, true)
137
165
  await mkdir(dir, { recursive: true, mode: 0o700 })
@@ -0,0 +1,56 @@
1
+ import { installSharedProject } from "./config.mjs"
2
+
3
+ export async function importKey(
4
+ apiKey,
5
+ {
6
+ endpoint = "https://debugger.helios-lang.io",
7
+ fetchImpl = fetch,
8
+ save = installSharedProject,
9
+ log = console.log
10
+ } = {}
11
+ ) {
12
+ if (typeof apiKey !== "string" || !/^hdbg_[a-f0-9]{64}$/.test(apiKey))
13
+ throw new Error(
14
+ "Invalid debugger API key. Copy the full key from the project's Console page."
15
+ )
16
+ const origin = new URL(endpoint)
17
+ if (
18
+ origin.protocol !== "https:" ||
19
+ origin.username ||
20
+ origin.password ||
21
+ origin.pathname !== "/" ||
22
+ origin.search ||
23
+ origin.hash
24
+ )
25
+ throw new Error("Debugger endpoint must be an HTTPS origin")
26
+ let response
27
+ try {
28
+ response = await fetchImpl(origin.origin + "/v1/project", {
29
+ headers: { Authorization: `Bearer ${apiKey}` },
30
+ redirect: "error",
31
+ signal: AbortSignal.timeout(10000)
32
+ })
33
+ } catch {
34
+ throw new Error(
35
+ "Could not reach the debugger service. Check your connection and try again."
36
+ )
37
+ }
38
+ if (response.status === 401)
39
+ throw new Error(
40
+ "This debugger API key is invalid or revoked. Ask the project owner for an active key."
41
+ )
42
+ if (!response.ok)
43
+ throw new Error(
44
+ `Debugger project lookup failed (HTTP ${response.status}). Try again later.`
45
+ )
46
+ const { id, name, created_at } = await response.json()
47
+ const { path, alreadyExists } = await save(
48
+ { id, name, created_at, apiKey },
49
+ origin.origin
50
+ )
51
+ if (alreadyExists) log("Key already exists")
52
+ else
53
+ log(`Imported project ${JSON.stringify(name)} into ${path}.
54
+ Compile with helios compile --project ${JSON.stringify(name)} (or hl2ts --project ${JSON.stringify(name)}).`)
55
+ return { id, name, created_at }
56
+ }
package/src/helios.mjs CHANGED
@@ -10,6 +10,11 @@ try {
10
10
  } else if (command === "wallet") {
11
11
  const { walletCommand } = await import("./cli/wallet.mjs")
12
12
  await walletCommand(args)
13
+ } else if (command === "import-key") {
14
+ if (args.length !== 1)
15
+ throw new Error("Usage: helios import-key <api-key>")
16
+ const { importKey } = await import("./cli/import-key.mjs")
17
+ await importKey(args[0])
13
18
  } else if (command === "login") {
14
19
  if (args.some((arg) => arg !== "--no-browser"))
15
20
  throw new Error("Usage: helios login [--no-browser]")
@@ -41,7 +46,7 @@ try {
41
46
  )
42
47
  } else if (!command || ["--help", "-h", "help"].includes(command)) {
43
48
  console.log(
44
- "Helios CLI\n\n helios compile [options] Compile contracts (same options as hl2ts)\n helios login [--no-browser] Install project API keys through the Console\n helios wallet create [--network preprod] [--out wallet.json] Generate a wallet offline\n helios --version"
49
+ "Helios CLI\n\n helios compile [options] Compile contracts (same options as hl2ts)\n helios login [--no-browser] Install project API keys through the Console\n helios import-key <api-key> Install a shared project API key\n helios wallet create [--network preprod] [--out wallet.json] Generate a wallet offline\n helios --version"
45
50
  )
46
51
  } else throw new Error(`Unknown command: ${command}. Run helios --help.`)
47
52
  } catch (error) {
@@ -1,5 +1,9 @@
1
1
  export function windowsAcl(path: any, directory: any, set?: boolean): Promise<void>;
2
2
  export function configPath(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv, home?: string): string;
3
3
  export function readConfig(path?: string): Promise<any>;
4
- export function installProjects(wallet: any, projects: any, endpoint: any, path?: string): Promise<string>;
4
+ export function installProjects(wallet: any, projects: any, endpoint: any, path?: string): Promise<any>;
5
+ export function installSharedProject(project: any, endpoint: any, path?: string): Promise<{
6
+ path: string;
7
+ alreadyExists: boolean;
8
+ }>;
5
9
  //# sourceMappingURL=config.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","sourceRoot":"","sources":["../../src/cli/config.mjs"],"names":[],"mappings":"AAeA,oFAiCC;AACD,uGAcC;AAqBD,wDAaC;AACD,2GA2DC"}
1
+ {"version":3,"file":"config.d.mts","sourceRoot":"","sources":["../../src/cli/config.mjs"],"names":[],"mappings":"AAeA,oFAiCC;AACD,uGAcC;AAqBD,wDAaC;AACD,wGAoBC;AACD;;;GAuBC"}