@amaster.ai/runtime-cli 1.1.27 → 1.1.29

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
@@ -50,10 +50,34 @@ Apps that expose anonymous endpoints do not require `amaster login` first. When
50
50
  # Initialize a new app
51
51
  amaster init --app-code myapp --url https://myapp.helige.cn
52
52
 
53
+ # Initialize by registry (auto infers https://<app-code>.<registry-host>)
54
+ amaster init --app-code myapp --registry helige
55
+ amaster init --app-code myapp --registry helige-int
56
+
53
57
  # With API key for OSS access
54
58
  amaster init --app-code myapp --url https://myapp.helige.cn --api-key YOUR_API_KEY
55
59
  ```
56
60
 
61
+ ### Registry Selection
62
+
63
+ The CLI supports multiple platform registries. Built-in registries:
64
+
65
+ - `helige` -> `https://helige.cn`
66
+ - `helige-int` -> `https://helige-int.cn`
67
+
68
+ You can switch the default registry or pass a custom registry URL:
69
+
70
+ ```bash
71
+ # List known registries
72
+ amaster registry list
73
+
74
+ # Use a built-in registry
75
+ amaster registry use helige-int
76
+
77
+ # Use a custom registry URL
78
+ amaster registry use https://staging.example.com
79
+ ```
80
+
57
81
  ### 2. Set Default App (Optional)
58
82
 
59
83
  ```bash
@@ -216,11 +240,19 @@ amaster use <app-code> # Set the default app
216
240
  ```bash
217
241
  amaster init # Initialize an app for OpenClaw
218
242
  --app-code <code> # Required: Application code (e.g., bleulig7o)
219
- --url <url> # Required: Base URL (e.g., https://app.helige.cn)
243
+ --url <url> # Optional: Explicit base URL
244
+ --registry <registry> # Optional: Registry name or URL (default: current registry)
220
245
  --api-key <key> # Optional: API Key for OSS access
221
246
  --oss-endpoint <endpoint> # Optional: OSS endpoint
222
247
  ```
223
248
 
249
+ ### Registry Management
250
+
251
+ ```bash
252
+ amaster registry list # List built-in and custom registries
253
+ amaster registry use <registry> # Set the default registry by name or URL
254
+ ```
255
+
224
256
  ### Authentication
225
257
 
226
258
  ```bash
package/dist/cli.cjs CHANGED
@@ -34,18 +34,27 @@ var config_exports = {};
34
34
  __export(config_exports, {
35
35
  addApp: () => addApp,
36
36
  clearAuthSession: () => clearAuthSession,
37
+ ensureRegistry: () => ensureRegistry,
37
38
  getAccessToken: () => getAccessToken,
38
39
  getAppConfig: () => getAppConfig,
39
40
  getAuthSession: () => getAuthSession,
40
41
  getBaseURL: () => getBaseURL,
41
42
  getConfig: () => getConfig,
42
43
  getCurrentApp: () => getCurrentApp,
44
+ getCurrentRegistry: () => getCurrentRegistry,
45
+ getRegistryConfig: () => getRegistryConfig,
46
+ inferRegistryBaseURLFromAppBaseURL: () => inferRegistryBaseURLFromAppBaseURL,
43
47
  isAuthenticated: () => isAuthenticated,
44
48
  listApps: () => listApps,
49
+ listRegistries: () => listRegistries,
50
+ normalizeRegistryBaseURL: () => normalizeRegistryBaseURL,
45
51
  removeApp: () => removeApp,
52
+ resolveAppBaseURL: () => resolveAppBaseURL,
53
+ resolveRegistry: () => resolveRegistry,
46
54
  saveAuthSession: () => saveAuthSession,
47
55
  saveConfig: () => saveConfig,
48
56
  setCurrentApp: () => setCurrentApp,
57
+ setCurrentRegistry: () => setCurrentRegistry,
49
58
  shouldRefreshToken: () => shouldRefreshToken
50
59
  });
51
60
  function getConfig() {
@@ -108,6 +117,137 @@ function listApps() {
108
117
  const config = getConfig();
109
118
  return Object.keys(config.apps);
110
119
  }
120
+ function listRegistries() {
121
+ const config = getConfig();
122
+ const currentRegistry = config.currentRegistry || DEFAULT_REGISTRY_NAME;
123
+ const registryMap = /* @__PURE__ */ new Map();
124
+ for (const [name, baseURL] of Object.entries(BUILTIN_REGISTRY_DEFINITIONS)) {
125
+ registryMap.set(name, {
126
+ name,
127
+ baseURL,
128
+ builtin: true
129
+ });
130
+ }
131
+ for (const [name, registry] of Object.entries(config.registries || {})) {
132
+ registryMap.set(name, {
133
+ name,
134
+ baseURL: registry.baseURL,
135
+ builtin: false
136
+ });
137
+ }
138
+ const registries = Array.from(registryMap.values()).sort((left, right) => left.name.localeCompare(right.name));
139
+ const currentResolved = resolveRegistry(currentRegistry);
140
+ return registries.map((registry) => ({
141
+ ...registry,
142
+ name: registry.name === currentResolved.name ? registry.name : registry.name
143
+ }));
144
+ }
145
+ function getCurrentRegistry() {
146
+ const config = getConfig();
147
+ return config.currentRegistry || DEFAULT_REGISTRY_NAME;
148
+ }
149
+ function setCurrentRegistry(registryRef) {
150
+ const resolved = ensureRegistry(registryRef);
151
+ const config = getConfig();
152
+ config.currentRegistry = resolved.name;
153
+ saveConfig(config);
154
+ return resolved;
155
+ }
156
+ function getRegistryConfig(name) {
157
+ const config = getConfig();
158
+ return config.registries?.[name] || null;
159
+ }
160
+ function ensureRegistry(registryRef) {
161
+ const normalizedRef = registryRef.trim();
162
+ if (!normalizedRef) {
163
+ throw new Error("Registry cannot be empty");
164
+ }
165
+ const builtinBaseURL = BUILTIN_REGISTRY_DEFINITIONS[normalizedRef];
166
+ if (builtinBaseURL) {
167
+ return {
168
+ name: normalizedRef,
169
+ baseURL: builtinBaseURL,
170
+ builtin: true
171
+ };
172
+ }
173
+ const config = getConfig();
174
+ const existing = config.registries?.[normalizedRef];
175
+ if (existing) {
176
+ return {
177
+ name: normalizedRef,
178
+ baseURL: existing.baseURL,
179
+ builtin: false
180
+ };
181
+ }
182
+ const normalizedBaseURL = normalizeRegistryBaseURL(normalizedRef);
183
+ for (const [name, baseURL] of Object.entries(BUILTIN_REGISTRY_DEFINITIONS)) {
184
+ if (normalizeRegistryBaseURL(baseURL) === normalizedBaseURL) {
185
+ return {
186
+ name,
187
+ baseURL,
188
+ builtin: true
189
+ };
190
+ }
191
+ }
192
+ const inferredName = inferRegistryName(normalizedBaseURL);
193
+ const current = config.registries?.[inferredName];
194
+ const nextRegistry = {
195
+ baseURL: normalizedBaseURL,
196
+ createdAt: current?.createdAt || (/* @__PURE__ */ new Date()).toISOString()
197
+ };
198
+ config.registries = {
199
+ ...config.registries || {},
200
+ [inferredName]: nextRegistry
201
+ };
202
+ saveConfig(config);
203
+ return {
204
+ name: inferredName,
205
+ baseURL: normalizedBaseURL,
206
+ builtin: false
207
+ };
208
+ }
209
+ function resolveRegistry(registryRef) {
210
+ return ensureRegistry(registryRef || getCurrentRegistry());
211
+ }
212
+ function normalizeRegistryBaseURL(value) {
213
+ const trimmed = value.trim();
214
+ if (!trimmed) {
215
+ throw new Error("Registry cannot be empty");
216
+ }
217
+ const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
218
+ const parsed = new URL(withProtocol);
219
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
220
+ throw new Error(`Unsupported registry URL: ${value}`);
221
+ }
222
+ if (parsed.pathname && parsed.pathname !== "/") {
223
+ throw new Error(`Registry URL must not include a path: ${value}`);
224
+ }
225
+ parsed.pathname = "";
226
+ return parsed.toString().replace(/\/$/, "");
227
+ }
228
+ function resolveAppBaseURL(appCode, registryRef) {
229
+ const resolved = resolveRegistry(registryRef);
230
+ const registryUrl = new URL(resolved.baseURL);
231
+ if (isLocalLikeHost(registryUrl.hostname)) {
232
+ throw new Error(`Cannot infer app URL from registry ${resolved.baseURL}; please pass --url explicitly`);
233
+ }
234
+ return `${registryUrl.protocol}//${appCode}.${registryUrl.host}`;
235
+ }
236
+ function inferRegistryBaseURLFromAppBaseURL(appBaseURL, appCode) {
237
+ const parsed = new URL(appBaseURL);
238
+ const hostnameParts = parsed.hostname.split(".");
239
+ if (hostnameParts.length <= 2 || isLocalLikeHost(parsed.hostname)) {
240
+ parsed.pathname = "";
241
+ return parsed.toString().replace(/\/$/, "");
242
+ }
243
+ if (appCode && parsed.hostname.startsWith(`${appCode}.`)) {
244
+ parsed.hostname = parsed.hostname.slice(appCode.length + 1);
245
+ } else {
246
+ parsed.hostname = hostnameParts.slice(1).join(".");
247
+ }
248
+ parsed.pathname = "";
249
+ return parsed.toString().replace(/\/$/, "");
250
+ }
111
251
  function getBaseURL(appCode) {
112
252
  const code = appCode || getCurrentApp();
113
253
  if (!code) return null;
@@ -162,11 +302,29 @@ function shouldRefreshToken(appCode) {
162
302
  const fiveMinutes = 5 * 60 * 1e3;
163
303
  return expiresAt.getTime() - now.getTime() < fiveMinutes;
164
304
  }
165
- var AMASTER_CONFIG_DIR, CONFIG_FILE;
305
+ function inferRegistryName(baseURL) {
306
+ const parsed = new URL(baseURL);
307
+ return parsed.hostname.replace(/\./g, "-");
308
+ }
309
+ function isLocalLikeHost(hostname) {
310
+ if (hostname === "localhost") {
311
+ return true;
312
+ }
313
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
314
+ return true;
315
+ }
316
+ return false;
317
+ }
318
+ var AMASTER_CONFIG_DIR, CONFIG_FILE, DEFAULT_REGISTRY_NAME, BUILTIN_REGISTRY_DEFINITIONS;
166
319
  var init_config = __esm({
167
320
  "src/config.ts"() {
168
321
  AMASTER_CONFIG_DIR = path.join(os.homedir(), ".amaster");
169
322
  CONFIG_FILE = path.join(AMASTER_CONFIG_DIR, "config.json");
323
+ DEFAULT_REGISTRY_NAME = "helige";
324
+ BUILTIN_REGISTRY_DEFINITIONS = {
325
+ helige: "https://helige.cn",
326
+ "helige-int": "https://helige-int.cn"
327
+ };
170
328
  }
171
329
  });
172
330
 
@@ -2799,6 +2957,8 @@ async function initOpenClaw(options) {
2799
2957
  }
2800
2958
  addApp(options.appCode, {
2801
2959
  baseURL: options.baseURL,
2960
+ registry: options.registry,
2961
+ registryBaseURL: options.registryBaseURL,
2802
2962
  ossEndpoint,
2803
2963
  initializedAt: (/* @__PURE__ */ new Date()).toISOString()
2804
2964
  });
@@ -2812,6 +2972,8 @@ async function initOpenClaw(options) {
2812
2972
  {
2813
2973
  appCode: options.appCode,
2814
2974
  baseURL: options.baseURL,
2975
+ registry: options.registry || null,
2976
+ registryBaseURL: options.registryBaseURL || null,
2815
2977
  ossEndpoint,
2816
2978
  openClawDetected: false,
2817
2979
  installedSkills: 0,
@@ -2827,6 +2989,9 @@ async function initOpenClaw(options) {
2827
2989
  console.log(chalk4__default.default.blue("\n\u{1F4CB} Configuration:\n"));
2828
2990
  console.log(` App Code: ${chalk4__default.default.green(options.appCode)}`);
2829
2991
  console.log(` Base URL: ${chalk4__default.default.gray(options.baseURL)}`);
2992
+ if (options.registryBaseURL) {
2993
+ console.log(` Registry: ${chalk4__default.default.gray(`${options.registry || "custom"} -> ${options.registryBaseURL}`)}`);
2994
+ }
2830
2995
  console.log();
2831
2996
  }
2832
2997
  spinner.start("Downloading skills...");
@@ -2880,12 +3045,16 @@ async function initOpenClaw(options) {
2880
3045
  const config = getAppConfig(app);
2881
3046
  return {
2882
3047
  appCode: app,
2883
- baseURL: config?.baseURL || null
3048
+ baseURL: config?.baseURL || null,
3049
+ registry: config?.registry || null,
3050
+ registryBaseURL: config?.registryBaseURL || null
2884
3051
  };
2885
3052
  });
2886
3053
  const summary = {
2887
3054
  appCode: options.appCode,
2888
3055
  baseURL: options.baseURL,
3056
+ registry: options.registry || null,
3057
+ registryBaseURL: options.registryBaseURL || null,
2889
3058
  ossEndpoint,
2890
3059
  openClawDetected: true,
2891
3060
  installedSkills,
@@ -3066,6 +3235,24 @@ function createAmasterClient(appCode) {
3066
3235
  }
3067
3236
  return client$1;
3068
3237
  }
3238
+ function resolveInitBaseURL(options) {
3239
+ if (options.url) {
3240
+ const resolvedRegistry2 = resolveRegistry(
3241
+ options.registry || inferRegistryBaseURLFromAppBaseURL(options.url, options.appCode)
3242
+ );
3243
+ return {
3244
+ baseURL: options.url,
3245
+ registryName: resolvedRegistry2.name,
3246
+ registryBaseURL: resolvedRegistry2.baseURL
3247
+ };
3248
+ }
3249
+ const resolvedRegistry = resolveRegistry(options.registry);
3250
+ return {
3251
+ baseURL: resolveAppBaseURL(options.appCode, resolvedRegistry.name),
3252
+ registryName: resolvedRegistry.name,
3253
+ registryBaseURL: resolvedRegistry.baseURL
3254
+ };
3255
+ }
3069
3256
  var program = new commander.Command();
3070
3257
  program.name("amaster").description("CLI for Amaster SDK - Multi-app support for OpenClaw").version(resolveCliVersion());
3071
3258
  program.command("apps").description("List all configured apps").addOption(createFormatOption()).action((options) => {
@@ -3076,6 +3263,8 @@ program.command("apps").description("List all configured apps").addOption(create
3076
3263
  return {
3077
3264
  appCode,
3078
3265
  baseURL: config?.baseURL || null,
3266
+ registry: config?.registry || null,
3267
+ registryBaseURL: config?.registryBaseURL || null,
3079
3268
  authenticated: isAuthenticated(appCode),
3080
3269
  current: appCode === current
3081
3270
  };
@@ -3092,6 +3281,9 @@ program.command("apps").description("List all configured apps").addOption(create
3092
3281
  const prefix = app.current ? chalk4__default.default.green("\u2192 ") : " ";
3093
3282
  console.log(`${prefix}${chalk4__default.default.bold(app.appCode)}${app.current ? chalk4__default.default.green(" (current)") : ""}`);
3094
3283
  console.log(` ${chalk4__default.default.gray(app.baseURL || "No URL")}`);
3284
+ if (app.registryBaseURL) {
3285
+ console.log(` ${chalk4__default.default.gray(`registry: ${app.registry} -> ${app.registryBaseURL}`)}`);
3286
+ }
3095
3287
  console.log(
3096
3288
  ` ${app.authenticated ? chalk4__default.default.green("\u25CF Authenticated") : chalk4__default.default.yellow("\u25CB Not authenticated")}`
3097
3289
  );
@@ -3102,6 +3294,45 @@ program.command("apps").description("List all configured apps").addOption(create
3102
3294
  }
3103
3295
  });
3104
3296
  });
3297
+ var registryCmd = program.command("registry").description("Manage addon and app registries");
3298
+ registryCmd.command("list").alias("ls").description("List available registries").addOption(createFormatOption()).action((options) => {
3299
+ const currentRegistry = getCurrentRegistry();
3300
+ const registries = listRegistries().map((registry) => ({
3301
+ ...registry,
3302
+ current: registry.name === currentRegistry
3303
+ }));
3304
+ renderOutput(registries, {
3305
+ format: options.format,
3306
+ pretty: () => {
3307
+ console.log(chalk4__default.default.blue("\n\u{1F310} Registries\n"));
3308
+ for (const registry of registries) {
3309
+ const prefix = registry.current ? chalk4__default.default.green("\u2192 ") : " ";
3310
+ const builtin = registry.builtin ? chalk4__default.default.gray("builtin") : chalk4__default.default.gray("custom");
3311
+ console.log(`${prefix}${chalk4__default.default.bold(registry.name)}${registry.current ? chalk4__default.default.green(" (current)") : ""}`);
3312
+ console.log(` ${chalk4__default.default.gray(registry.baseURL)} ${builtin}`);
3313
+ }
3314
+ console.log();
3315
+ }
3316
+ });
3317
+ });
3318
+ registryCmd.command("use <registry>").description("Set the default registry by name or URL").addOption(createFormatOption()).action((registry, options) => {
3319
+ const resolved = setCurrentRegistry(registry);
3320
+ renderOutput(
3321
+ {
3322
+ name: resolved.name,
3323
+ baseURL: resolved.baseURL,
3324
+ builtin: resolved.builtin,
3325
+ current: true
3326
+ },
3327
+ {
3328
+ format: options.format,
3329
+ pretty: () => {
3330
+ console.log(chalk4__default.default.green(`\u2705 Now using registry: ${resolved.name}`));
3331
+ console.log(chalk4__default.default.gray(resolved.baseURL));
3332
+ }
3333
+ }
3334
+ );
3335
+ });
3105
3336
  program.command("use <app-code>").description("Set the default app for subsequent commands").addOption(createFormatOption()).action((appCode, options) => {
3106
3337
  if (setCurrentApp(appCode)) {
3107
3338
  renderOutput(
@@ -3121,10 +3352,17 @@ program.command("use <app-code>").description("Set the default app for subsequen
3121
3352
  process.exit(1);
3122
3353
  }
3123
3354
  });
3124
- program.command("init").description("Initialize an app for OpenClaw integration").requiredOption("--app-code <code>", "Application code (e.g., fhv94bto1)").requiredOption("--url <url>", "Base URL (e.g., https://fhv94bto1.helige.cn)").option("--api-key <key>", "API Key for OSS access").option("--oss-endpoint <endpoint>", "OSS endpoint").option("--force", "Reinitialize an existing app").addOption(createFormatOption()).action(async (options) => {
3355
+ program.command("init").description("Initialize an app for OpenClaw integration").requiredOption("--app-code <code>", "Application code (e.g., fhv94bto1)").option("--url <url>", "Base URL (e.g., https://fhv94bto1.helige.cn)").option("--registry <registry>", "Registry name or URL (default: current registry)").option("--api-key <key>", "API Key for OSS access").option("--oss-endpoint <endpoint>", "OSS endpoint").option("--force", "Reinitialize an existing app").addOption(createFormatOption()).action(async (options) => {
3356
+ const resolvedInit = resolveInitBaseURL({
3357
+ appCode: options.appCode,
3358
+ url: options.url,
3359
+ registry: options.registry
3360
+ });
3125
3361
  await initOpenClaw({
3126
3362
  appCode: options.appCode,
3127
- baseURL: options.url,
3363
+ baseURL: resolvedInit.baseURL,
3364
+ registry: resolvedInit.registryName,
3365
+ registryBaseURL: resolvedInit.registryBaseURL,
3128
3366
  apiKey: options.apiKey,
3129
3367
  ossEndpoint: options.ossEndpoint,
3130
3368
  force: options.force,
@@ -3681,5 +3919,6 @@ if (isDirectCliExecution()) {
3681
3919
  exports.createAmasterClient = createAmasterClient;
3682
3920
  exports.isDirectCliExecution = isDirectCliExecution;
3683
3921
  exports.resolveAppCode = resolveAppCode;
3922
+ exports.resolveInitBaseURL = resolveInitBaseURL;
3684
3923
  //# sourceMappingURL=cli.cjs.map
3685
3924
  //# sourceMappingURL=cli.cjs.map