Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: holepunchto/pear
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 64f02a21660cb158656277822fa1ab68e76410b6
Choose a base ref
..
head repository: holepunchto/pear
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c7971fa383a18e0a94ff4f3578b889b4e079f6ac
Choose a head ref
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@ jobs:
platform: win32
arch: x64
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 https://github.com/actions/checkout/releases/tag/v4.1.1
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@
* CLI - `pear run` - only set NODE_ENV production when not dev mode
* Desktop - setPosition edge-case guard
* Desktop - re-enable decal loader titlebar and window controls
* Reporting - client-side reporting-to-sidecar state-fix
* Reporting - client-side reporting-to-sidecar state-fix
* CLI - `pear run [flags] <link|dir> [...app-args]` - fix for `link [...app-args]` on Windows

### Improvements

@@ -33,6 +34,7 @@
* API - `Pear.worker` - Worker now inherits flags passed to `pear` which creates equivalent `Pear.config` state in Worker
* CLI - `pear -v` improved output
* CLI - electron and pear SemVers added to `pear versions`
* Desktop - Not Found default screen

## v1.3.4

17 changes: 13 additions & 4 deletions cmd/dump.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
'use strict'
const { ERR_INVALID_INPUT } = require('../errors')
const { isAbsolute, resolve } = require('bare-path')
const { outputter } = require('./iface')
const { outputter, password } = require('./iface')

const output = outputter('stage', {
dumping: ({ link, dir, list }) => list > -1 ? '' : `\n🍐 Dumping ${link} into ${dir}`,
file: ({ key, value }) => `${key}${value ? '\n' + value : ''}`,
complete: '\nDumping complete!\n',
error: ({ code, stack }) => `Dumping Error (code: ${code || 'none'}) ${stack}`
error: (err, info, ipc) => {
if (err.info && err.info.encrypted && info.ask) {
const explain = 'This application is encrypted.\n' +
'\nEnter the password to dump the app.\n\n'
const message = 'Added encryption key, run dump again to complete it.'
return password({ ipc, key: err.info.key, explain, message })
}
return `Dumping Error (code: ${err.code || 'none'}) ${err.stack}`
}
})

module.exports = (ipc) => async function dump (cmd) {
const { checkout, json } = cmd.flags
const { checkout, json, encryptionKey, ask } = cmd.flags
const { link } = cmd.args
let { dir } = cmd.args
if (!link) throw ERR_INVALID_INPUT('<link> must be specified.')
if (!dir) throw ERR_INVALID_INPUT('<dir> must be specified.')
dir = dir === '-' ? '-' : (isAbsolute(dir) ? dir : resolve('.', dir))
await output(json, ipc.dump({ id: Bare.pid, link, dir, checkout }))
await output(json, ipc.dump({ id: Bare.pid, link, dir, checkout, encryptionKey }), { ask }, ipc)
}
2 changes: 1 addition & 1 deletion cmd/encryption-key.js
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ class EncryptionKey {
async add (cmd) {
const { name, secret } = cmd.args
try { hypercoreid.decode(secret) } catch { throw ERR_INVALID_INPUT('Invalid encryption key') }
const stream = this.ipc.encryptionKey({ pid: Bare.pid, action: 'add', name, secret }, this.ipc)
const stream = this.ipc.encryptionKey({ pid: Bare.pid, action: 'add', name, value: secret }, this.ipc)
await output(false, stream)
}

65 changes: 40 additions & 25 deletions cmd/iface.js
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ function indicator (value, type = 'success') {
return value < 0 ? ansi.cross + ' ' : (value > 0 ? ansi.tick + ' ' : ansi.gray('- '))
}

const outputter = (cmd, taggers = {}) => async (json, stream, info = {}) => {
const outputter = (cmd, taggers = {}) => async (json, stream, info = {}, ipc) => {
let error = null
if (Array.isArray(stream)) stream = asyncIterate(stream)
try {
@@ -71,7 +71,7 @@ const outputter = (cmd, taggers = {}) => async (json, stream, info = {}) => {
}
let result = null
try {
result = typeof taggers[tag] === 'function' ? taggers[tag](data, info) : (taggers[tag] || false)
result = typeof taggers[tag] === 'function' ? await taggers[tag](data, info, ipc) : (taggers[tag] || false)
} catch (err) {
error = err
break
@@ -144,37 +144,52 @@ and then becomes the sidecar.`,

const usage = { header, version, banner, descriptions, footer }

async function permit ({ ipc, key, message, explain, ask, act }) {
async function trust ({ ipc, key, explain, act, ask, message }) {
const z32 = hypercoreid.encode(key)
explain = explain ?? 'Be sure that software is trusted before running it\n' +
'\nType "TRUST" to allow execution or anything else to exit\n\n'
ask = ask ?? 'Trust application'
act = act ?? 'Use pear run again to execute trusted application'
const sure = ansi.cross + ' Key pear://' + z32 + ' is not known\n\n' + explain

const prompt = new Interact(sure, [
{
name: 'trust',
default: '',
prompt: ask,
delim: '?',
validation: (value) => !(value.toLowerCase() !== 'trust' && value === 'TRUST'),
msg: ansi.cross + ' uppercase TRUST to confirm'
}
])
const result = await prompt.run()
if (result.trust === 'TRUST') {
await ipc.trust(key)
const dialog = ansi.cross + ' Key pear://' + z32 + ' is not known\n\n' + explain
const delim = '?'
const validation = (value) => !(value.toLowerCase() !== 'trust' && value === 'TRUST')
const msg = ansi.cross + ' uppercase TRUST to confirm'

const result = await permit({ dialog, ask, delim, validation, msg })
if (result.value === 'TRUST') {
await ipc.permit({ key })
print('\n' + ansi.tick + ' pear://' + z32 + ' is now trusted\n')
print(act + '\n')
await ipc.close()
Bare.exit()
} else {
print('')
print(message + '\n', false)
print(message, false)
await ipc.close()
Bare.exit(77)
}
}

module.exports = { usage, permit, stdio, ansi, indicator, status, print, byteDiff, diff, outputter }
async function password ({ ipc, key, explain, message }) {
const dialog = ansi.cross + ' ' + explain
const ask = 'Password'
const delim = ':'
const validation = (key) => key.length > 0
const msg = '\nPlease, enter a valid password.\n'
const result = await permit({ dialog, ask, delim, validation, msg })
await ipc.permit({ key, password: result.value })
print('\n' + ansi.tick + ' ' + message + '\n')
await ipc.close()
Bare.exit()
}

async function permit ({ dialog, ask, delim, validation, msg }) {
const interact = new Interact(dialog, [
{
name: 'value',
default: '',
prompt: ask,
delim,
validation,
msg
}
])
return interact.run()
}

module.exports = { usage, trust, password, stdio, ansi, indicator, status, print, byteDiff, diff, outputter }
43 changes: 16 additions & 27 deletions cmd/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use strict'
const { header, footer, command, flag, hiddenFlag, hiddenCommand, arg, summary, description, rest, bail } = require('paparam')
const { header, footer, command, flag, hiddenCommand, arg, summary, description, bail, sloppy } = require('paparam')
const { usage, print } = require('./iface')
const { CHECKOUT } = require('../constants')
const errors = require('../errors')
@@ -38,29 +38,12 @@ module.exports = async (ipc, argv = Bare.argv.slice(1)) => {

const dev = command(
'dev',
summary('Run a project in development mode'),
description(usage.descriptions.dev),
arg('[link|dir]', 'Source to run app from (default: .)'),
rest('[...app-args]', 'Application arguments'),
flag('--no-devtools', 'Open devtools with application [Desktop]'),
flag('--no-updates-diff', 'Enable diff computation for Pear.updates'),
flag('--no-updates', 'Disable updates firing via Pear.updates'),
flag('--link <url>', 'Simulate deep-link click open'),
flag('--store|-s <path>', 'Set the Application Storage path'),
flag('--tmp-store|-t', 'Automatic new tmp folder as store path'),
flag('--chrome-webrtc-internals', 'Enable chrome://webrtc-internals'),
flag('--unsafe-clear-app-storage', 'Clear app storage'),
flag('--unsafe-clear-preferences', 'Clear preferences (such as trustlist)'),
flag('--appling <path>', 'Set application shell path'),
flag('--checkout <n|release|staged>', 'Run a checkout from version length'),
flag('--detached', 'Wakeup existing app or run detached'),
hiddenFlag('--encryption-key <name>'), // internal temporarily
hiddenFlag('--detach'),
hiddenFlag('--trace <n>'),
hiddenFlag('--swap <path>'),
hiddenFlag('--start-id <id>'),
hiddenFlag('--no-sandbox'), // electron passthrough
(cmd) => runners.run(ipc)(cmd, true)
summary('pear dev has been deprecated, use pear run --dev instead.'),
sloppy({ args: true, flags: true }),
() => {
print('pear dev has been deprecated, use pear run --dev instead.', false)
ipc.close()
}
)

const seed = command(
@@ -71,7 +54,8 @@ module.exports = async (ipc, argv = Bare.argv.slice(1)) => {
arg('[dir]', 'Project directory path (default: .)'),
flag('--verbose|-v', 'Additional output'),
flag('--seeders|-s ', 'Additional public keys to seed from'),
flag('--name', 'Advanced. Override app name'),
flag('--name <name>', 'Advanced. Override app name'),
flag('--encryption-key <name>', 'Application encryption key'),
flag('--json', 'Newline delimited JSON output'),
runners.seed(ipc)
)
@@ -86,9 +70,10 @@ module.exports = async (ipc, argv = Bare.argv.slice(1)) => {
flag('--bare|-b', 'File data only, no warmup optimization'),
flag('--ignore <list>', 'Comma separated file path ignore list'),
flag('--truncate <n>', 'Advanced. Truncate to version length n'),
flag('--name', 'Advanced. Override app name'),
flag('--name <name>', 'Advanced. Override app name'),
flag('--json', 'Newline delimited JSON output'),
hiddenFlag('--encryption-key <name>'), // internal temporarily
flag('--encryption-key <name>', 'Application encryption key'),
flag('--no-ask', 'Suppress permissions dialogs'),
runners.stage(ipc)
)

@@ -122,6 +107,8 @@ module.exports = async (ipc, argv = Bare.argv.slice(1)) => {
flag('--metadata', 'View metadata only'),
flag('--key', 'View key only'),
flag('--json', 'Newline delimited JSON output'),
flag('--encryption-key <name>', 'Application encryption key'),
flag('--no-ask', 'Suppress permissions dialogs'),
runners.info(ipc)
)

@@ -132,6 +119,8 @@ module.exports = async (ipc, argv = Bare.argv.slice(1)) => {
arg('<dir>', 'Directory path to dump to, may be - for stdout'),
flag('--checkout <n>', 'Dump from specified checkout, n is version length'),
flag('--json', 'Newline delimited JSON output'),
flag('--encryption-key <name>', 'Application encryption key'),
flag('--no-ask', 'Suppress permissions dialogs'),
runners.dump(ipc)
)

17 changes: 14 additions & 3 deletions cmd/info.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ const { outputter } = require('./iface')
const os = require('bare-os')
const { isAbsolute, resolve } = require('bare-path')
const { ERR_INVALID_INPUT } = require('../errors')
const { password } = require('./iface')

const keys = ({ content, discovery, project }) => `
keys hex
@@ -40,11 +41,20 @@ const output = outputter('info', {
keys,
info,
changelog,
error: ({ code, stack }) => `Info Error (code: ${code || 'none'}) ${stack}`
error: (err, info, ipc) => {
if (err.info && err.info.encrypted && info.ask) {
const explain = 'This application is encrypted.\n' +
'\nEnter the password to retrieve info.\n\n'
const message = 'Added encryption key, run info again to complete it.'
return password({ ipc, key: err.info.key, explain, message })
} else {
return `Info Error (code: ${err.code || 'none'}) ${err.stack}`
}
}
})

module.exports = (ipc) => async function info (cmd) {
const { json, changelog, fullChangelog: full, metadata, key: showKey } = cmd.flags
const { json, changelog, fullChangelog: full, metadata, key: showKey, encryptionKey } = cmd.flags
const isKey = cmd.args.link && parseLink(cmd.args.link).drive.key !== null
const channel = isKey ? null : cmd.args.link
const link = isKey ? cmd.args.link : null
@@ -60,6 +70,7 @@ module.exports = (ipc) => async function info (cmd) {
metadata,
changelog,
full,
encryptionKey,
cmdArgs: Bare.argv.slice(1)
}))
}), { ask: cmd.flags.ask }, ipc)
}
4 changes: 2 additions & 2 deletions cmd/init.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
const fsp = require('bare-fs/promises')
const os = require('bare-os')
const { basename, resolve } = require('bare-path')
const { ansi, permit, outputter } = require('./iface')
const { ansi, trust, outputter } = require('./iface')

const output = outputter('init', {
writing: () => '',
@@ -44,7 +44,7 @@ module.exports = (ipc) => async function init (cmd) {
'\nType "TRUST" to allow template initialization or anything else to exit\n\n'
const ask = 'Trust template'
const act = 'Use pear init again to initalize from trusted template'
await permit({ ipc, key: err.info.key, message: err.message, explain, ask, act })
await trust({ ipc, key: err.info.key, message: err.message, explain, ask, act })
} finally {
await ipc.close()
}
22 changes: 19 additions & 3 deletions cmd/run.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'
const { outputter, permit, stdio } = require('./iface')
const { outputter, trust, stdio, password } = require('./iface')
const hypercoreid = require('hypercore-id-encoding')

const output = outputter('run', {
exit: ({ code }) => Bare.exit(code),
@@ -21,7 +22,22 @@ module.exports = (ipc) => async function run (cmd, devrun = false) {
const appArgs = cmd.rest || []
await output(json, await require('../run')({ flags: cmd.flags, link: cmd.args.link, indices: cmd.indices, appArgs, ipc, args, cmdArgs: Bare.argv.slice(1), storage: store, detached }))
} catch (err) {
if (err.code !== 'ERR_PERMISSION_REQUIRED') throw err
await permit({ ipc, key: err.info.key, message: err.message })
if (err.code === 'ERR_PERMISSION_REQUIRED' && cmd.flags.ask) {
if (!err.info.encrypted) {
const explain = 'Be sure that software is trusted before running it\n' +
'\nType "TRUST" to allow execution or anything else to exit\n\n'
const act = 'Use pear run again to execute trusted application\n'
const ask = 'Trust application'
await trust({ ipc, key: err.info.key, message: err.message, explain, act, ask })
} else {
const z32 = hypercoreid.normalize(err.info.key)
const explain = 'pear://' + z32 + ' is an encrypted application. \n' +
'\nEnter the password to run the app.\n\n'
const message = 'Added encryption key for pear://' + z32
await password({ ipc, key: err.info.key, explain, message })
}
} else {
throw err
}
}
}
5 changes: 3 additions & 2 deletions cmd/seed.js
Original file line number Diff line number Diff line change
@@ -23,11 +23,12 @@ module.exports = (ipc) => async function seed (cmd) {
const isKey = parseLink(cmd.args.channel).drive.key !== null
const channel = isKey ? null : cmd.args.channel
const link = isKey ? cmd.args.channel : null
let { name } = cmd.flags
let { name, encryptionKey } = cmd.flags
if (!name && !link) {
const pkg = JSON.parse(await readFile(join(dir, 'package.json')))
name = pkg.pear?.name || pkg.name
}
const id = Bare.pid
await output(json, ipc.seed({ id, name, channel, link, verbose, seeders, dir, cmdArgs: Bare.argv.slice(1) }))

await output(json, ipc.seed({ id, name, channel, link, verbose, seeders, dir, encryptionKey, cmdArgs: Bare.argv.slice(1) }))
}
14 changes: 12 additions & 2 deletions cmd/stage.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ const { isAbsolute, resolve } = require('bare-path')
const { outputter, ansi } = require('./iface')
const parseLink = require('../lib/parse-link')
const { ERR_INVALID_INPUT } = require('../errors')
const { password } = require('./iface')

let blocks = 0
let total = 0
@@ -20,7 +21,16 @@ const output = outputter('stage', {
const message = (data.success ? 'Warmed' : 'Warming') + ' up app (used ' + blocks + '/' + total + ' blocks) ' // Adding a space as a hack for an issue with the outputter which duplicates the last char on done
return { output: 'status', message }
},
error: ({ code, stack }) => `Staging Error (code: ${code || 'none'}) ${stack}`,
error: async (err, info, ipc) => {
if (err.info && err.info.encrypted && info.ask) {
const explain = 'This application is encrypted.\n' +
'\nEnter the password to stage the app.\n\n'
const message = 'Added encryption key, run stage again to complete it.'
return password({ ipc, key: err.info.key, explain, message })
} else {
return `Staging Error (code: ${err.code || 'none'}) ${err.stack}`
}
},
addendum: ({ version, release, channel, link }) => `Latest version is now ${version} with release set to ${release}\n\nUse \`pear release ${channel}\` to set release to latest version\n\n[ ${ansi.dim(link)} ]\n`
})

@@ -33,5 +43,5 @@ module.exports = (ipc) => async function stage (cmd) {
let { dir = os.cwd() } = cmd.args
if (isAbsolute(dir) === false) dir = dir ? resolve(os.cwd(), dir) : os.cwd()
const id = Bare.pid
await output(json, ipc.stage({ id, channel, key, dir, encryptionKey, dryRun, bare, ignore, name, truncate, cmdArgs: Bare.argv.slice(1) }))
await output(json, ipc.stage({ id, channel, key, dir, encryptionKey, dryRun, bare, ignore, name, truncate, cmdArgs: Bare.argv.slice(1) }), { ask: cmd.flags.ask }, ipc)
}
2 changes: 2 additions & 0 deletions constants.js
Original file line number Diff line number Diff line change
@@ -72,6 +72,8 @@ exports.DESKTOP_RUNTIME = toPath(new URL(BIN + DESKTOP_EXEC, swapURL))

exports.BARE_RESTART_EXIT_CODE = 75

exports.SALT = b4a.from('d134aa8b0631f1193b5031b356d82dbea214389208fa4a0bcdf5c2e062d8ced2', 'hex')

function electronModuleURL () {
const u = pathToFileURL(process.execPath)
const i = u.href.lastIndexOf(BIN)
Loading