amorphous-cli 0.3.9 → 0.3.10

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/lib/config.mjs CHANGED
@@ -1,16 +1,46 @@
1
1
  import { readFileSync, existsSync } from 'node:fs'
2
2
  import { homedir } from 'node:os'
3
- import { join } from 'node:path'
3
+ import { dirname, join, relative, resolve, sep } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
4
5
 
5
- export function loadDotEnv(cwd = process.cwd()) {
6
+ const CLI_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
7
+
8
+ function isWithin(root, path) {
9
+ const fromRoot = relative(resolve(root), resolve(path))
10
+ return fromRoot === '' || (fromRoot !== '..' && !fromRoot.startsWith(`..${sep}`))
11
+ }
12
+
13
+ function localRepositoryRoot(packageRoot) {
14
+ const root = resolve(packageRoot, '..')
15
+ try {
16
+ const appPackage = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
17
+ const cliPackage = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))
18
+ if (appPackage.name === 'amorphous' && cliPackage.name === 'amorphous-cli' && resolve(root, 'cli') === resolve(packageRoot)) {
19
+ return root
20
+ }
21
+ } catch {
22
+ /* The published CLI has no enclosing Amorphous app repository. */
23
+ }
24
+ return null
25
+ }
26
+
27
+ export function loadDotEnv(cwd = process.cwd(), options = {}) {
28
+ const packageRoot = options.packageRoot || CLI_PACKAGE_ROOT
29
+ const explicitRoot = options.amorphousRoot ?? process.env.AMORPHOUS_ROOT
6
30
  const roots = []
7
- if (process.env.AMORPHOUS_ROOT) roots.push(process.env.AMORPHOUS_ROOT)
8
- let dir = cwd
9
- for (let i = 0; i < 8; i++) {
10
- roots.push(dir)
11
- const parent = join(dir, '..')
12
- if (parent === dir) break
13
- dir = parent
31
+ if (explicitRoot) {
32
+ roots.push(explicitRoot)
33
+ } else {
34
+ const repoRoot = localRepositoryRoot(packageRoot)
35
+ if (!repoRoot || !isWithin(repoRoot, cwd)) return
36
+ let dir = resolve(cwd)
37
+ for (let i = 0; i < 8; i++) {
38
+ roots.push(dir)
39
+ if (dir === repoRoot) break
40
+ const parent = dirname(dir)
41
+ if (parent === dir || !isWithin(repoRoot, parent)) break
42
+ dir = parent
43
+ }
14
44
  }
15
45
  for (const base of roots) {
16
46
  const path = join(base, '.env')
@@ -0,0 +1,79 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import test from 'node:test'
6
+ import { loadDotEnv } from './config.mjs'
7
+
8
+ function withEnv(values, run) {
9
+ const previous = Object.fromEntries(Object.keys(values).map((name) => [name, process.env[name]]))
10
+ for (const [name, value] of Object.entries(values)) {
11
+ if (value === undefined) delete process.env[name]
12
+ else process.env[name] = value
13
+ }
14
+ try {
15
+ return run()
16
+ } finally {
17
+ for (const [name, value] of Object.entries(previous)) {
18
+ if (value === undefined) delete process.env[name]
19
+ else process.env[name] = value
20
+ }
21
+ }
22
+ }
23
+
24
+ test('published CLI ignores an unrelated project .env by default', () => {
25
+ const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
26
+ const project = join(root, 'project')
27
+ const packageRoot = join(root, 'npm-cache', 'amorphous-cli')
28
+ mkdirSync(project, { recursive: true })
29
+ mkdirSync(packageRoot, { recursive: true })
30
+ writeFileSync(join(project, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
31
+
32
+ try {
33
+ withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
34
+ loadDotEnv(project, { packageRoot })
35
+ assert.equal(process.env.APP_ORIGIN, undefined)
36
+ })
37
+ } finally {
38
+ rmSync(root, { recursive: true, force: true })
39
+ }
40
+ })
41
+
42
+ test('repository source CLI loads the repository .env for local development', () => {
43
+ const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
44
+ const cliRoot = join(root, 'cli')
45
+ mkdirSync(cliRoot, { recursive: true })
46
+ writeFileSync(join(root, 'package.json'), JSON.stringify({ name: 'amorphous' }))
47
+ writeFileSync(join(cliRoot, 'package.json'), JSON.stringify({ name: 'amorphous-cli' }))
48
+ writeFileSync(join(root, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
49
+
50
+ try {
51
+ withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
52
+ loadDotEnv(root, { packageRoot: cliRoot })
53
+ assert.equal(process.env.APP_ORIGIN, 'https://amorphous.test')
54
+ })
55
+ } finally {
56
+ rmSync(root, { recursive: true, force: true })
57
+ }
58
+ })
59
+
60
+ test('AMORPHOUS_ROOT explicitly opts a published CLI into a local .env', () => {
61
+ const root = mkdtempSync(join(tmpdir(), 'amorphous-config-'))
62
+ const project = join(root, 'project')
63
+ const localRoot = join(root, 'local-app')
64
+ const packageRoot = join(root, 'npm-cache', 'amorphous-cli')
65
+ mkdirSync(project, { recursive: true })
66
+ mkdirSync(localRoot, { recursive: true })
67
+ mkdirSync(packageRoot, { recursive: true })
68
+ writeFileSync(join(localRoot, '.env'), 'APP_ORIGIN=https://amorphous.test\n')
69
+
70
+ try {
71
+ withEnv({ APP_ORIGIN: undefined, AMORPHOUS_ROOT: undefined }, () => {
72
+ loadDotEnv(project, { packageRoot, amorphousRoot: localRoot })
73
+ assert.equal(process.env.APP_ORIGIN, 'https://amorphous.test')
74
+ })
75
+ } finally {
76
+ rmSync(root, { recursive: true, force: true })
77
+ }
78
+ })
79
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amorphous-cli",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "mcpName": "io.github.amorphous-one/amorphous",
5
5
  "description": "Make whatever you want, yourself, with your own agent, or with others.",
6
6
  "type": "module",
@@ -40,7 +40,7 @@ They tell you when to push, preview, publish, or invite. Do those when they ask.
40
40
 
41
41
  ## Start
42
42
 
43
- Run CLI commands as `npx --yes amorphous-cli@latest <command>`; this uses the latest CLI without a global install. The command examples below abbreviate that prefix as `amorphous-cli`. If a command prints `Run: npx --yes amorphous-cli@latest setup`, run that line, then repeat the command. If you are not signed in, run `npx --yes amorphous-cli@latest login`. Show the person the link it prints and ask them to finish in the browser. Do not ask for an email or a code.
43
+ Run CLI commands as `npx --yes amorphous-cli@latest <command>`; this uses the latest CLI without a global install. The published CLI ignores `.env` files in the current project and uses the hosted app. To explicitly use local settings, set `AMORPHOUS_ROOT` to the local Amorphous repo root. The command examples below abbreviate that prefix as `amorphous-cli`. If a command prints `Run: npx --yes amorphous-cli@latest setup`, run that line, then repeat the command. If you are not signed in, run `npx --yes amorphous-cli@latest login`. Show the person the link it prints and ask them to finish in the browser. Do not ask for an email or a code.
44
44
 
45
45
  Then choose the path that matches the request:
46
46