@hyperscale0/create 1.0.0-alpha.3 → 1.0.0-alpha.4

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
@@ -4,7 +4,8 @@ The Hyperscale project launcher: `npm create @hyperscale0` scaffolds a
4
4
  minimal project wired to your Product's hosted TypeScript SDK. It pins the
5
5
  SDK version the registry serves today, writes the project `.npmrc` for the
6
6
  authenticated registry, puts your key in a gitignored `.env`, and generates
7
- a `hello.mjs` that makes the product's first read call.
7
+ a `hello.mjs` that calls the selected parameterless SDK read or points to the
8
+ SDK's tailored first-call example.
8
9
 
9
10
  ## Use
10
11
 
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as b}from"node:child_process";import{existsSync as x,mkdirSync as P,readdirSync as T,writeFileSync as L}from"node:fs";import{basename as K,join as O,resolve as v}from"node:path";const E="https://hyperscale0.ai",S="sandbox",k="hyperscale-app",I=3e4,C=`bunx @hyperscale0/create [directory] [options]
2
+ import{spawn as b}from"node:child_process";import{existsSync as K,mkdirSync as P,readdirSync as T,writeFileSync as L}from"node:fs";import{basename as O,join as D,resolve as S}from"node:path";const E="https://hyperscale0.ai",k="sandbox",A="hyperscale-app",I=3e4,C=`bunx @hyperscale0/create [directory] [options]
3
3
 
4
4
  Scaffolds a minimal project wired to your Product's hosted TypeScript SDK:
5
5
  a package.json pinned to the SDK version the registry serves today, a
6
6
  project .npmrc for the authenticated registry, a .env carrying the key from
7
- your environment, and hello.mjs making the product's first read call.
7
+ your environment, and hello.mjs calling the selected parameterless SDK read or
8
+ pointing to the SDK's tailored first-call example.
8
9
 
9
10
  Options:
10
11
  --api-key <key> Product API key (default: $HYPERSCALE_API_KEY)
11
12
  --base-url <url> API origin (default: $HYPERSCALE_BASE_URL, else ${E})
12
- --environment <name> sandbox or live (default: $HYPERSCALE_ENVIRONMENT, else ${S})
13
+ --environment <name> sandbox or live (default: $HYPERSCALE_ENVIRONMENT, else ${k})
13
14
  --no-install Write the project without running npm install
14
15
  --json Print a machine-readable summary
15
16
  --version Print the launcher version
@@ -26,8 +27,8 @@ Examples:
26
27
  npm create @hyperscale0
27
28
  bunx @hyperscale0/create my-product
28
29
  bunx @hyperscale0/create my-product --no-install
29
- `;class a extends Error{}const j=new Set(["--api-key","--base-url","--environment"]),D=new Set(["--help","--json","--no-install","--version"]);function H(e){const t=[],n={};for(let r=0;r<e.length;r+=1){const s=e[r];if(!s.startsWith("--")){t.push(s);continue}if(j.has(s)){const o=e[r+1];if(o===void 0)throw new a(`${s} needs a value`);n[s]=o,r+=1;continue}if(D.has(s)){n[s]=!0;continue}throw new a(`Unknown option: ${s}`)}return{flags:n,positional:t}}function Y(e,t){const n=e["--api-key"]??t.HYPERSCALE_API_KEY;if(!n)throw new a("No API key. Set HYPERSCALE_API_KEY, or pass --api-key. Request access at https://hyperscale0.ai/request-access.");const r=e["--environment"]??t.HYPERSCALE_ENVIRONMENT??S;if(r!=="sandbox"&&r!=="live")throw new a(`Unknown environment: ${r}. Use sandbox or live.`);const s=(e["--base-url"]??t.HYPERSCALE_BASE_URL??E).replace(/\/+$/,"");return{apiKey:n,baseUrl:s,environment:r}}async function A(e,t){try{const n=await fetch(t,{headers:{accept:"application/json",authorization:`Bearer ${e.apiKey}`,"x-hyperscale-environment":e.environment},signal:AbortSignal.timeout(I),redirect:"manual"});if(n.status>=300&&n.status<400)throw new a(`${t} redirected, so the key was not sent on. Point --base-url at the origin the API answers on; the usual cause is http:// where it serves https://.`);return{response:n,text:V(await n.text())}}catch(n){throw n instanceof a?n:new a(`Could not reach ${t}: ${n.message}`)}}async function F(e){const{response:t,text:n}=await A(e,`${e.baseUrl}/v1/llms.txt`);if(!t.ok)throw N(t,n);const r=/^# (.+)$/m.exec(n),s=/^## Operations \((\d+)\)$/m.exec(n),o=/^- Auth: \S+ \(secret via (\S+)\)$/m.exec(n);if(!r||!s||!o)throw new Error("The product document is not in the expected format; upgrade the launcher.");const i=o[1];if(!/^HYPERSCALE_[A-Z0-9_]*$/.test(i))throw new a(`The product document names an invalid auth environment variable (${i}); refusing to scaffold.`);const c=[],d=/^- ([A-Z]+) (\S+) · (\S+) \((\S+); idempotency (\S+)\)$/gm;for(const u of n.matchAll(d))c.push({method:u[1],operationId:u[4],path:u[2]});const h=Number(s[1]);if(c.length!==h)throw new Error(`The product document declares ${h} operations but ${c.length} parsed; upgrade the launcher.`);const l=new Map,_=/^ {2}\d+\. (\S+) \(([^)]+)\)$/gm;for(const u of n.matchAll(_)){const y=u[1];if(!/^[A-Za-z_$][\w$]*$/.test(y))throw new a(`The product document names an invalid SDK function (${y}); refusing to scaffold.`);const[$,U]=u[2].split("; ");U!=="gap"&&!l.has($)&&l.set($,y)}const g=/^- Package: (.+)$/m.exec(n),w=/^- Registry: (\S+)$/m.exec(n);if(!g||!w)throw new a("This Product has no hosted SDK yet. Build it in the composer, then rerun.");const m=g[1].trim();if(!/^@hyperscale\/[a-z0-9][a-z0-9._-]*$/.test(m))throw new a(`The product document names an invalid SDK package (${m}); refusing to scaffold.`);const f=w[1].replace(/\/+$/,"");if(B(f)!==new URL(e.baseUrl).origin)throw new a(`The product document names a registry on another origin (${f}); refusing to scaffold.`);return{apiKeyEnvVar:i,operations:c,packageName:m,registryUrl:f,sdkFunctionByOperationId:l,title:r[1].trim()}}function V(e){return e.replace(/\p{Cc}/gu,t=>`
30
- \r`.includes(t)?t:"")}function B(e){try{return new URL(e).origin}catch{return null}}async function J(e,t){const n=encodeURIComponent(t.packageName).replace("%40","@"),{response:r,text:s}=await A(e,`${t.registryUrl}/${n}`);if(!r.ok)throw N(r,s);let o;try{o=JSON.parse(s)["dist-tags"]?.latest}catch{o=void 0}if(typeof o!="string")throw new a(`The registry packument for ${t.packageName} names no latest version; upgrade the launcher.`);if(!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(o))throw new a(`The registry packument for ${t.packageName} names ${o} as its latest version, which is not a version; refusing to scaffold.`);return o}function N(e,t){let n=t.trim();try{const r=JSON.parse(t);r&&r.error&&typeof r.error.message=="string"&&(n=`${r.error.code}: ${r.error.message}`)}catch{}return new a(`HTTP ${e.status}${n?` \xB7 ${n}`:""}`)}function R(e){const t=e.operations.find(n=>n.method==="GET"&&!n.path.includes("{")&&e.sdkFunctionByOperationId.has(n.operationId));if(t)return e.sdkFunctionByOperationId.get(t.operationId)}function M(e,t){const n=`// Generated by @hyperscale0/create for ${e.title}.
30
+ `;class a extends Error{}const j=new Set(["--api-key","--base-url","--environment"]),H=new Set(["--help","--json","--no-install","--version"]);function Y(e){const n=[],t={};for(let r=0;r<e.length;r+=1){const s=e[r];if(!s.startsWith("--")){n.push(s);continue}if(j.has(s)){const o=e[r+1];if(o===void 0)throw new a(`${s} needs a value`);t[s]=o,r+=1;continue}if(H.has(s)){t[s]=!0;continue}throw new a(`Unknown option: ${s}`)}return{flags:t,positional:n}}function F(e,n){const t=e["--api-key"]??n.HYPERSCALE_API_KEY;if(!t)throw new a("No API key. Set HYPERSCALE_API_KEY, or pass --api-key. Request access at https://hyperscale0.ai/request-access.");const r=e["--environment"]??n.HYPERSCALE_ENVIRONMENT??k;if(r!=="sandbox"&&r!=="live")throw new a(`Unknown environment: ${r}. Use sandbox or live.`);const s=(e["--base-url"]??n.HYPERSCALE_BASE_URL??E).replace(/\/+$/,"");return{apiKey:t,baseUrl:s,environment:r}}async function N(e,n){try{const t=await fetch(n,{headers:{accept:"application/json",authorization:`Bearer ${e.apiKey}`,"x-hyperscale-environment":e.environment},signal:AbortSignal.timeout(I),redirect:"manual"});if(t.status>=300&&t.status<400)throw new a(`${n} redirected, so the key was not sent on. Point --base-url at the origin the API answers on; the usual cause is http:// where it serves https://.`);return{response:t,text:B(await t.text())}}catch(t){throw t instanceof a?t:new a(`Could not reach ${n}: ${t.message}`)}}async function V(e){const{response:n,text:t}=await N(e,`${e.baseUrl}/v1/llms.txt`);if(!n.ok)throw _(n,t,[e.apiKey]);const r=/^# (.+)$/m.exec(t),s=/^## Operations \((\d+)\)$/m.exec(t),o=/^- Auth: \S+ \(secret via (\S+)\)$/m.exec(t);if(!r||!s||!o)throw new Error("The product document is not in the expected format; upgrade the launcher.");const i=o[1];if(!/^HYPERSCALE_[A-Z0-9_]*$/.test(i))throw new a(`The product document names an invalid auth environment variable (${i}); refusing to scaffold.`);const c=[],d=/^- ([A-Z]+) (\S+) · (\S+) \((\S+); idempotency (\S+)\)$/gm;for(const u of t.matchAll(d))c.push({method:u[1],operationId:u[4],path:u[2]});const h=Number(s[1]);if(c.length!==h)throw new Error(`The product document declares ${h} operations but ${c.length} parsed; upgrade the launcher.`);const l=new Map,x=/^ {2}\d+\. (\S+) \(([^)]+)\)$/gm;for(const u of t.matchAll(x)){const y=u[1];if(!/^[A-Za-z_$][\w$]*$/.test(y))throw new a(`The product document names an invalid SDK function (${y}); refusing to scaffold.`);const[v,U]=u[2].split("; ");U!=="gap"&&!l.has(v)&&l.set(v,y)}const w=/^- Package: (.+)$/m.exec(t),$=/^- Registry: (\S+)$/m.exec(t);if(!w||!$)throw new a("This Product has no hosted SDK yet. Build it in the composer, then rerun.");const m=w[1].trim();if(!/^@hyperscale\/[a-z0-9][a-z0-9._-]*$/.test(m))throw new a(`The product document names an invalid SDK package (${m}); refusing to scaffold.`);const f=$[1].replace(/\/+$/,"");if(J(f)!==new URL(e.baseUrl).origin)throw new a(`The product document names a registry on another origin (${f}); refusing to scaffold.`);return{apiKeyEnvVar:i,operations:c,packageName:m,registryUrl:f,sdkFunctionByOperationId:l,title:r[1].trim()}}function B(e){return e.replace(/\p{Cc}/gu,n=>`
31
+ \r`.includes(n)?n:"")}function J(e){try{return new URL(e).origin}catch{return null}}async function M(e,n){const t=encodeURIComponent(n.packageName).replace("%40","@"),{response:r,text:s}=await N(e,`${n.registryUrl}/${t}`);if(!r.ok)throw _(r,s,[e.apiKey]);let o;try{o=JSON.parse(s)["dist-tags"]?.latest}catch{o=void 0}if(typeof o!="string")throw new a(`The registry packument for ${n.packageName} names no latest version; upgrade the launcher.`);if(!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(o))throw new a(`The registry packument for ${n.packageName} names ${o} as its latest version, which is not a version; refusing to scaffold.`);return o}function R(e,n){let t=e;for(const r of n)typeof r=="string"&&r.length>0&&(t=t.replaceAll(r,"[redacted]"));return t}function _(e,n,t=[]){let r=R(n.trim(),t);try{const s=JSON.parse(n);s&&s.error&&typeof s.error.message=="string"&&(r=R(`${s.error.code}: ${s.error.message}`,t))}catch{}return new a(`HTTP ${e.status}${r?` \xB7 ${r}`:""}`)}function g(e){const n=e.operations.find(t=>t.method==="GET"&&!t.path.includes("{")&&e.sdkFunctionByOperationId.has(t.operationId));if(n)return e.sdkFunctionByOperationId.get(n.operationId)}function z(e,n){const t=`// Generated by @hyperscale0/create for ${e.title}.
31
32
  // Run with: npm run hello (reads ${e.apiKeyEnvVar} from .env)
32
33
  import { createHyperscaleSdk } from ${JSON.stringify(e.packageName)};
33
34
 
@@ -36,19 +37,19 @@ if (!apiKey) {
36
37
  throw new Error("Set ${e.apiKeyEnvVar} in .env before running.");
37
38
  }
38
39
  const client = createHyperscaleSdk({
39
- environment: ${JSON.stringify(t)},
40
+ environment: ${JSON.stringify(n)},
40
41
  auth: () => apiKey,
41
42
  ...(process.env.HYPERSCALE_BASE_URL
42
43
  ? { baseUrl: process.env.HYPERSCALE_BASE_URL }
43
44
  : {}),
44
- });`,r=R(e);if(!r){const s=`SDK client ready for ${e.title}.`,o=`node_modules/${e.packageName}/README.md`;return`${n}
45
+ });`,r=g(e);if(!r){const s=`SDK client ready for ${e.title}.`,o=`node_modules/${e.packageName}/README.md`;return`${t}
45
46
 
46
47
  console.log(${JSON.stringify(s)});
47
48
  console.log(
48
- "This product has no parameterless read; see the first-call example in",
49
+ "No callable parameterless SDK read was selected; see the first-call example in",
49
50
  ${JSON.stringify(o)},
50
51
  );
51
- `}return`${n}
52
+ `}return`${t}
52
53
 
53
54
  const result = await client[${JSON.stringify(r)}]();
54
55
  if (result.error) {
@@ -56,23 +57,23 @@ if (result.error) {
56
57
  process.exit(1);
57
58
  }
58
59
  console.log(JSON.stringify(result.data, null, 2));
59
- `}function z(e){const t=new URL(e.registryUrl),n=t.pathname.endsWith("/")?t.pathname:`${t.pathname}/`;return`@hyperscale:registry=${e.registryUrl}
60
- //${t.host}${n}:_authToken=\${${e.apiKeyEnvVar}}
60
+ `}function q(e){const n=new URL(e.registryUrl),t=n.pathname.endsWith("/")?n.pathname:`${n.pathname}/`;return`@hyperscale:registry=${e.registryUrl}
61
+ //${n.host}${t}:_authToken=\${${e.apiKeyEnvVar}}
61
62
  ignore-scripts=true
62
- `}function q(e,t){return`${e.apiKeyEnvVar}=${t.apiKey}
63
- HYPERSCALE_BASE_URL=${t.baseUrl}
64
- `}function G(e){return K(v(e)).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^[-._]+/,"")||k}function W(e,t,n){return`${JSON.stringify({name:G(e),version:"0.0.0",private:!0,type:"module",scripts:{hello:"node --env-file=.env hello.mjs"},engines:{node:">=20.6"},dependencies:{[t.packageName]:n}},null,2)}
65
- `}function Z(e,t){return`# ${e.title}
63
+ `}function G(e,n){return`${e.apiKeyEnvVar}=${n.apiKey}
64
+ HYPERSCALE_BASE_URL=${n.baseUrl}
65
+ `}function W(e){return O(S(e)).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^[-._]+/,"")||A}function Z(e,n,t){return`${JSON.stringify({name:W(e),version:"0.0.0",private:!0,type:"module",scripts:{hello:"node --env-file=.env hello.mjs"},engines:{node:">=20.6"},dependencies:{[n.packageName]:t}},null,2)}
66
+ `}function Q(e,n){const t=g(e)?"makes the product's first read call through the SDK":"constructs the SDK client and points to its tailored first-call example";return`# ${e.title}
66
67
 
67
68
  A minimal project wired to the hosted Hyperscale SDK for this product.
68
69
 
69
- - \`package.json\` pins \`${e.packageName}@${t}\`, the version the
70
+ - \`package.json\` pins \`${e.packageName}@${n}\`, the version the
70
71
  registry served when this project was created.
71
72
  - \`.npmrc\` points the \`@hyperscale\` scope at the product's registry and
72
73
  reads the key from \`${e.apiKeyEnvVar}\`.
73
74
  - \`.env\` carries the key and the API host this project was created with. It
74
75
  is gitignored; rotate the key from the Developers desk whenever you need to.
75
- - \`hello.mjs\` makes the product's first read call through the SDK.
76
+ - \`hello.mjs\` ${t}.
76
77
 
77
78
  ## Run
78
79
 
@@ -90,8 +91,8 @@ bunx @hyperscale0/cli ops list
90
91
  \`\`\`
91
92
 
92
93
  Needs Node 20.6 or newer (\`--env-file\`).
93
- `}function Q(e,t,n,r){return[{path:".env",contents:q(t,n)},{path:".gitignore",contents:`node_modules/
94
+ `}function X(e,n,t,r){return[{path:".gitignore",contents:`node_modules/
94
95
  .env
95
- `},{path:".npmrc",contents:z(t)},{path:"README.md",contents:Z(t,r)},{path:"hello.mjs",contents:M(t,n.environment)},{path:"package.json",contents:W(e,t,r)}]}function X(e,t){const n=v(e);if(x(n)&&T(n).length>0)throw new a(`${e} already exists and is not empty. Pick a new directory.`);P(n,{recursive:!0});for(const r of t)L(O(n,r.path),r.contents);return n}function ee(e,t,n){return new Promise((r,s)=>{const o=b("npm",["install","--no-audit","--no-fund","--ignore-scripts"],{cwd:e,env:{...process.env,[t.apiKeyEnvVar]:n.apiKey},stdio:["ignore",2,2]});o.on("error",i=>{s(new a(`npm install could not start: ${i.message}. The project is written; fix the cause and run npm install in it.`))}),o.on("exit",i=>{if(i===0){r();return}s(new a(`npm install failed with exit code ${i}. The project is written; fix the cause and run npm install in it.`))})})}function p(e){process.stdout.write(`${e}
96
- `)}async function ne(){const{readFileSync:e}=await import("node:fs");return JSON.parse(e(new URL("./package.json",import.meta.url),"utf8")).version}async function te(e,t){const{flags:n,positional:r}=H(e);if(n["--version"])return p(await ne()),0;if(n["--help"])return process.stdout.write(C),0;if(r.length>1)throw new a("At most one directory argument is allowed.");const s=r[0]??k,o=Y(n,t),i=await F(o),c=await J(o,i),d=X(s,Q(s,i,o,c)),h=!n["--no-install"];h&&await ee(d,i,o);const l=R(i);return n["--json"]?(p(JSON.stringify({baseUrl:o.baseUrl,directory:d,environment:o.environment,helloOperation:l??null,installed:h,packageName:i.packageName,product:i.title,version:c},null,2)),0):(p(`Created ${s} for ${i.title}.`),p(""),p(` ${i.packageName}@${c} (${o.environment})`),h||p(" npm install"),p(l?` npm run hello calls ${l}`:" npm run hello constructs the client; see README.md for the first call"),0)}te(process.argv.slice(2),process.env).then(e=>{process.exitCode=e},e=>{e instanceof a?process.stderr.write(`${e.message}
96
+ `},{path:".env",contents:G(n,t),mode:384},{path:".npmrc",contents:q(n)},{path:"README.md",contents:Q(n,r)},{path:"hello.mjs",contents:z(n,t.environment)},{path:"package.json",contents:Z(e,n,r)}]}function ee(e,n){const t=S(e);if(K(t)&&T(t).length>0)throw new a(`${e} already exists and is not empty. Pick a new directory.`);P(t,{recursive:!0});for(const r of n)L(D(t,r.path),r.contents,r.mode?{mode:r.mode}:{});return t}function te(e,n,t){return new Promise((r,s)=>{const o=b("npm",["install","--no-audit","--no-fund","--ignore-scripts","--registry",n.registryUrl,"--prefer-online","--offline=false"],{cwd:e,env:{...process.env,[n.apiKeyEnvVar]:t.apiKey},stdio:["ignore",2,2]});o.on("error",i=>{s(new a(`npm install could not start: ${i.message}. The project is written; fix the cause and run npm install in it.`))}),o.on("exit",i=>{if(i===0){r();return}s(new a(`npm install failed with exit code ${i}. The project is written; fix the cause and run npm install in it.`))})})}function p(e){process.stdout.write(`${e}
97
+ `)}async function ne(){const{readFileSync:e}=await import("node:fs");return JSON.parse(e(new URL("./package.json",import.meta.url),"utf8")).version}async function re(e,n){const{flags:t,positional:r}=Y(e);if(t["--version"])return p(await ne()),0;if(t["--help"])return process.stdout.write(C),0;if(r.length>1)throw new a("At most one directory argument is allowed.");const s=r[0]??A,o=F(t,n),i=await V(o),c=await M(o,i),d=ee(s,X(s,i,o,c)),h=!t["--no-install"];h&&await te(d,i,o);const l=g(i);return t["--json"]?(p(JSON.stringify({baseUrl:o.baseUrl,directory:d,environment:o.environment,helloOperation:l??null,installed:h,packageName:i.packageName,product:i.title,version:c},null,2)),0):(p(`Created ${s} for ${i.title}.`),p(""),p(` ${i.packageName}@${c} (${o.environment})`),h||p(" npm install"),p(l?` npm run hello calls ${l}`:" npm run hello constructs the client; see README.md for the first call"),0)}re(process.argv.slice(2),process.env).then(e=>{process.exitCode=e},e=>{e instanceof a?process.stderr.write(`${e.message}
97
98
  `):console.error(e),process.exitCode=1});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperscale0/create",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "The Hyperscale project launcher: scaffold a minimal project wired to one Product's hosted SDK.",
5
5
  "license": "LicenseRef-Hyperscale-Proprietary",
6
6
  "author": "Hyperscale LLC",