t3-discord-presence 0.1.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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/cli/diagnostics.d.ts +10 -0
  4. package/dist/cli/diagnostics.d.ts.map +1 -0
  5. package/dist/cli/diagnostics.js +52 -0
  6. package/dist/cli/diagnostics.js.map +1 -0
  7. package/dist/cli/help.d.ts +3 -0
  8. package/dist/cli/help.d.ts.map +1 -0
  9. package/dist/cli/help.js +43 -0
  10. package/dist/cli/help.js.map +1 -0
  11. package/dist/cli/lifecycle.d.ts +64 -0
  12. package/dist/cli/lifecycle.d.ts.map +1 -0
  13. package/dist/cli/lifecycle.js +118 -0
  14. package/dist/cli/lifecycle.js.map +1 -0
  15. package/dist/cli/prompt.d.ts +7 -0
  16. package/dist/cli/prompt.d.ts.map +1 -0
  17. package/dist/cli/prompt.js +37 -0
  18. package/dist/cli/prompt.js.map +1 -0
  19. package/dist/cli/purge.d.ts +28 -0
  20. package/dist/cli/purge.d.ts.map +1 -0
  21. package/dist/cli/purge.js +114 -0
  22. package/dist/cli/purge.js.map +1 -0
  23. package/dist/cli/run.d.ts +52 -0
  24. package/dist/cli/run.d.ts.map +1 -0
  25. package/dist/cli/run.js +544 -0
  26. package/dist/cli/run.js.map +1 -0
  27. package/dist/cli.d.ts +3 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +4 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/config/config.d.ts +23 -0
  32. package/dist/config/config.d.ts.map +1 -0
  33. package/dist/config/config.js +131 -0
  34. package/dist/config/config.js.map +1 -0
  35. package/dist/config/credentials.d.ts +34 -0
  36. package/dist/config/credentials.d.ts.map +1 -0
  37. package/dist/config/credentials.js +271 -0
  38. package/dist/config/credentials.js.map +1 -0
  39. package/dist/config/paths.d.ts +22 -0
  40. package/dist/config/paths.d.ts.map +1 -0
  41. package/dist/config/paths.js +79 -0
  42. package/dist/config/paths.js.map +1 -0
  43. package/dist/daemon/backoff.d.ts +4 -0
  44. package/dist/daemon/backoff.d.ts.map +1 -0
  45. package/dist/daemon/backoff.js +36 -0
  46. package/dist/daemon/backoff.js.map +1 -0
  47. package/dist/daemon/daemon.d.ts +53 -0
  48. package/dist/daemon/daemon.d.ts.map +1 -0
  49. package/dist/daemon/daemon.js +285 -0
  50. package/dist/daemon/daemon.js.map +1 -0
  51. package/dist/daemon/lock.d.ts +43 -0
  52. package/dist/daemon/lock.d.ts.map +1 -0
  53. package/dist/daemon/lock.js +229 -0
  54. package/dist/daemon/lock.js.map +1 -0
  55. package/dist/daemon/status.d.ts +35 -0
  56. package/dist/daemon/status.d.ts.map +1 -0
  57. package/dist/daemon/status.js +127 -0
  58. package/dist/daemon/status.js.map +1 -0
  59. package/dist/daemon/t3-connection.d.ts +37 -0
  60. package/dist/daemon/t3-connection.d.ts.map +1 -0
  61. package/dist/daemon/t3-connection.js +377 -0
  62. package/dist/daemon/t3-connection.js.map +1 -0
  63. package/dist/discord/activity.d.ts +11 -0
  64. package/dist/discord/activity.d.ts.map +1 -0
  65. package/dist/discord/activity.js +144 -0
  66. package/dist/discord/activity.js.map +1 -0
  67. package/dist/discord/application.d.ts +2 -0
  68. package/dist/discord/application.d.ts.map +1 -0
  69. package/dist/discord/application.js +2 -0
  70. package/dist/discord/application.js.map +1 -0
  71. package/dist/discord/client.d.ts +21 -0
  72. package/dist/discord/client.d.ts.map +1 -0
  73. package/dist/discord/client.js +244 -0
  74. package/dist/discord/client.js.map +1 -0
  75. package/dist/discord/types.d.ts +11 -0
  76. package/dist/discord/types.d.ts.map +1 -0
  77. package/dist/discord/types.js +2 -0
  78. package/dist/discord/types.js.map +1 -0
  79. package/dist/logging/logger.d.ts +30 -0
  80. package/dist/logging/logger.d.ts.map +1 -0
  81. package/dist/logging/logger.js +285 -0
  82. package/dist/logging/logger.js.map +1 -0
  83. package/dist/startup/index.d.ts +43 -0
  84. package/dist/startup/index.d.ts.map +1 -0
  85. package/dist/startup/index.js +138 -0
  86. package/dist/startup/index.js.map +1 -0
  87. package/dist/startup/linux.d.ts +61 -0
  88. package/dist/startup/linux.d.ts.map +1 -0
  89. package/dist/startup/linux.js +287 -0
  90. package/dist/startup/linux.js.map +1 -0
  91. package/dist/startup/macos.d.ts +51 -0
  92. package/dist/startup/macos.d.ts.map +1 -0
  93. package/dist/startup/macos.js +195 -0
  94. package/dist/startup/macos.js.map +1 -0
  95. package/dist/startup/unix.d.ts +32 -0
  96. package/dist/startup/unix.d.ts.map +1 -0
  97. package/dist/startup/unix.js +148 -0
  98. package/dist/startup/unix.js.map +1 -0
  99. package/dist/startup/windows.d.ts +98 -0
  100. package/dist/startup/windows.d.ts.map +1 -0
  101. package/dist/startup/windows.js +526 -0
  102. package/dist/startup/windows.js.map +1 -0
  103. package/dist/t3/activity.d.ts +16 -0
  104. package/dist/t3/activity.d.ts.map +1 -0
  105. package/dist/t3/activity.js +92 -0
  106. package/dist/t3/activity.js.map +1 -0
  107. package/dist/t3/auth.d.ts +70 -0
  108. package/dist/t3/auth.d.ts.map +1 -0
  109. package/dist/t3/auth.js +350 -0
  110. package/dist/t3/auth.js.map +1 -0
  111. package/dist/t3/cli.d.ts +38 -0
  112. package/dist/t3/cli.d.ts.map +1 -0
  113. package/dist/t3/cli.js +264 -0
  114. package/dist/t3/cli.js.map +1 -0
  115. package/dist/t3/discovery.d.ts +23 -0
  116. package/dist/t3/discovery.d.ts.map +1 -0
  117. package/dist/t3/discovery.js +250 -0
  118. package/dist/t3/discovery.js.map +1 -0
  119. package/dist/t3/rpc.d.ts +55 -0
  120. package/dist/t3/rpc.d.ts.map +1 -0
  121. package/dist/t3/rpc.js +326 -0
  122. package/dist/t3/rpc.js.map +1 -0
  123. package/dist/t3/state.d.ts +54 -0
  124. package/dist/t3/state.d.ts.map +1 -0
  125. package/dist/t3/state.js +337 -0
  126. package/dist/t3/state.js.map +1 -0
  127. package/dist/t3/types.d.ts +42 -0
  128. package/dist/t3/types.d.ts.map +1 -0
  129. package/dist/t3/types.js +2 -0
  130. package/dist/t3/types.js.map +1 -0
  131. package/dist/utils/atomic-file.d.ts +6 -0
  132. package/dist/utils/atomic-file.d.ts.map +1 -0
  133. package/dist/utils/atomic-file.js +46 -0
  134. package/dist/utils/atomic-file.js.map +1 -0
  135. package/dist/utils/presence-text.d.ts +2 -0
  136. package/dist/utils/presence-text.d.ts.map +1 -0
  137. package/dist/utils/presence-text.js +16 -0
  138. package/dist/utils/presence-text.js.map +1 -0
  139. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 f3tch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # t3-discord-presence
2
+
3
+ `t3-discord-presence` is a small background daemon that shows current T3 Code agent activity as Discord Rich Presence. It starts at user login, waits quietly while T3 Code or Discord is closed, and reconnects when either application returns.
4
+
5
+ It does not modify or inject into T3 Code, inspect the screen, or read process memory. It discovers T3 Code's loopback server, authenticates through T3's supported pairing flow with read-only access, subscribes to the local state API, reduces that state to privacy-safe labels, and sends the result to the Discord desktop application's local RPC interface.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 22 or newer. The Node.js 22 and 24 LTS lines are tested in CI.
10
+ - T3 Code Desktop. For first-time authorization, the app automatically uses the CLI bundled with the verified Desktop installation, then falls back to a supported global `t3` command.
11
+ - Discord Desktop running whenever you want Rich Presence to appear. Discord in a browser is not sufficient.
12
+ - Windows, macOS, or Linux. Startup is installed for the current user and does not require root or administrator access under normal conditions.
13
+
14
+ ## Install
15
+
16
+ Install the package globally, then run it once:
17
+
18
+ ```sh
19
+ npm install -g t3-discord-presence
20
+ t3-discord-presence
21
+ ```
22
+
23
+ That is the complete setup. The package includes the public Discord Application ID `1539247632227246150`, so there is no Discord Developer Portal setup, client-ID environment variable, bot token, or client secret to configure.
24
+
25
+ Running `t3-discord-presence` without a command is idempotent. It installs or repairs startup for the current user, attempts initial authorization when T3 Code is open, and ensures the daemon is running. Repeating it does not create another startup registration or daemon process. The explicit `t3-discord-presence install` command performs the same setup and remains available for scripts.
26
+
27
+ If T3 Code is closed during setup, the command still succeeds. The daemon starts in the background, waits quietly, and automatically connects when T3 Code and Discord Desktop become available. Open T3 Code and run `t3-discord-presence auth` later if authorization is still pending.
28
+
29
+ After the first run, it starts automatically when you log in, waits quietly for T3 Code, updates Discord Rich Presence when T3 is active, clears the presence when T3 closes, and reconnects whenever T3 Code or Discord Desktop returns.
30
+
31
+ The generated `config.json` contains optional privacy and image settings only. For example, project names and thread titles can be hidden or shown without changing the built-in Discord application:
32
+
33
+ ```json
34
+ {
35
+ "presence": {
36
+ "showProject": true,
37
+ "showThread": false,
38
+ "showModel": true,
39
+ "showProvider": true,
40
+ "showElapsedTime": true
41
+ },
42
+ "discord": {}
43
+ }
44
+ ```
45
+
46
+ ## Authentication
47
+
48
+ To authorize explicitly, open T3 Code and run:
49
+
50
+ ```sh
51
+ t3-discord-presence auth
52
+ ```
53
+
54
+ The command asks the official local T3 CLI for a short-lived pairing credential, preferring the verified T3 Desktop installation and falling back to a supported global CLI. It exchanges that credential with the verified local T3 server and requests only the `orchestration:read` scope. The resulting access credential is stored per T3 environment in the operating-system credential store when available. A current-user-only app state file is used as a restrictive fallback.
55
+
56
+ If automatic pairing is unavailable, `auth` can prompt in an interactive terminal for the `Token` produced by `t3 pair --label "t3 discord presence"`. Input is hidden. That one-time pairing value is exchanged immediately and is never persisted; only the scoped bearer credential is stored. The login daemon never prompts for input.
57
+
58
+ Pairing credentials and WebSocket tickets are short-lived and are not persisted. Access credentials are never printed, logged, or sent to Discord. When a credential expires or is rejected, run `auth` again.
59
+
60
+ ## Daily use
61
+
62
+ The login daemon should require no attention. These commands are available when needed:
63
+
64
+ ```sh
65
+ t3-discord-presence status
66
+ t3-discord-presence start
67
+ t3-discord-presence stop
68
+ t3-discord-presence restart
69
+ t3-discord-presence doctor
70
+ t3-discord-presence logs
71
+ ```
72
+
73
+ `status` reports startup registration, daemon, T3, Discord, and authorization state without revealing secrets. `doctor` checks the local prerequisites and explains common setup failures. `logs` prints the path to the bounded, rotated JSONL daemon log.
74
+
75
+ Running in the foreground is useful while diagnosing a changing T3 protocol:
76
+
77
+ ```sh
78
+ t3-discord-presence run --debug
79
+ ```
80
+
81
+ Debug logging includes safe event classifications and connection state, not raw payloads or private content.
82
+
83
+ ## Background startup
84
+
85
+ - **Windows:** installs a current-user Task Scheduler task triggered at logon. It launches the exact Node executable and installed CLI through a hidden launcher. If Task Scheduler registration is unavailable, it falls back to a launcher in the current user's Startup folder.
86
+ - **macOS:** installs `~/Library/LaunchAgents/com.f3tchcodes.t3-discord-presence.plist` and loads it with modern `launchctl` user-domain commands. Recent macOS versions may show it under Login Items / Allow in the Background; allow it there if macOS disables it.
87
+ - **Linux:** installs and enables a `systemd --user` service. If a user systemd manager is unavailable, it falls back to an XDG autostart desktop entry.
88
+
89
+ All mechanisms run the actual globally installed Node executable and compiled entry point rather than relying on an interactive shell's `PATH`.
90
+
91
+ After updating the global package, run the command again so it can repair a registration whose installed path changed:
92
+
93
+ ```sh
94
+ npm update -g t3-discord-presence
95
+ t3-discord-presence
96
+ ```
97
+
98
+ ## Privacy
99
+
100
+ The default Discord activity can include the project name, model, provider, elapsed time, a coarse activity label, and a deterministic active-agent count. Thread titles are hidden by default. Set any of the five `presence` options in the config example to `false` to hide that field; in particular, set `showProject` to `false` to hide project names.
101
+
102
+ The daemon never persists or publishes to Discord:
103
+
104
+ - raw prompts, chat messages, or response text;
105
+ - raw commands, terminal output, or tool arguments;
106
+ - file contents, patches, or diffs;
107
+ - workspace roots, current working directories, or other absolute paths;
108
+ - full T3 activities, thread objects, or raw RPC payloads.
109
+
110
+ Activities are mapped to a small allowlist such as `editing code`, `thinking`, `running commands`, or `waiting for input`; unknown activities become a generic label. Authentication secrets are the one intentional private value retained for reconnects: access credentials are kept in the OS credential store or restricted fallback file, but never included in presence, status output, or logs. Logs are size-bounded, rotated, and redact secret-shaped values and sensitive metadata.
111
+
112
+ There is no screen capture, process-memory inspection, T3 database scraping, or intermediary service operated by this project. Network communication is limited to T3's authenticated loopback backend and the local Discord RPC client; Discord itself receives only the filtered presence fields described above.
113
+
114
+ ## Uninstall
115
+
116
+ Stop the daemon and remove only this application's startup registration:
117
+
118
+ ```sh
119
+ t3-discord-presence uninstall
120
+ ```
121
+
122
+ Normal uninstall preserves this app's config and stored authorization so reinstalling does not require setup again. To also remove this app's config, credentials, logs, and transient state, use:
123
+
124
+ ```sh
125
+ t3-discord-presence uninstall --purge
126
+ ```
127
+
128
+ Neither form removes or changes T3 Code data. `--purge` applies only to `t3-discord-presence` files and credentials. It removes fallback-file credentials plus operating-system credential entries for currently known T3 environments. Because operating-system keyrings do not provide a portable way to enumerate a service's historical entries, an entry for an old environment that is no longer present in app state or discoverable may need to be removed through the OS credential manager.
129
+
130
+ ## Troubleshooting
131
+
132
+ Start with:
133
+
134
+ ```sh
135
+ t3-discord-presence status
136
+ t3-discord-presence doctor
137
+ t3-discord-presence logs
138
+ ```
139
+
140
+ - **Discord is waiting or disconnected:** start Discord Desktop and sign in. The daemon retries automatically. Optional invalid image keys are retried without images, so they cannot prevent presence updates.
141
+ - **T3 is waiting:** open T3 Code. Stale runtime files and servers whose PID, origin, or environment descriptor cannot be verified are deliberately ignored. For a nonstandard development installation, set `T3CODE_HOME` to the relevant T3 base directory before starting the daemon.
142
+ - **Authorization is required:** ensure T3 Code is open, then run `t3-discord-presence auth`. It checks T3 Desktop's bundled CLI and then a supported global `t3` command automatically. If neither can pair automatically, run `t3 pair --label "t3 discord presence"` and paste its `Token` into the hidden prompt. Run `restart` afterward if the daemon does not reconnect immediately.
143
+ - **Presence looks stale:** `restart` safely replaces the one daemon instance and republishes current state. Rapid T3 events are deliberately debounced.
144
+ - **Startup does not run after login:** rerun `t3-discord-presence`, inspect `doctor`, and check the platform-specific login mechanism described above. On macOS, also check Allow in the Background.
145
+
146
+ Do not post the contents of credential files when opening an issue. Status and redacted logs are the appropriate diagnostics.
147
+
148
+ ## Development
149
+
150
+ ```sh
151
+ npm ci
152
+ npm run check
153
+ npm run build
154
+ npm run dev
155
+ ```
156
+
157
+ `npm run check` runs ESLint, TypeScript type checking, and the unit test suite. Unit tests use mocks and pure startup renderers; they do not install login services or require T3 Code or Discord.
158
+
159
+ Read-only integration tests are explicitly gated because they connect to a real local T3 environment. They never send agent commands or modify projects or threads. On macOS/Linux:
160
+
161
+ ```sh
162
+ T3_DISCORD_PRESENCE_INTEGRATION=1 npm run test:integration
163
+ ```
164
+
165
+ In PowerShell:
166
+
167
+ ```powershell
168
+ $env:T3_DISCORD_PRESENCE_INTEGRATION="1"; npm run test:integration
169
+ ```
170
+
171
+ Normal CI runs only `npm ci`, `npm run check`, and `npm run build`; it needs neither T3 Code nor Discord and does not publish packages.
172
+
173
+ ## License
174
+
175
+ MIT
@@ -0,0 +1,10 @@
1
+ export interface DiscordIpcDependencies {
2
+ readonly isSocket: (filePath: string) => Promise<boolean>;
3
+ }
4
+ export declare function discordIpcAvailable(options?: {
5
+ readonly environment?: NodeJS.ProcessEnv;
6
+ readonly platform?: NodeJS.Platform;
7
+ readonly dependencies?: Partial<DiscordIpcDependencies>;
8
+ }): Promise<boolean | undefined>;
9
+ export declare function supportedNodeVersion(version: string): boolean;
10
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/cli/diagnostics.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,sBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AAgCD,wBAAsB,mBAAmB,CACrC,OAAO,GAAE;IACL,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACtD,GACP,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAkB9B;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG7D"}
@@ -0,0 +1,52 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { posix } from "node:path";
3
+ const nodeDependencies = {
4
+ async isSocket(filePath) {
5
+ try {
6
+ return (await stat(filePath)).isSocket();
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ },
12
+ };
13
+ function nonEmpty(value) {
14
+ const trimmed = value?.trim();
15
+ return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;
16
+ }
17
+ function unixSocketRoots(environment, platform) {
18
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
19
+ return [...new Set([
20
+ nonEmpty(environment.XDG_RUNTIME_DIR),
21
+ nonEmpty(environment.TMPDIR),
22
+ nonEmpty(environment.TMP),
23
+ nonEmpty(environment.TEMP),
24
+ platform === "linux" && uid !== undefined ? `/run/user/${String(uid)}` : undefined,
25
+ "/tmp",
26
+ ].filter((value) => value !== undefined && posix.isAbsolute(value)))];
27
+ }
28
+ export async function discordIpcAvailable(options = {}) {
29
+ const platform = options.platform ?? process.platform;
30
+ if (platform === "win32") {
31
+ // probing a windows named pipe requires connecting to it, which would interfere with discord rpc.
32
+ return undefined;
33
+ }
34
+ const environment = options.environment ?? process.env;
35
+ const dependencies = { ...nodeDependencies, ...options.dependencies };
36
+ const subdirectories = ["", "app/com.discordapp.Discord", "snap.discord"];
37
+ for (const root of unixSocketRoots(environment, platform)) {
38
+ for (const subdirectory of subdirectories) {
39
+ for (let index = 0; index < 10; index += 1) {
40
+ const socket = posix.join(root, subdirectory, `discord-ipc-${String(index)}`);
41
+ if (await dependencies.isSocket(socket))
42
+ return true;
43
+ }
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ export function supportedNodeVersion(version) {
49
+ const match = /^v?(\d+)\./.exec(version);
50
+ return match !== null && Number(match[1]) >= 22;
51
+ }
52
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/cli/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const commandNames: readonly ["install", "uninstall", "start", "stop", "restart", "status", "auth", "doctor", "logs", "run"];
2
+ export declare const helpText = "t3-discord-presence\n\nDiscord Rich Presence for T3 Code agent activity.\n\nusage:\n t3-discord-presence [command] [options]\n\nRun without a command for first-time setup or to repair login startup and ensure the daemon is running.\n\ncommands:\n install explicitly run the same idempotent first-time setup\n uninstall stop the daemon and remove login startup\n start start the background daemon\n stop stop the background daemon\n restart restart the background daemon\n status show daemon and connection status\n auth authorize against the local T3 environment\n doctor check the local setup\n logs print the log file location\n run run in the foreground (add --debug for diagnostics)\n\ninternal:\n daemon login-service entrypoint; not needed for normal use\n\noptions:\n --purge with uninstall, also remove this app's config and credentials\n --debug with run, enable privacy-safe debug logging\n -h, --help show help\n -v, --version show the installed version\n";
3
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,0GAWf,CAAC;AAEX,eAAO,MAAM,QAAQ,8iCA6BpB,CAAC"}
@@ -0,0 +1,43 @@
1
+ export const commandNames = [
2
+ "install",
3
+ "uninstall",
4
+ "start",
5
+ "stop",
6
+ "restart",
7
+ "status",
8
+ "auth",
9
+ "doctor",
10
+ "logs",
11
+ "run",
12
+ ];
13
+ export const helpText = `t3-discord-presence
14
+
15
+ Discord Rich Presence for T3 Code agent activity.
16
+
17
+ usage:
18
+ t3-discord-presence [command] [options]
19
+
20
+ Run without a command for first-time setup or to repair login startup and ensure the daemon is running.
21
+
22
+ commands:
23
+ install explicitly run the same idempotent first-time setup
24
+ uninstall stop the daemon and remove login startup
25
+ start start the background daemon
26
+ stop stop the background daemon
27
+ restart restart the background daemon
28
+ status show daemon and connection status
29
+ auth authorize against the local T3 environment
30
+ doctor check the local setup
31
+ logs print the log file location
32
+ run run in the foreground (add --debug for diagnostics)
33
+
34
+ internal:
35
+ daemon login-service entrypoint; not needed for normal use
36
+
37
+ options:
38
+ --purge with uninstall, also remove this app's config and credentials
39
+ --debug with run, enable privacy-safe debug logging
40
+ -h, --help show help
41
+ -v, --version show the installed version
42
+ `;
43
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,SAAS;IACT,WAAW;IACX,OAAO;IACP,MAAM;IACN,SAAS;IACT,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,MAAM;IACN,KAAK;CACC,CAAC;AAEX,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BvB,CAAC"}
@@ -0,0 +1,64 @@
1
+ import type { AppPaths } from "../config/paths.js";
2
+ import { type DaemonLockRecord } from "../daemon/lock.js";
3
+ export interface DetachedSpawnOptions {
4
+ readonly detached: true;
5
+ readonly env: NodeJS.ProcessEnv;
6
+ readonly shell: false;
7
+ readonly stdio: "ignore";
8
+ readonly windowsHide: true;
9
+ }
10
+ export interface SpawnedDaemon {
11
+ readonly pid: number;
12
+ }
13
+ export interface DaemonProcessAdapter {
14
+ spawn(executable: string, arguments_: ReadonlyArray<string>, options: DetachedSpawnOptions): Promise<SpawnedDaemon>;
15
+ }
16
+ export declare const nodeDaemonProcessAdapter: DaemonProcessAdapter;
17
+ export interface DaemonLifecycleDependencies {
18
+ readonly readLock: (filePath: string) => Promise<DaemonLockRecord | undefined>;
19
+ readonly requestStop: (lockFile: string, stopFile: string) => Promise<DaemonLockRecord | undefined>;
20
+ readonly isProcessRunning: (pid: number) => boolean;
21
+ readonly process: DaemonProcessAdapter;
22
+ readonly delay: (milliseconds: number) => Promise<void>;
23
+ }
24
+ export interface StartDaemonOptions {
25
+ readonly paths: AppPaths;
26
+ readonly cliEntrypoint: string;
27
+ readonly nodeExecutable?: string;
28
+ readonly environment?: NodeJS.ProcessEnv;
29
+ readonly timeoutMs?: number;
30
+ readonly pollMs?: number;
31
+ readonly dependencies?: Partial<DaemonLifecycleDependencies>;
32
+ }
33
+ export interface StopDaemonOptions {
34
+ readonly paths: AppPaths;
35
+ readonly timeoutMs?: number;
36
+ readonly pollMs?: number;
37
+ readonly dependencies?: Partial<DaemonLifecycleDependencies>;
38
+ }
39
+ export interface DaemonInspection {
40
+ readonly running: boolean;
41
+ readonly record?: DaemonLockRecord;
42
+ }
43
+ export type StartDaemonResult = {
44
+ readonly outcome: "started";
45
+ readonly pid: number;
46
+ } | {
47
+ readonly outcome: "already-running";
48
+ readonly pid: number;
49
+ };
50
+ export type StopDaemonResult = {
51
+ readonly outcome: "stopped";
52
+ readonly pid: number;
53
+ } | {
54
+ readonly outcome: "already-stopped";
55
+ } | {
56
+ readonly outcome: "timed-out";
57
+ readonly pid: number;
58
+ };
59
+ export declare function inspectDaemon(paths: Pick<AppPaths, "lockFile">, options?: {
60
+ readonly dependencies?: Partial<DaemonLifecycleDependencies>;
61
+ }): Promise<DaemonInspection>;
62
+ export declare function startDaemon(options: StartDaemonOptions): Promise<StartDaemonResult>;
63
+ export declare function stopDaemon(options: StopDaemonOptions): Promise<StopDaemonResult>;
64
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/cli/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { spawn } from "node:child_process";
2
+ import { processIsRunning, readDaemonLock, requestDaemonStop, } from "../daemon/lock.js";
3
+ const DEFAULT_POLL_MS = 100;
4
+ const DEFAULT_START_TIMEOUT_MS = 10_000;
5
+ const DEFAULT_STOP_TIMEOUT_MS = 20_000;
6
+ export const nodeDaemonProcessAdapter = {
7
+ async spawn(executable, arguments_, options) {
8
+ return new Promise((resolve, reject) => {
9
+ const child = spawn(executable, [...arguments_], options);
10
+ let settled = false;
11
+ child.once("error", error => {
12
+ if (settled)
13
+ return;
14
+ settled = true;
15
+ reject(error);
16
+ });
17
+ child.once("spawn", () => {
18
+ if (settled)
19
+ return;
20
+ settled = true;
21
+ const { pid } = child;
22
+ if (pid === undefined) {
23
+ reject(new Error("the daemon process did not receive a process id"));
24
+ return;
25
+ }
26
+ child.unref();
27
+ resolve({ pid });
28
+ });
29
+ });
30
+ },
31
+ };
32
+ const defaultDependencies = {
33
+ readLock: readDaemonLock,
34
+ requestStop: requestDaemonStop,
35
+ isProcessRunning: processIsRunning,
36
+ process: nodeDaemonProcessAdapter,
37
+ delay: async (milliseconds) => new Promise(resolve => {
38
+ setTimeout(resolve, milliseconds);
39
+ }),
40
+ };
41
+ function dependenciesWithDefaults(dependencies) {
42
+ return { ...defaultDependencies, ...dependencies };
43
+ }
44
+ function positiveInteger(value, fallback, name) {
45
+ const resolved = value ?? fallback;
46
+ if (!Number.isSafeInteger(resolved) || resolved < 1) {
47
+ throw new RangeError(`${name} must be a positive safe integer`);
48
+ }
49
+ return resolved;
50
+ }
51
+ export async function inspectDaemon(paths, options = {}) {
52
+ const dependencies = dependenciesWithDefaults(options.dependencies);
53
+ const record = await dependencies.readLock(paths.lockFile);
54
+ if (record === undefined)
55
+ return { running: false };
56
+ // a stale heartbeat can be caused by sleep, suspension, or a debugger. a
57
+ // live owner is safer than briefly running two presence daemons at once.
58
+ const running = dependencies.isProcessRunning(record.pid);
59
+ return { running, record };
60
+ }
61
+ function pollCount(timeoutMs, pollMs) {
62
+ return Math.ceil(timeoutMs / pollMs) + 1;
63
+ }
64
+ export async function startDaemon(options) {
65
+ const dependencies = dependenciesWithDefaults(options.dependencies);
66
+ const timeoutMs = positiveInteger(options.timeoutMs, DEFAULT_START_TIMEOUT_MS, "timeoutMs");
67
+ const pollMs = positiveInteger(options.pollMs, DEFAULT_POLL_MS, "pollMs");
68
+ const existing = await inspectDaemon(options.paths, {
69
+ dependencies,
70
+ });
71
+ if (existing.running && existing.record !== undefined) {
72
+ return { outcome: "already-running", pid: existing.record.pid };
73
+ }
74
+ const child = await dependencies.process.spawn(options.nodeExecutable ?? process.execPath, [options.cliEntrypoint, "daemon"], {
75
+ detached: true,
76
+ env: { ...(options.environment ?? process.env) },
77
+ shell: false,
78
+ stdio: "ignore",
79
+ windowsHide: true,
80
+ });
81
+ for (let attempt = 0; attempt < pollCount(timeoutMs, pollMs); attempt += 1) {
82
+ const inspection = await inspectDaemon(options.paths, {
83
+ dependencies,
84
+ });
85
+ if (inspection.running && inspection.record !== undefined) {
86
+ return inspection.record.pid === child.pid
87
+ ? { outcome: "started", pid: child.pid }
88
+ : { outcome: "already-running", pid: inspection.record.pid };
89
+ }
90
+ if (attempt + 1 < pollCount(timeoutMs, pollMs)) {
91
+ await dependencies.delay(pollMs);
92
+ }
93
+ }
94
+ throw new Error("the daemon did not become ready before the startup timeout");
95
+ }
96
+ export async function stopDaemon(options) {
97
+ const dependencies = dependenciesWithDefaults(options.dependencies);
98
+ const timeoutMs = positiveInteger(options.timeoutMs, DEFAULT_STOP_TIMEOUT_MS, "timeoutMs");
99
+ const pollMs = positiveInteger(options.pollMs, DEFAULT_POLL_MS, "pollMs");
100
+ const existing = await dependencies.readLock(options.paths.lockFile);
101
+ if (existing === undefined || !dependencies.isProcessRunning(existing.pid)) {
102
+ return { outcome: "already-stopped" };
103
+ }
104
+ const target = await dependencies.requestStop(options.paths.lockFile, options.paths.stopFile);
105
+ if (target === undefined)
106
+ return { outcome: "already-stopped" };
107
+ const attempts = pollCount(timeoutMs, pollMs);
108
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
109
+ const current = await dependencies.readLock(options.paths.lockFile);
110
+ if (current === undefined || current.nonce !== target.nonce) {
111
+ return { outcome: "stopped", pid: target.pid };
112
+ }
113
+ if (attempt + 1 < attempts)
114
+ await dependencies.delay(pollMs);
115
+ }
116
+ return { outcome: "timed-out", pid: target.pid };
117
+ }
118
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/cli/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface SecretPromptOptions {
2
+ readonly prompt: string;
3
+ readonly input?: NodeJS.ReadStream;
4
+ readonly output?: NodeJS.WriteStream;
5
+ }
6
+ export declare function promptForSecret(options: SecretPromptOptions): Promise<string | undefined>;
7
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/cli/prompt.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;CACxC;AAED,wBAAsB,eAAe,CACjC,OAAO,EAAE,mBAAmB,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgC7B"}
@@ -0,0 +1,37 @@
1
+ import { createInterface } from "node:readline";
2
+ import { Writable } from "node:stream";
3
+ export async function promptForSecret(options) {
4
+ const input = options.input ?? process.stdin;
5
+ const output = options.output ?? process.stderr;
6
+ if (!input.isTTY || !output.isTTY)
7
+ return undefined;
8
+ output.write(options.prompt);
9
+ const mutedOutput = new Writable({
10
+ write(_chunk, _encoding, callback) {
11
+ callback();
12
+ },
13
+ });
14
+ const readline = createInterface({
15
+ input,
16
+ output: mutedOutput,
17
+ terminal: true,
18
+ });
19
+ return new Promise(resolve => {
20
+ let settled = false;
21
+ const finish = (value) => {
22
+ if (settled)
23
+ return;
24
+ settled = true;
25
+ readline.close();
26
+ output.write("\n");
27
+ resolve(value);
28
+ };
29
+ readline.once("SIGINT", () => finish(undefined));
30
+ readline.once("close", () => finish(undefined));
31
+ readline.question("", answer => {
32
+ const trimmed = answer.trim();
33
+ finish(trimmed === "" ? undefined : trimmed);
34
+ });
35
+ });
36
+ }
37
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/cli/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQvC,MAAM,CAAC,KAAK,UAAU,eAAe,CACjC,OAA4B;IAE5B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAChD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAEpD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,WAAW,GAAG,IAAI,QAAQ,CAAC;QAC7B,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ;YAC7B,QAAQ,EAAE,CAAC;QACf,CAAC;KACJ,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,eAAe,CAAC;QAC7B,KAAK;QACL,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,IAAI;KACjB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;QACzB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,KAAyB,EAAE,EAAE;YACzC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QACjD,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE;YAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { CredentialStore } from "../config/credentials.js";
2
+ import { type AppPaths } from "../config/paths.js";
3
+ export interface PurgeFileSystem {
4
+ readonly readTextFile: (filePath: string) => Promise<string | undefined>;
5
+ readonly remove: (target: string, options: {
6
+ readonly force: true;
7
+ readonly recursive: boolean;
8
+ }) => Promise<void>;
9
+ }
10
+ export interface PurgeAppDataOptions {
11
+ readonly paths: AppPaths;
12
+ readonly credentials: CredentialStore;
13
+ readonly platform?: NodeJS.Platform;
14
+ readonly environmentIds?: ReadonlyArray<string>;
15
+ readonly fileSystem?: PurgeFileSystem;
16
+ }
17
+ export interface PurgeAppDataResult {
18
+ readonly credentialsRemoved: number;
19
+ readonly directoriesRemoved: number;
20
+ }
21
+ export declare function environmentIdsFromCredentialFile(contents: string | undefined): ReadonlyArray<string>;
22
+ export declare function assertAppOwnedPaths(paths: AppPaths, platform?: NodeJS.Platform): void;
23
+ export declare function removeTransientDaemonData(paths: AppPaths, options?: {
24
+ readonly platform?: NodeJS.Platform;
25
+ readonly fileSystem?: PurgeFileSystem;
26
+ }): Promise<void>;
27
+ export declare function purgeAppData(options: PurgeAppDataOptions): Promise<PurgeAppDataResult>;
28
+ //# sourceMappingURL=purge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purge.d.ts","sourceRoot":"","sources":["../../src/cli/purge.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAsB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACzE,QAAQ,CAAC,MAAM,EAAE,CACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;KAAE,KAC7D,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACvC;AAuBD,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAcpG;AAMD,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,QAAQ,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC7C,IAAI,CAqCN;AAED,wBAAsB,yBAAyB,CAC3C,KAAK,EAAE,QAAQ,EACf,OAAO,GAAE;IACL,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACpC,GACP,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwB5F"}