freecode-sh 0.1.0-alpha.22 → 0.1.0-alpha.23

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 (3) hide show
  1. package/README.md +10 -0
  2. package/freecode.mjs +39 -4
  3. package/package.json +5 -1
package/README.md CHANGED
@@ -10,6 +10,9 @@ are not shipped in the npm package.
10
10
 
11
11
  Both `freecode` and `freecode-sh` launch the same Free Code CLI.
12
12
 
13
+ The signed native `alpha` channel is the canonical current release. npm
14
+ `latest` mirrors that version; npm `alpha` is intentionally not used.
15
+
13
16
  ## Install
14
17
 
15
18
  ```bash
@@ -19,6 +22,13 @@ freecode doctor
19
22
  freecode
20
23
  ```
21
24
 
25
+ For a reproducible clean install of this immutable release:
26
+
27
+ ```bash
28
+ npm install -g freecode-sh@0.1.0-alpha.23
29
+ freecode --version
30
+ ```
31
+
22
32
  ```bash
23
33
  freecode logout
24
34
  npm uninstall -g freecode-sh
package/freecode.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { spawn } from 'node:child_process'
4
4
  import { constants } from 'node:fs'
5
- import { access, readFile, realpath } from 'node:fs/promises'
5
+ import { access, chmod, lstat, mkdir, readFile, realpath, writeFile } from 'node:fs/promises'
6
6
  import { homedir } from 'node:os'
7
7
  import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
8
8
  import { fileURLToPath } from 'node:url'
@@ -12,7 +12,13 @@ const packageDirectory = dirname(fileURLToPath(import.meta.url))
12
12
  const bootstrapPath = fileURLToPath(import.meta.url)
13
13
  const packageMetadata = JSON.parse(await readFile(join(packageDirectory, 'package.json'), 'utf8'))
14
14
 
15
- if (packageMetadata.name !== 'freecode-sh' || !/^\d+\.\d+\.\d+-alpha\.\d+$/.test(packageMetadata.version)) {
15
+ const releaseContract = packageMetadata.freecodeRelease
16
+ if (
17
+ packageMetadata.name !== 'freecode-sh'
18
+ || !/^\d+\.\d+\.\d+-alpha\.\d+$/.test(packageMetadata.version)
19
+ || releaseContract?.channel !== 'alpha'
20
+ || releaseContract?.nativeVersion !== packageMetadata.version
21
+ ) {
16
22
  fail('The npm package metadata is invalid. Reinstall freecode-sh and try again.')
17
23
  }
18
24
 
@@ -59,15 +65,16 @@ if (nativeVersion && compareAlphaVersions(nativeVersion, packageMetadata.version
59
65
  const installer = windows
60
66
  ? {
61
67
  command: 'powershell.exe',
62
- args: ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', join(packageDirectory, 'install.ps1'), '-Version', packageMetadata.version],
68
+ args: ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', join(packageDirectory, 'install.ps1'), '-Channel', releaseContract.channel, '-Version', releaseContract.nativeVersion],
63
69
  }
64
- : { command: '/bin/sh', args: [join(packageDirectory, 'install.sh'), '--version', packageMetadata.version] }
70
+ : { command: '/bin/sh', args: [join(packageDirectory, 'install.sh'), '--channel', releaseContract.channel, '--version', releaseContract.nativeVersion] }
65
71
  const installerEnvironment = {
66
72
  ...process.env,
67
73
  FREECODE_INSTALL_ROOT: installRoot,
68
74
  FREECODE_BIN_DIR: binDirectory,
69
75
  FREECODE_INSTALL_EMBEDDED: 'true',
70
76
  }
77
+ const installStartedAt = Date.now()
71
78
  const installStatus = await run(
72
79
  installer.command,
73
80
  installer.args,
@@ -78,6 +85,11 @@ if (nativeVersion && compareAlphaVersions(nativeVersion, packageMetadata.version
78
85
  if (!nativeVersion || compareAlphaVersions(nativeVersion, packageMetadata.version) < 0) {
79
86
  fail('The signed installer completed without creating a managed Free Code executable.')
80
87
  }
88
+ await writeInstallTelemetryReceipt({
89
+ releaseId: packageMetadata.version,
90
+ platform: ['darwin', 'linux', 'win32'].includes(platform) ? platform : 'unknown',
91
+ durationMs: Math.min(86_400_000, Math.max(0, Date.now() - installStartedAt)),
92
+ })
81
93
  process.exitCode = await runManaged(nativeExecutable, nativeVersion, process.argv.slice(2))
82
94
  }
83
95
 
@@ -116,6 +128,29 @@ function runManaged(path, version, args) {
116
128
  return run(command, args)
117
129
  }
118
130
 
131
+ async function writeInstallTelemetryReceipt({ releaseId, platform: receiptPlatform, durationMs }) {
132
+ const configRoot = windows && process.env.APPDATA ? process.env.APPDATA : join(homeDirectory, '.config')
133
+ const directory = resolve(configRoot, 'freecode')
134
+ const path = join(directory, 'install-receipt.json')
135
+ try {
136
+ await mkdir(directory, { recursive: true, mode: 0o700 })
137
+ if ((await lstat(directory)).isSymbolicLink()) return
138
+ try {
139
+ if ((await lstat(path)).isSymbolicLink()) return
140
+ } catch (error) {
141
+ if (error?.code !== 'ENOENT') return
142
+ }
143
+ if (!windows) await chmod(directory, 0o700)
144
+ await writeFile(path, `${JSON.stringify({ version: 1, releaseId, platform: receiptPlatform, durationMs })}\n`, {
145
+ encoding: 'utf8',
146
+ mode: 0o600,
147
+ })
148
+ if (!windows) await chmod(path, 0o600)
149
+ } catch {
150
+ // Install telemetry is best effort and must never fail a verified install.
151
+ }
152
+ }
153
+
119
154
  function compareAlphaVersions(left, right) {
120
155
  const parse = (version) => {
121
156
  const match = version.match(/^(\d+)\.(\d+)\.(\d+)-alpha\.(\d+)$/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freecode-sh",
3
- "version": "0.1.0-alpha.22",
3
+ "version": "0.1.0-alpha.23",
4
4
  "description": "A free coding agent for your terminal.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Free Code",
@@ -44,5 +44,9 @@
44
44
  "publishConfig": {
45
45
  "access": "public",
46
46
  "tag": "latest"
47
+ },
48
+ "freecodeRelease": {
49
+ "channel": "alpha",
50
+ "nativeVersion": "0.1.0-alpha.23"
47
51
  }
48
52
  }