dashcam 0.3.2 → 0.4.0

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
@@ -1,113 +1,58 @@
1
- <img src="https://user-images.githubusercontent.com/318295/204898620-922afee0-5415-46a9-a84f-ae6237001bf0.png" height="50" alt="Dashcam"/>
1
+ <img src="https://github.com/replayableio/cli/assets/318295/c8019bca-e4d8-42a2-af55-0f4c83ce133e" height="60px" />
2
2
 
3
- # Dashcam API
3
+ # Dashcam CLI
4
4
 
5
- Add Dashcam to your app or workflow. This package allows you to control the Dashcam desktop application from the via CLI SDK.
5
+ Add Dashcam to your app or workflow. This package allows you to capture logs and control the Dashcam desktop application via CLI.
6
6
 
7
- > Why double-back when you can capture it the first time? Playback and share exactly what happened with Dashcam's desktop replay buffer.
8
-
9
- You can easily embed desktop replays within git commits, pull requests, bug reports, jira tickets, and even within log files. Desktop replays are a great way to share context behind problems and document the application state within logs, tickets and more.
10
-
11
- Requires that you [install Dashcam Desktop](https://dashcam.io). Dashcam Desktop runs in the background giving you access to a buffer of video.
7
+ Requires that you [install Dashcam Desktop](https://dashcam.io).
12
8
 
13
9
  ## Table of contents
14
10
 
15
- - [Dashcam API](#dashcam-api)
16
- - [Install Dashcam Desktop](#install-dashcam-desktop)
17
- - [Examples](#examples)
18
- - [Web](#web)
19
- - [Setup](#setup)
20
- - [HTML Anchor Tag](#html-anchor-tag)
21
- - [JS Error Handler](#js-error-handler)
22
- - [NodeJS SDK](#nodejs-sdk)
23
- - [Setup](#setup)
24
- - [Create a Replay](#create-a-replay)
25
- - [Error Handler](#error-handler)
26
- - [CLI](#cli)
27
- - [Setup](#setup)
28
- - [Create a Replay](#create-a-replay)
29
- - [Return a rich markdown link](#return-a-rich-markdown-link)
30
- - [Set a replay title](#set-a-replay-title)
31
- - [Attach the last 20 CLI commands to the replay](#attach-the-last-20-cli-commands-to-the-replay)
32
- - [Attach a logfile to the replay](#attach-a-logfile-to-the-replay)
33
- - [GitHub CLI](#github-cli)
34
- - [Create a github issue with a replay in the description](#create-a-github-issue-with-a-replay-in-the-description)
35
- - [Create a github pull request with a replay in the description](#create-a-github-pull-request-with-a-replay-in-the-description)
36
- - [Append a 30 second replay to a commit](#append-a-30-second-replay-to-a-commit)
37
- - [Advanced Usage](#advanced-usage)
38
- - [Ideas](#ideas)
11
+ - [CLI](#cli)
12
+ - [Setup](#setup)
13
+ - [Record CLI](#record-cli)
14
+ - [Create a Dash](#create-a-replay)
15
+ - [Return a rich markdown link](#return-a-rich-markdown-link)
16
+ - [Set a Dash title](#set-a-replay-title)
17
+ - [Attach the last 20 CLI commands to the Dash](#attach-the-last-20-cli-commands-to-the-replay)
18
+ - [Attach a logfile to the replay](#attach-a-logfile-to-the-replay)
19
+ - [Web SDK](#web)
20
+ - [Setup](#setup)
21
+ - [HTML Anchor Tag](#html-anchor-tag)
22
+ - [JS Error Handler](#js-error-handler)
23
+ - [NodeJS SDK](#nodejs-sdk)
24
+ - [Setup](#setup)
25
+ - [Create a Dash](#create-a-replay)
26
+ - [Error Handler](#error-handler)
27
+ - [GitHub CLI](#github-cli)
28
+ - [Create a GitHub issue with a Dash in the description](#create-a-github-issue-with-a-replay-in-the-description)
29
+ - [Create a GitHub pull request with a Dash in the description](#create-a-github-pull-request-with-a-replay-in-the-description)
30
+ - [Append a 30 second Dash to a commit](#append-a-30-second-replay-to-a-commit)
39
31
 
40
32
  # Examples
41
33
 
42
34
  Also see [the examples folder](https://github.com/replayableio/cli/tree/main/examples).
43
35
 
44
- ## Web
45
-
46
- ### Setup
47
-
48
- Nothing! The app exposes the protocol to the system natively via `dashcam://`.
49
-
50
- ### HTML Anchor Tag
51
-
52
- ```html
53
- <a href="dashcam://replay/create" target="_blank">Create a Replay</a>
54
- ```
55
-
56
- ### JS Error Handler
57
-
58
- ```js
59
- window.onerror = function myErrorHandler() {
60
- window.open("dashcam://replay/create", "_blank");
61
- };
62
-
63
- setTimeout(() => {
64
- throw new Error("Throw makes it go boom!");
65
- }, 3000);
66
- ```
67
-
68
- ## NodeJS SDK
36
+ ## CLI
69
37
 
70
38
  ### Setup
71
39
 
72
40
  ```sh
73
- npm install dashcam
41
+ npm install dashcam -g
74
42
  ```
75
43
 
76
- ### Create a Replay
44
+ ## Record CLI
77
45
 
78
- ```js
79
- const dashcam = require("dashcam");
46
+ To record the CLI in the Dashcam app, use the following command
80
47
 
81
- let replay = await dashcam.createReplay({
82
- title: "My New Replay",
83
- description: `This **renders markdown** or plaintext in monospace font.`,
84
- });
85
48
  ```
86
-
87
- ### Error Handler
88
-
89
- ```js
90
- const dashcam = require("dashcam");
91
-
92
- process.on("uncaughtException", async (err) => {
93
- let replay = await dashcam.createReplay({
94
- title: "uncaughtException",
95
- description: err,
96
- });
97
- console.log("Dashcam", replay);
98
- });
99
-
100
- setTimeout(() => {
101
- throw new Error("Throw makes it go boom!");
102
- }, 3000);
49
+ dashcam record
103
50
  ```
104
51
 
105
- ## CLI
106
-
107
- ### Setup
52
+ Anything you type in your terminal will appear in your dash. To exit, simply type `exit`.
108
53
 
109
- ```sh
110
- npm install dashcam -g
54
+ ```
55
+ exit
111
56
  ```
112
57
 
113
58
  ### Create a Replay
@@ -189,6 +134,67 @@ Options:
189
134
  -h, --help display help for command
190
135
  ```
191
136
 
137
+ ## Web
138
+
139
+ ### Setup
140
+
141
+ Nothing! The app exposes the protocol to the system natively via `dashcam://`.
142
+
143
+ ### HTML Anchor Tag
144
+
145
+ ```html
146
+ <a href="dashcam://replay/create" target="_blank">Create a Replay</a>
147
+ ```
148
+
149
+ ### JS Error Handler
150
+
151
+ ```js
152
+ window.onerror = function myErrorHandler() {
153
+ window.open("dashcam://replay/create", "_blank");
154
+ };
155
+
156
+ setTimeout(() => {
157
+ throw new Error("Throw makes it go boom!");
158
+ }, 3000);
159
+ ```
160
+
161
+ ## NodeJS SDK
162
+
163
+ ### Setup
164
+
165
+ ```sh
166
+ npm install dashcam
167
+ ```
168
+
169
+ ### Create a Replay
170
+
171
+ ```js
172
+ const dashcam = require("dashcam");
173
+
174
+ let replay = await dashcam.createReplay({
175
+ title: "My New Replay",
176
+ description: `This **renders markdown** or plaintext in monospace font.`,
177
+ });
178
+ ```
179
+
180
+ ### Error Handler
181
+
182
+ ```js
183
+ const dashcam = require("dashcam");
184
+
185
+ process.on("uncaughtException", async (err) => {
186
+ let replay = await dashcam.createReplay({
187
+ title: "uncaughtException",
188
+ description: err,
189
+ });
190
+ console.log("Dashcam", replay);
191
+ });
192
+
193
+ setTimeout(() => {
194
+ throw new Error("Throw makes it go boom!");
195
+ }, 3000);
196
+ ```
197
+
192
198
  ## Ideas
193
199
 
194
200
  It would be possible to string this along in [a git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) to publish with every commit.
@@ -1,8 +1,8 @@
1
- const replayable = require("./index");
1
+ const replayable = require("../index");
2
2
 
3
3
  process.on("uncaughtException", async (err) => {
4
4
  console.error(err);
5
- let replay = await replayable.createReplay();
5
+ let replay = await replayable.createReplay({ description: err.stack });
6
6
  console.log("Replayable", replay);
7
7
  });
8
8
 
package/index.js CHANGED
@@ -1,6 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const fs = require("fs");
4
+ const crypto = require("crypto");
3
5
  const lib = require("./lib");
6
+ const Recorder = require("./recorder");
7
+
8
+ if (module.parent) {
9
+ module.exports = lib;
10
+ return;
11
+ }
12
+
4
13
  const { program } = require("commander");
5
14
 
6
15
  let stdin = "";
@@ -47,6 +56,26 @@ program
47
56
  process.exit(0);
48
57
  });
49
58
 
59
+ program
60
+ .command("record")
61
+ .description(
62
+ "Start a recording terminal to be included in your dashcam video recording"
63
+ )
64
+ .action(async function (str, options) {
65
+ try {
66
+ const dashcam = new lib.PersistantDashcamIPC();
67
+ const id = crypto.randomUUID();
68
+ const logFile = lib.getLogFilePath(id);
69
+
70
+ dashcam.onConnected = () => dashcam.emit("track-cli", logFile);
71
+ fs.appendFileSync(logFile, "");
72
+ const recorder = new Recorder(logFile);
73
+ recorder.start();
74
+ } catch (e) {
75
+ console.log("Error: ", e);
76
+ }
77
+ });
78
+
50
79
  if (process.stdin.isTTY) {
51
80
  program.parse(process.argv);
52
81
  } else {
@@ -62,7 +91,3 @@ if (process.stdin.isTTY) {
62
91
  program.parse(process.argv);
63
92
  });
64
93
  }
65
-
66
- if (module.parent) {
67
- module.exports = lib;
68
- }
package/lib.js CHANGED
@@ -1,11 +1,17 @@
1
- const ipc = require("node-ipc").default;
1
+ const os = require("os");
2
+ const path = require("path");
2
3
  const clc = require("cli-color");
4
+ const ipc = require("node-ipc").default;
3
5
 
4
6
  ipc.config.id = "dashcam-cli";
5
7
  ipc.config.retry = 1500;
6
8
  ipc.config.silent = true;
7
9
  ipc.config.maxRetries = 0;
8
10
 
11
+ const persistantIPC = new ipc.IPC();
12
+ persistantIPC.config.retry = 500;
13
+ persistantIPC.config.silent = true;
14
+
9
15
  const connectToIpc = function () {
10
16
  return new Promise((resolve, reject) => {
11
17
  ipc.connectTo("dashcam");
@@ -61,4 +67,42 @@ const createReplay = async function (options = {}) {
61
67
  });
62
68
  };
63
69
 
64
- module.exports = { createReplay };
70
+ let singleInstance = null;
71
+ class PersistantDashcamIPC {
72
+ #isConnected = false;
73
+ onConnected = null;
74
+
75
+ constructor() {
76
+ if (singleInstance) return singleInstance;
77
+ singleInstance = this;
78
+
79
+ persistantIPC.connectTo("dashcam");
80
+
81
+ persistantIPC.of.dashcam.on("connect", () => {
82
+ this.#isConnected = true;
83
+ if (this.onConnected && typeof this.onConnected === "function")
84
+ this.onConnected();
85
+ });
86
+ persistantIPC.of.dashcam.on("disconnect", () => {
87
+ this.#isConnected = false;
88
+ });
89
+ persistantIPC.of.dashcam.on("error", () => {
90
+ this.#isConnected = false;
91
+ });
92
+ }
93
+
94
+ emit(event, payload) {
95
+ if (!this.#isConnected) return;
96
+ persistantIPC.of.dashcam.emit(event, payload);
97
+ }
98
+ }
99
+
100
+ const getLogFilePath = (id) => {
101
+ return path.join(os.tmpdir(), `dashcam_cli_recording_${id}.log`);
102
+ };
103
+
104
+ module.exports = {
105
+ createReplay,
106
+ getLogFilePath,
107
+ PersistantDashcamIPC,
108
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Fix bugs, close pulls, and update your team with desktop instant replay.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,6 +17,7 @@
17
17
  "commander": "^9.4.0",
18
18
  "husky": "^7.0.4",
19
19
  "node-ipc": "^10.1.0",
20
+ "node-pty-prebuilt-multiarch": "^0.10.1-pre.5",
20
21
  "yargs": "^17.3.1"
21
22
  },
22
23
  "devDependencies": {
package/recorder.js ADDED
@@ -0,0 +1,66 @@
1
+ const fs = require("fs");
2
+ const pty = require("node-pty-prebuilt-multiarch");
3
+
4
+ class Recorder {
5
+ #ptyProcess = null;
6
+ #logFile = null;
7
+
8
+ constructor(logFile) {
9
+ // This way we don't run the recording script recursively, especially
10
+ // if it's inside bash/zsh configs
11
+ if (process.env.DASHCAM_TERMINAL_RECORDING) {
12
+ console.log("The current terminal is already being recorded");
13
+ process.exit(0);
14
+ }
15
+ this.#logFile = logFile;
16
+ }
17
+
18
+ #onInput(data) {
19
+ this.#ptyProcess.write(data);
20
+ }
21
+
22
+ #onData(data) {
23
+ process.stdout.write(data);
24
+ fs.appendFileSync(this.#logFile, data, "ascii");
25
+ }
26
+
27
+ start() {
28
+ console.log(
29
+ "This session is being recorded by Dashcam and dumped to",
30
+ this.#logFile
31
+ );
32
+ console.log("Type `exit` to stop recording");
33
+
34
+ // TODO: Find a way to consistently get the current shell this is running from
35
+ // instead of using the default user shell (Maybe use parent processId to find
36
+ // the process filepath)
37
+ const shell = process.env.SHELL;
38
+ const args = [];
39
+ if (!shell.toLowerCase().includes("powershell")) args.push("-l");
40
+ this.#ptyProcess = pty.spawn(shell, args, {
41
+ // Inject a terminal variable to let the child processes know
42
+ // of the active recording so they we don't record recursively
43
+ env: { ...process.env, DASHCAM_TERMINAL_RECORDING: "TRUE" },
44
+ });
45
+
46
+ process.stdin.on("data", this.#onInput.bind(this));
47
+ this.#ptyProcess.on("data", this.#onData.bind(this));
48
+ this.#ptyProcess.on("exit", this.stop.bind(this));
49
+
50
+ process.stdout.setDefaultEncoding("utf8");
51
+ process.stdin.setEncoding("utf8");
52
+ process.stdin.setRawMode(true);
53
+ process.stdin.resume();
54
+ }
55
+
56
+ stop() {
57
+ process.stdin.removeListener("data", this.#onInput.bind(this));
58
+ process.stdin.setRawMode(false);
59
+ process.stdin.pause();
60
+ console.clear();
61
+ process.kill(process.ppid, "SIGTERM");
62
+ process.exit();
63
+ }
64
+ }
65
+
66
+ module.exports = Recorder;