Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ To use the Acurast CLI, type `acurast` followed by any of the available options
- `deployments update editor <deployment-id> <new-editor-address> [options]` - Transfer editor permissions for a mutable deployment.
- `live [options] [project]` - Setup a "live-code-processor" and run your project on the processor in real time.
- `init` - Create an acurast.json file and .env file.
- `devtools <deployment-id>` - Request a DevTools view key and print the URL for a deployment.
- `open` - Open the Acurast resources in your browser.
- `help [command]` - Display help for command.

Expand Down Expand Up @@ -239,6 +240,48 @@ When running `acurast deploy`, the environment variables will now automatically

When running interval based deployments with multiple executions, the environment variables can be updated between executions. To do that, update the `.env` file and run `acurast deployments <id> -e`. This will update the environment variables for the deployment with the given ID.

## DevTools

Acurast DevTools lets you see live `console.log`, `console.warn`, `console.error`, `console.info`, and `console.debug` output from your processors in a web dashboard.

### Setup

Add `"enableDevtools": true` to your project config in `acurast.json`:

```json
{
"projects": {
"my-project": {
"projectName": "my-project",
"fileUrl": "dist/bundle.js",
"enableDevtools": true,
...
}
}
}
```

Then deploy as usual with `acurast deploy my-project`. After deployment, the CLI prints a DevTools URL with a view key — open it to see your logs.

### Requesting a new view key

View keys are time-limited. If yours has expired, request a new one:

```bash
acurast devtools <deployment-id>
```

### Privacy

Logs are only accessible with a valid view key. The key is scoped to the specific deployment and only the deployment owner can request new keys.

### Environment variables

| Variable | Default | Description |
|---|---|---|
| `ACURAST_DEVTOOLS_URL` | `https://devtools.acurast.com` | DevTools frontend URL |
| `ACURAST_DEVTOOLS_API_URL` | `https://api.devtools.acurast.com` | DevTools API URL |

## Deployment Management

The Acurast CLI provides comprehensive deployment management capabilities, including the ability to update mutable deployments and transfer editor permissions.
Expand Down
26 changes: 26 additions & 0 deletions acurast.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,32 @@
"maxCostPerExecution": 1000000000,
"includeEnvironmentVariables": [],
"processorWhitelist": []
},
"test-devtools": {
"projectName": "test-devtools",
"fileUrl": "examples/devtools-test.js",
"network": "mainnet",
"onlyAttestedDevices": true,
"enableDevtools": true,
"assignmentStrategy": {
"type": "Single"
},
"execution": {
"type": "onetime",
"maxExecutionTimeInMs": 60000
},
"maxAllowedStartDelayInMs": 10000,
"usageLimit": {
"maxMemory": 0,
"maxNetworkRequests": 0,
"maxStorage": 0
},
"numberOfReplicas": 20,
"requiredModules": [],
"minProcessorReputation": 0,
"maxCostPerExecution": 3000000000,
"includeEnvironmentVariables": [],
"processorWhitelist": []
}
}
}
27 changes: 27 additions & 0 deletions examples/devtools-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Test script that logs various things for ~1 minute, then exits.

console.log("Devtools test started", { jobId: _STD_.job.getId(), device: _STD_.device.getAddress() })
console.info("Processor info", { timestamp: Date.now() })

let tick = 0
const interval = setInterval(() => {
tick++
console.log("Tick " + tick, { elapsed: tick * 5 + "s" })

if (tick % 3 === 0) {
console.warn("Warning at tick " + tick + ": this is a test warning")
}

if (tick % 5 === 0) {
try {
JSON.parse("not valid json {{{")
} catch (e) {
console.error("Caught error at tick " + tick + ":", e.message)
}
}

if (tick >= 12) {
clearInterval(interval)
console.log("Devtools test complete after " + tick + " ticks")
}
}, 5000)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@acurast/cli",
"version": "0.6.0",
"version": "0.7.0",
"description": "A cli to interact with the Acurast Cloud.",
"main": "dist/index.js",
"bin": {
Expand Down
21 changes: 20 additions & 1 deletion src/acurast/createJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import { filelogger } from '../util/fileLogger.js'
import { zipFolder } from '../util/zipFolder.js'
import { createManifest } from '../util/createManifest.js'
import { checkIsFolder } from '../util/checkIsFolder.js'
import { basename } from 'node:path'
import { basename, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { injectDevtoolsSnippet } from '../devtools/injectDevtoolsSnippet.js'
import { getEnv } from '../config.js'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const BUNDLE_FOLDER = '.acurast/bundles'

Expand Down Expand Up @@ -71,6 +77,19 @@ export const createJob = async (
config.projectName
)

if (config.enableDevtools) {
const devtoolsApiUrl = getEnv('ACURAST_DEVTOOLS_API_URL')
const snippetDir = join(__dirname, '..', 'devtools')
zipPath = await injectDevtoolsSnippet(
zipPath,
config.entrypoint ?? basename(config.fileUrl),
devtoolsApiUrl,
wallet.address,
snippetDir
)
filelogger.debug('Devtools snippet injected into bundle')
}

filelogger.log(`zipPath ${zipPath}`)

ipfsHash = await uploadScript({ file: zipPath })
Expand Down
26 changes: 26 additions & 0 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { fetchAndDisplayPricing } from '../util/fetchPricingAdvice.js'
import { BigNumber } from 'bignumber.js'
import { confirm, select, input } from '@inquirer/prompts'
import { AssignmentStrategyVariant } from '../types.js'
import { getDevtoolsViewKey, buildDevtoolsUrl } from '../devtools/devtoolsApi.js'

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

Expand Down Expand Up @@ -480,6 +481,7 @@ export const addCommandDeploy = (program: Command) => {

const deploymentTime = new Date()
let jobRegistrationTemp: JobRegistration | undefined = undefined
let deployedJobId: string | undefined = undefined

const job = convertConfigToJob(config)

Expand Down Expand Up @@ -513,6 +515,7 @@ export const addCommandDeploy = (program: Command) => {
if (!jobRegistrationTemp) {
throw new Error('Deployment Registration is null!')
}
deployedJobId = String(data.jobIds[0]?.[1] ?? data.jobIds[0])
await storeDeployment(
deploymentTime,
originalConfig,
Expand Down Expand Up @@ -790,6 +793,29 @@ export const addCommandDeploy = (program: Command) => {
try {
await tasks.run()

if (config.enableDevtools && deployedJobId) {
try {
const viewKeyResponse = await getDevtoolsViewKey(deployedJobId)
log('')
log(
`DevTools: ${toAcurastColor(
buildDevtoolsUrl(deployedJobId, viewKeyResponse.viewKey)
)}`
)
log(
`View key expires at ${new Date(viewKeyResponse.expiresAt).toLocaleString()}`
)
log('')
} catch (e: any) {
filelogger.error(
`Failed to get devtools view key: ${e.message}`
)
log(
`Warning: Could not retrieve DevTools view key: ${e.message}`
)
}
}

process.exit(0)
} catch (e) {
console.log('Error', e)
Expand Down
25 changes: 25 additions & 0 deletions src/commands/devtools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Command } from 'commander'
import { getDevtoolsViewKey, buildDevtoolsUrl } from '../devtools/devtoolsApi.js'
import { acurastColor } from '../util.js'

export const addCommandDevtools = (program: Command) => {
program
.command('devtools <deployment-id>')
.description(
'Request a DevTools view key for a deployment and print the URL.'
)
.action(async (deploymentId: string) => {
const viewKeyResponse = await getDevtoolsViewKey(deploymentId)

console.log('')
console.log(
`DevTools: ${acurastColor(
buildDevtoolsUrl(deploymentId, viewKeyResponse.viewKey)
)}`
)
console.log(
`View key expires at ${new Date(viewKeyResponse.expiresAt).toLocaleString()}`
)
console.log('')
})
}
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ const INDEXER_MAINNET_API_KEY = 'HbLxqSJoPTnzwa_rkF-tYv'

const IPFS_PROXY = 'https://ipfs-proxy.acurast.prod.gke.papers.tech'

const DEVTOOLS_URL = 'https://devtools.acurast.com'
const DEVTOOLS_API_URL = 'https://api.devtools.acurast.com'

export type EnvKeys =
| 'ACURAST_MNEMONIC'
| 'ACURAST_IPFS_URL'
| 'ACURAST_IPFS_API_KEY'
| 'ACURAST_RPC'
| 'ACURAST_CANARY_RPC'
| 'ACURAST_DEVTOOLS_URL'
| 'ACURAST_DEVTOOLS_API_URL'
| 'DEBUG'

const defaultValues: Record<EnvKeys, string | undefined> = {
Expand All @@ -29,6 +34,8 @@ const defaultValues: Record<EnvKeys, string | undefined> = {
ACURAST_IPFS_API_KEY: '', // With the default IPFS Proxy, no API key is required
ACURAST_RPC: RPC_MAINNET,
ACURAST_CANARY_RPC: RPC_CANARY,
ACURAST_DEVTOOLS_URL: DEVTOOLS_URL,
ACURAST_DEVTOOLS_API_URL: DEVTOOLS_API_URL,
DEBUG: 'false',
}

Expand Down
25 changes: 25 additions & 0 deletions src/devtools/acurast-processor.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Type declarations for Acurast processor runtime globals

declare function httpPOST(
url: string,
body: string,
headers: Record<string, string>,
onSuccess: (response: string, certificate: string) => void,
onError: (error: string) => void
): void

declare const _STD_: {
job: {
getId(): { origin: { kind: string; source: string }; id: string }
getPublicKeys(): { p256: string; secp256k1: string; ed25519: string }
}
device: {
getAddress(): string
}
env: Record<string, string>
signers: {
ed25519: {
sign(payloadHex: string): string
}
}
}
Loading
Loading