@urateam/cli 0.1.40 → 0.1.42

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 (55) hide show
  1. package/dist/__tests__/env-file.test.d.ts +2 -0
  2. package/dist/__tests__/env-file.test.d.ts.map +1 -0
  3. package/dist/__tests__/env-file.test.js +89 -0
  4. package/dist/__tests__/env-file.test.js.map +1 -0
  5. package/dist/__tests__/linear-oauth.test.d.ts +2 -0
  6. package/dist/__tests__/linear-oauth.test.d.ts.map +1 -0
  7. package/dist/__tests__/linear-oauth.test.js +162 -0
  8. package/dist/__tests__/linear-oauth.test.js.map +1 -0
  9. package/dist/__tests__/oauth-state.test.d.ts +2 -0
  10. package/dist/__tests__/oauth-state.test.d.ts.map +1 -0
  11. package/dist/__tests__/oauth-state.test.js +40 -0
  12. package/dist/__tests__/oauth-state.test.js.map +1 -0
  13. package/dist/__tests__/self-auth-linear.test.d.ts +2 -0
  14. package/dist/__tests__/self-auth-linear.test.d.ts.map +1 -0
  15. package/dist/__tests__/self-auth-linear.test.js +92 -0
  16. package/dist/__tests__/self-auth-linear.test.js.map +1 -0
  17. package/dist/__tests__/service-unit.test.d.ts +2 -0
  18. package/dist/__tests__/service-unit.test.d.ts.map +1 -0
  19. package/dist/__tests__/service-unit.test.js +65 -0
  20. package/dist/__tests__/service-unit.test.js.map +1 -0
  21. package/dist/__tests__/service.test.d.ts +2 -0
  22. package/dist/__tests__/service.test.d.ts.map +1 -0
  23. package/dist/__tests__/service.test.js +193 -0
  24. package/dist/__tests__/service.test.js.map +1 -0
  25. package/dist/commands/self-auth-linear.d.ts +22 -0
  26. package/dist/commands/self-auth-linear.d.ts.map +1 -0
  27. package/dist/commands/self-auth-linear.js +100 -0
  28. package/dist/commands/self-auth-linear.js.map +1 -0
  29. package/dist/commands/service.d.ts +13 -0
  30. package/dist/commands/service.d.ts.map +1 -0
  31. package/dist/commands/service.js +164 -0
  32. package/dist/commands/service.js.map +1 -0
  33. package/dist/index.js +4 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/env-file.d.ts +3 -0
  36. package/dist/lib/env-file.d.ts.map +1 -0
  37. package/dist/lib/env-file.js +69 -0
  38. package/dist/lib/env-file.js.map +1 -0
  39. package/dist/lib/linear-oauth-deps.d.ts +14 -0
  40. package/dist/lib/linear-oauth-deps.d.ts.map +1 -0
  41. package/dist/lib/linear-oauth-deps.js +54 -0
  42. package/dist/lib/linear-oauth-deps.js.map +1 -0
  43. package/dist/lib/linear-oauth.d.ts +46 -0
  44. package/dist/lib/linear-oauth.d.ts.map +1 -0
  45. package/dist/lib/linear-oauth.js +154 -0
  46. package/dist/lib/linear-oauth.js.map +1 -0
  47. package/dist/lib/oauth-state.d.ts +9 -0
  48. package/dist/lib/oauth-state.d.ts.map +1 -0
  49. package/dist/lib/oauth-state.js +43 -0
  50. package/dist/lib/oauth-state.js.map +1 -0
  51. package/dist/lib/service-unit.d.ts +25 -0
  52. package/dist/lib/service-unit.d.ts.map +1 -0
  53. package/dist/lib/service-unit.js +67 -0
  54. package/dist/lib/service-unit.js.map +1 -0
  55. package/package.json +3 -3
@@ -0,0 +1,193 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdtempSync, rmSync, existsSync, readFileSync, writeFileSync, mkdirSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ const execFileMock = vi.fn(async (_cmd, _args) => ({
6
+ stdout: "",
7
+ stderr: "",
8
+ }));
9
+ vi.mock("node:child_process", async () => {
10
+ const actual = await vi.importActual("node:child_process");
11
+ return {
12
+ ...actual,
13
+ execFile: (cmd, args, cb) => {
14
+ execFileMock(cmd, args)
15
+ .then((r) => cb(null, r.stdout, r.stderr))
16
+ .catch((e) => cb(e));
17
+ },
18
+ };
19
+ });
20
+ let fakeHome;
21
+ vi.mock("node:os", async () => {
22
+ const actual = await vi.importActual("node:os");
23
+ return {
24
+ ...actual,
25
+ homedir: () => fakeHome,
26
+ userInfo: () => ({ username: "tester" }),
27
+ };
28
+ });
29
+ const { serviceCommand } = await import("../commands/service.js");
30
+ describe("ura service install (darwin)", () => {
31
+ let tmp;
32
+ let originalPlatform;
33
+ beforeEach(() => {
34
+ tmp = mkdtempSync(join(tmpdir(), "ura-service-"));
35
+ fakeHome = tmp;
36
+ mkdirSync(join(tmp, "Library", "LaunchAgents"), { recursive: true });
37
+ mkdirSync(join(tmp, ".urateam", "data"), { recursive: true });
38
+ process.env.URATEAM_HOME = join(tmp, ".urateam");
39
+ originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
40
+ Object.defineProperty(process, "platform", { value: "darwin" });
41
+ execFileMock.mockClear();
42
+ });
43
+ afterEach(() => {
44
+ delete process.env.URATEAM_HOME;
45
+ Object.defineProperty(process, "platform", originalPlatform);
46
+ rmSync(tmp, { recursive: true, force: true });
47
+ });
48
+ it("writes the plist and loads it via launchctl", async () => {
49
+ await serviceCommand.parseAsync(["install"], { from: "user" });
50
+ const plist = join(tmp, "Library", "LaunchAgents", "com.urateam.daemon.plist");
51
+ expect(existsSync(plist)).toBe(true);
52
+ const content = readFileSync(plist, "utf8");
53
+ expect(content).toContain("<key>Label</key>");
54
+ expect(content).toContain("com.urateam.daemon");
55
+ const launchctlCalls = execFileMock.mock.calls.filter(([cmd]) => cmd === "launchctl");
56
+ expect(launchctlCalls.length).toBeGreaterThanOrEqual(1);
57
+ expect(launchctlCalls.some(([, args]) => args.includes("load"))).toBe(true);
58
+ });
59
+ it("--dry-run prints the plist and writes nothing", async () => {
60
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
61
+ await serviceCommand.parseAsync(["install", "--dry-run"], { from: "user" });
62
+ const plist = join(tmp, "Library", "LaunchAgents", "com.urateam.daemon.plist");
63
+ expect(existsSync(plist)).toBe(false);
64
+ const out = spy.mock.calls.flat().join("\n");
65
+ expect(out).toContain("<?xml");
66
+ expect(out).toContain("com.urateam.daemon");
67
+ expect(execFileMock).not.toHaveBeenCalled();
68
+ spy.mockRestore();
69
+ });
70
+ it("is idempotent: skips when the plist already exists", async () => {
71
+ const plist = join(tmp, "Library", "LaunchAgents", "com.urateam.daemon.plist");
72
+ writeFileSync(plist, "<!-- existing -->");
73
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
74
+ await serviceCommand.parseAsync(["install"], { from: "user" });
75
+ expect(readFileSync(plist, "utf8")).toBe("<!-- existing -->");
76
+ expect(spy.mock.calls.flat().join("\n")).toMatch(/already exists/i);
77
+ expect(execFileMock).not.toHaveBeenCalled();
78
+ spy.mockRestore();
79
+ });
80
+ });
81
+ describe("ura service install (linux)", () => {
82
+ let tmp;
83
+ let originalPlatform;
84
+ beforeEach(() => {
85
+ tmp = mkdtempSync(join(tmpdir(), "ura-service-"));
86
+ fakeHome = tmp;
87
+ mkdirSync(join(tmp, ".config", "systemd", "user"), { recursive: true });
88
+ mkdirSync(join(tmp, ".urateam", "data"), { recursive: true });
89
+ process.env.URATEAM_HOME = join(tmp, ".urateam");
90
+ originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
91
+ Object.defineProperty(process, "platform", { value: "linux" });
92
+ execFileMock.mockClear();
93
+ });
94
+ afterEach(() => {
95
+ delete process.env.URATEAM_HOME;
96
+ Object.defineProperty(process, "platform", originalPlatform);
97
+ rmSync(tmp, { recursive: true, force: true });
98
+ });
99
+ it("writes the systemd unit and runs daemon-reload + enable --now", async () => {
100
+ await serviceCommand.parseAsync(["install"], { from: "user" });
101
+ const unit = join(tmp, ".config", "systemd", "user", "urateam.service");
102
+ expect(existsSync(unit)).toBe(true);
103
+ expect(readFileSync(unit, "utf8")).toContain("ExecStart=");
104
+ const systemctlCalls = execFileMock.mock.calls.filter(([cmd]) => cmd === "systemctl");
105
+ expect(systemctlCalls.some(([, args]) => args.includes("daemon-reload"))).toBe(true);
106
+ expect(systemctlCalls.some(([, args]) => args.includes("enable") && args.includes("urateam.service"))).toBe(true);
107
+ });
108
+ });
109
+ describe("ura service install (unsupported platform)", () => {
110
+ let originalPlatform;
111
+ beforeEach(() => {
112
+ originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
113
+ Object.defineProperty(process, "platform", { value: "win32" });
114
+ });
115
+ afterEach(() => {
116
+ Object.defineProperty(process, "platform", originalPlatform);
117
+ });
118
+ it("fails with a clear message", async () => {
119
+ await expect(serviceCommand.parseAsync(["install"], { from: "user" })).rejects.toThrow(/unsupported platform/i);
120
+ });
121
+ });
122
+ describe("ura service uninstall (linux)", () => {
123
+ let tmp;
124
+ let originalPlatform;
125
+ beforeEach(() => {
126
+ tmp = mkdtempSync(join(tmpdir(), "ura-service-"));
127
+ fakeHome = tmp;
128
+ mkdirSync(join(tmp, ".config", "systemd", "user"), { recursive: true });
129
+ originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
130
+ Object.defineProperty(process, "platform", { value: "linux" });
131
+ execFileMock.mockClear();
132
+ });
133
+ afterEach(() => {
134
+ Object.defineProperty(process, "platform", originalPlatform);
135
+ rmSync(tmp, { recursive: true, force: true });
136
+ });
137
+ it("disables and deletes the unit when present", async () => {
138
+ const unit = join(tmp, ".config", "systemd", "user", "urateam.service");
139
+ writeFileSync(unit, "[Unit]\n");
140
+ await serviceCommand.parseAsync(["uninstall"], { from: "user" });
141
+ expect(existsSync(unit)).toBe(false);
142
+ expect(execFileMock.mock.calls.some(([cmd, args]) => cmd === "systemctl" &&
143
+ args.includes("disable") &&
144
+ args.includes("urateam.service"))).toBe(true);
145
+ });
146
+ it("is a no-op when the unit is already absent", async () => {
147
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
148
+ await serviceCommand.parseAsync(["uninstall"], { from: "user" });
149
+ expect(spy.mock.calls.flat().join("\n")).toMatch(/not installed/i);
150
+ expect(execFileMock).not.toHaveBeenCalled();
151
+ spy.mockRestore();
152
+ });
153
+ it("still deletes the unit when systemctl disable fails (best-effort stop)", async () => {
154
+ const unit = join(tmp, ".config", "systemd", "user", "urateam.service");
155
+ writeFileSync(unit, "[Unit]\n");
156
+ execFileMock.mockImplementationOnce(async () => {
157
+ throw new Error("Failed to disable urateam.service: not loaded");
158
+ });
159
+ await serviceCommand.parseAsync(["uninstall"], { from: "user" });
160
+ expect(existsSync(unit)).toBe(false);
161
+ });
162
+ });
163
+ describe("ura service uninstall (darwin)", () => {
164
+ let tmp;
165
+ let originalPlatform;
166
+ beforeEach(() => {
167
+ tmp = mkdtempSync(join(tmpdir(), "ura-service-"));
168
+ fakeHome = tmp;
169
+ mkdirSync(join(tmp, "Library", "LaunchAgents"), { recursive: true });
170
+ originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
171
+ Object.defineProperty(process, "platform", { value: "darwin" });
172
+ execFileMock.mockClear();
173
+ });
174
+ afterEach(() => {
175
+ Object.defineProperty(process, "platform", originalPlatform);
176
+ rmSync(tmp, { recursive: true, force: true });
177
+ });
178
+ it("unloads and deletes the plist when present", async () => {
179
+ const plist = join(tmp, "Library", "LaunchAgents", "com.urateam.daemon.plist");
180
+ writeFileSync(plist, "<!-- existing -->");
181
+ await serviceCommand.parseAsync(["uninstall"], { from: "user" });
182
+ expect(existsSync(plist)).toBe(false);
183
+ expect(execFileMock.mock.calls.some(([cmd, args]) => cmd === "launchctl" && args.includes("unload"))).toBe(true);
184
+ });
185
+ it("is a no-op when the plist is already absent", async () => {
186
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
187
+ await serviceCommand.parseAsync(["uninstall"], { from: "user" });
188
+ expect(spy.mock.calls.flat().join("\n")).toMatch(/not installed/i);
189
+ expect(execFileMock).not.toHaveBeenCalled();
190
+ spy.mockRestore();
191
+ });
192
+ });
193
+ //# sourceMappingURL=service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.test.js","sourceRoot":"","sources":["../../src/__tests__/service.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `ura self-auth-linear` — browser-based Linear OAuth flow.
3
+ *
4
+ * Preconditions:
5
+ * 1. `ura init` has been run (`$URATEAM_HOME` exists).
6
+ * 2. `LINEAR_CLIENT_ID` and `LINEAR_CLIENT_SECRET` are set (operator created
7
+ * an OAuth app in Linear's settings — see deploy/USER_LEVEL_INSTALL.md).
8
+ *
9
+ * Behavior:
10
+ * - Starts an ephemeral 127.0.0.1 HTTP server, opens the authorize URL in
11
+ * the operator's browser, verifies the HMAC-signed state on callback,
12
+ * exchanges the code for an access token, fetches workspace metadata.
13
+ * - Writes LINEAR_API_KEY=<access_token> to $URATEAM_HOME/.env, preserving
14
+ * unrelated keys.
15
+ * - Emits a `linear.oauth_completed` audit event opportunistically.
16
+ *
17
+ * The access token is never logged. The success-page HTML returned to the
18
+ * browser contains no token.
19
+ */
20
+ import { Command } from "commander";
21
+ export declare const selfAuthLinearCommand: Command;
22
+ //# sourceMappingURL=self-auth-linear.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-auth-linear.d.ts","sourceRoot":"","sources":["../../src/commands/self-auth-linear.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwDpC,eAAO,MAAM,qBAAqB,SAwE9B,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * `ura self-auth-linear` — browser-based Linear OAuth flow.
3
+ *
4
+ * Preconditions:
5
+ * 1. `ura init` has been run (`$URATEAM_HOME` exists).
6
+ * 2. `LINEAR_CLIENT_ID` and `LINEAR_CLIENT_SECRET` are set (operator created
7
+ * an OAuth app in Linear's settings — see deploy/USER_LEVEL_INSTALL.md).
8
+ *
9
+ * Behavior:
10
+ * - Starts an ephemeral 127.0.0.1 HTTP server, opens the authorize URL in
11
+ * the operator's browser, verifies the HMAC-signed state on callback,
12
+ * exchanges the code for an access token, fetches workspace metadata.
13
+ * - Writes LINEAR_API_KEY=<access_token> to $URATEAM_HOME/.env, preserving
14
+ * unrelated keys.
15
+ * - Emits a `linear.oauth_completed` audit event opportunistically.
16
+ *
17
+ * The access token is never logged. The success-page HTML returned to the
18
+ * browser contains no token.
19
+ */
20
+ import { Command } from "commander";
21
+ import { existsSync } from "node:fs";
22
+ import { join } from "node:path";
23
+ import { userInfo } from "node:os";
24
+ import { createDb, logAuditEvent, linearOauthCompletedEvent, } from "@urateam/core";
25
+ import { resolveUserLevelHome, userLevelDataDir, } from "../lib/user-level-config.js";
26
+ import { runLinearOAuth } from "../lib/linear-oauth.js";
27
+ import { defaultBrowserOpen, defaultFetchTokenEndpoint, defaultFetchViewer, } from "../lib/linear-oauth-deps.js";
28
+ import { upsertEnvFile } from "../lib/env-file.js";
29
+ const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
30
+ const DEFAULT_SCOPE = "read,write";
31
+ /**
32
+ * Fixed default port for the callback server. Linear's OAuth flow requires
33
+ * the redirect URI in the authorize request to EXACTLY match the URI
34
+ * registered in the OAuth app settings (host + port + path), so a random
35
+ * port would force the operator to re-register every time. 9898 is
36
+ * unprivileged and unlikely to collide with common dev-server ports. The
37
+ * `--port` flag overrides it when 9898 is taken — but the operator then
38
+ * has to register the new URI in Linear.
39
+ */
40
+ const DEFAULT_PORT = 9898;
41
+ async function tryEmitAuditEvent(args) {
42
+ try {
43
+ const dbPath = join(userLevelDataDir(), "urateam.db");
44
+ if (!existsSync(dbPath))
45
+ return;
46
+ const db = await createDb({ connectionString: dbPath });
47
+ const actor = `cli:${userInfo().username ?? "unknown"}`;
48
+ await logAuditEvent(db, linearOauthCompletedEvent({
49
+ workspaceId: args.workspaceId,
50
+ workspaceName: args.workspaceName,
51
+ actor,
52
+ }));
53
+ }
54
+ catch {
55
+ // Audit failure must not break the OAuth flow.
56
+ }
57
+ }
58
+ export const selfAuthLinearCommand = new Command("self-auth-linear")
59
+ .description("Browser-based Linear OAuth flow; stores the token as LINEAR_API_KEY in ~/.urateam/.env")
60
+ .option("--timeout-ms <ms>", "How long to wait for the operator to authorize (default 5 minutes)", String(DEFAULT_TIMEOUT_MS))
61
+ .option("--scope <scope>", "OAuth scopes to request (comma-separated)", DEFAULT_SCOPE)
62
+ .option("--port <port>", "Local port for the callback server (must match the redirect URI registered in Linear)", String(DEFAULT_PORT))
63
+ .action(async (opts) => {
64
+ const home = resolveUserLevelHome();
65
+ if (!existsSync(home)) {
66
+ throw new Error(`ura self-auth-linear: ${home} does not exist. Run 'ura init' first.`);
67
+ }
68
+ const clientId = process.env.LINEAR_CLIENT_ID;
69
+ const clientSecret = process.env.LINEAR_CLIENT_SECRET;
70
+ if (!clientId) {
71
+ throw new Error("ura self-auth-linear: LINEAR_CLIENT_ID is not set. Create a Linear OAuth app at https://linear.app/settings/api/applications/new and set LINEAR_CLIENT_ID + LINEAR_CLIENT_SECRET in ~/.urateam/.env before running this command.");
72
+ }
73
+ if (!clientSecret) {
74
+ throw new Error("ura self-auth-linear: LINEAR_CLIENT_SECRET is not set. See https://linear.app/settings/api/applications and copy the client secret into ~/.urateam/.env.");
75
+ }
76
+ const port = Number(opts.port);
77
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
78
+ throw new Error(`ura self-auth-linear: --port must be an integer between 0 and 65535 (got '${opts.port}')`);
79
+ }
80
+ console.log(`ura self-auth-linear: opening Linear in your browser (callback on http://127.0.0.1:${port}/callback)…`);
81
+ const result = await runLinearOAuth({
82
+ clientId,
83
+ clientSecret,
84
+ scope: opts.scope,
85
+ timeoutMs: Number(opts.timeoutMs),
86
+ port,
87
+ openBrowser: defaultBrowserOpen,
88
+ fetchTokenEndpoint: defaultFetchTokenEndpoint,
89
+ fetchViewer: defaultFetchViewer,
90
+ });
91
+ upsertEnvFile(join(home, ".env"), {
92
+ LINEAR_API_KEY: result.accessToken,
93
+ });
94
+ console.log(`ura self-auth-linear: authorized for workspace ${result.workspaceName ?? result.workspaceId}; token written to ${join(home, ".env")}.`);
95
+ await tryEmitAuditEvent({
96
+ workspaceId: result.workspaceId,
97
+ workspaceName: result.workspaceName,
98
+ });
99
+ });
100
+ //# sourceMappingURL=self-auth-linear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-auth-linear.js","sourceRoot":"","sources":["../../src/commands/self-auth-linear.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `ura service install` / `ura service uninstall`
3
+ *
4
+ * Generates a platform-appropriate service unit (launchd plist on macOS,
5
+ * systemd-user unit on Linux) so the user-level daemon auto-starts on login.
6
+ * Mirrors what `deploy/USER_LEVEL_INSTALL.md` previously documented as
7
+ * copy-paste blocks. Idempotent: refuses to overwrite an existing unit
8
+ * (operator must uninstall first). `--dry-run` prints the unit content
9
+ * without touching the filesystem.
10
+ */
11
+ import { Command } from "commander";
12
+ export declare const serviceCommand: Command;
13
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/commands/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwGpC,eAAO,MAAM,cAAc,SAE1B,CAAC"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * `ura service install` / `ura service uninstall`
3
+ *
4
+ * Generates a platform-appropriate service unit (launchd plist on macOS,
5
+ * systemd-user unit on Linux) so the user-level daemon auto-starts on login.
6
+ * Mirrors what `deploy/USER_LEVEL_INSTALL.md` previously documented as
7
+ * copy-paste blocks. Idempotent: refuses to overwrite an existing unit
8
+ * (operator must uninstall first). `--dry-run` prints the unit content
9
+ * without touching the filesystem.
10
+ */
11
+ import { Command } from "commander";
12
+ import { execFile } from "node:child_process";
13
+ import { existsSync, mkdirSync, rmSync, writeFileSync, } from "node:fs";
14
+ import { homedir, userInfo } from "node:os";
15
+ import { dirname, join } from "node:path";
16
+ import { promisify } from "node:util";
17
+ import { renderLaunchdPlist, renderSystemdUserUnit, SERVICE_LABEL, SYSTEMD_UNIT_BASENAME, } from "../lib/service-unit.js";
18
+ import { createDb, logAuditEvent, serviceInstalledEvent, serviceUninstalledEvent, } from "@urateam/core";
19
+ import { resolveUserLevelHome, userLevelDataDir, } from "../lib/user-level-config.js";
20
+ const execFileP = promisify(execFile);
21
+ function detectPlatform() {
22
+ if (process.platform === "darwin")
23
+ return "darwin";
24
+ if (process.platform === "linux")
25
+ return "linux";
26
+ throw new Error(`ura service: unsupported platform '${process.platform}'. ` +
27
+ `Only macOS (launchd) and Linux (systemd-user) are supported. ` +
28
+ `For other platforms, see the manual service-setup snippets in ` +
29
+ `deploy/USER_LEVEL_INSTALL.md.`);
30
+ }
31
+ function unitPath(platform) {
32
+ if (platform === "darwin") {
33
+ return join(homedir(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
34
+ }
35
+ return join(homedir(), ".config", "systemd", "user", SYSTEMD_UNIT_BASENAME);
36
+ }
37
+ function buildInput() {
38
+ // Resolve the binary the operator invoked us as — that's what the service
39
+ // unit should re-launch. `process.argv[1]` is the `ura` script (commander's
40
+ // entry point); it's already absolute when invoked via the npm-installed
41
+ // shim. Fall back to the common npm-global location if anything looks off.
42
+ let binaryPath = process.argv[1] ?? "/usr/local/bin/ura";
43
+ if (!binaryPath.startsWith("/"))
44
+ binaryPath = "/usr/local/bin/ura";
45
+ const home = resolveUserLevelHome();
46
+ return {
47
+ binaryPath,
48
+ urateamHome: home,
49
+ envFilePath: join(home, ".env"),
50
+ stdoutPath: join(userLevelDataDir(), "daemon.log"),
51
+ stderrPath: join(userLevelDataDir(), "daemon.err.log"),
52
+ };
53
+ }
54
+ async function tryEmitAuditEvent(args) {
55
+ // Opportunistic: only writes when the daemon DB already exists. CLI commands
56
+ // can run before the daemon has ever started, so the DB may not be there
57
+ // yet. Never throws — audit failure must not break the install.
58
+ try {
59
+ const dbPath = join(userLevelDataDir(), "urateam.db");
60
+ if (!existsSync(dbPath))
61
+ return;
62
+ const db = await createDb({ connectionString: dbPath });
63
+ const actor = `cli:${userInfo().username ?? "unknown"}`;
64
+ const builder = args.eventType === "installed"
65
+ ? serviceInstalledEvent
66
+ : serviceUninstalledEvent;
67
+ const evt = builder({
68
+ platform: args.platform,
69
+ unitPath: args.unitPath,
70
+ actor,
71
+ });
72
+ // License-gated path — `logAuditEvent` is a no-op when `audit-log` is
73
+ // unlicensed. Service-install events are an Enterprise-tier operational
74
+ // signal; OSS / Pro deployments simply drop them.
75
+ await logAuditEvent(db, evt);
76
+ }
77
+ catch {
78
+ // Audit must never crash the install — swallow.
79
+ }
80
+ }
81
+ export const serviceCommand = new Command("service").description("Manage the urateam launchd/systemd service unit");
82
+ serviceCommand
83
+ .command("install")
84
+ .description("Install the platform service unit (launchd on macOS, systemd-user on Linux)")
85
+ .option("--dry-run", "Print the unit content; do not write or load it")
86
+ .action(async (opts) => {
87
+ const platform = detectPlatform();
88
+ const input = buildInput();
89
+ const dest = unitPath(platform);
90
+ const content = platform === "darwin"
91
+ ? renderLaunchdPlist(input)
92
+ : renderSystemdUserUnit(input);
93
+ if (opts.dryRun) {
94
+ console.log(`# Would write: ${dest}`);
95
+ console.log(content);
96
+ return;
97
+ }
98
+ if (existsSync(dest)) {
99
+ console.log(`ura service install: ${dest} already exists. ` +
100
+ `Run 'ura service uninstall' first if you want to reinstall.`);
101
+ return;
102
+ }
103
+ mkdirSync(dirname(dest), { recursive: true });
104
+ writeFileSync(dest, content);
105
+ if (platform === "darwin") {
106
+ await execFileP("launchctl", ["load", "-w", dest]);
107
+ await execFileP("launchctl", ["start", SERVICE_LABEL]);
108
+ }
109
+ else {
110
+ await execFileP("systemctl", ["--user", "daemon-reload"]);
111
+ await execFileP("systemctl", [
112
+ "--user",
113
+ "enable",
114
+ "--now",
115
+ SYSTEMD_UNIT_BASENAME,
116
+ ]);
117
+ }
118
+ console.log(`ura service install: wrote ${dest} and started the service.`);
119
+ await tryEmitAuditEvent({
120
+ eventType: "installed",
121
+ platform,
122
+ unitPath: dest,
123
+ });
124
+ });
125
+ serviceCommand
126
+ .command("uninstall")
127
+ .description("Remove the platform service unit and stop the service")
128
+ .action(async () => {
129
+ const platform = detectPlatform();
130
+ const dest = unitPath(platform);
131
+ if (!existsSync(dest)) {
132
+ console.log(`ura service uninstall: ${dest} not installed — nothing to remove.`);
133
+ return;
134
+ }
135
+ if (platform === "darwin") {
136
+ try {
137
+ await execFileP("launchctl", ["unload", "-w", dest]);
138
+ }
139
+ catch {
140
+ // unit may have been unloaded already — best-effort stop
141
+ }
142
+ }
143
+ else {
144
+ try {
145
+ await execFileP("systemctl", [
146
+ "--user",
147
+ "disable",
148
+ "--now",
149
+ SYSTEMD_UNIT_BASENAME,
150
+ ]);
151
+ }
152
+ catch {
153
+ // unit may have been disabled already — best-effort stop
154
+ }
155
+ }
156
+ rmSync(dest, { force: true });
157
+ console.log(`ura service uninstall: removed ${dest}.`);
158
+ await tryEmitAuditEvent({
159
+ eventType: "uninstalled",
160
+ platform,
161
+ unitPath: dest,
162
+ });
163
+ });
164
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/commands/service.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -28,6 +28,8 @@ import { stopCommand, haltCommand } from "./commands/control.js";
28
28
  import { initCommand } from "./commands/init.js";
29
29
  import { repoCommand } from "./commands/repo.js";
30
30
  import { uninstallCommand } from "./commands/uninstall.js";
31
+ import { serviceCommand } from "./commands/service.js";
32
+ import { selfAuthLinearCommand } from "./commands/self-auth-linear.js";
31
33
  const program = new Command();
32
34
  program
33
35
  .name("ura")
@@ -46,5 +48,7 @@ program.addCommand(haltCommand);
46
48
  program.addCommand(initCommand);
47
49
  program.addCommand(repoCommand);
48
50
  program.addCommand(uninstallCommand);
51
+ program.addCommand(serviceCommand);
52
+ program.addCommand(selfAuthLinearCommand);
49
53
  program.parse();
50
54
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,oCAAoC;AACpC,4EAA4E;AAC5E,2EAA2E;AAC3E,mEAAmE;AACnE,6EAA6E;AAC7E,oEAAoE;AACpE,IAAI,CAAC;IACH,OAAO,CAAC,WAAW,EAAE,CAAC;AACxB,CAAC;AAAC,OAAO,GAAY,EAAE,CAAC;IACtB,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;IAClD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CACV,qCAAqC,OAAO,CAAC,GAAG,EAAE,KAChD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,KAAK,CAAC;KACX,WAAW,CAAC,aAAa,CAAC;KAC1B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;AAEhC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAErC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,oCAAoC;AACpC,4EAA4E;AAC5E,2EAA2E;AAC3E,mEAAmE;AACnE,6EAA6E;AAC7E,oEAAoE;AACpE,IAAI,CAAC;IACH,OAAO,CAAC,WAAW,EAAE,CAAC;AACxB,CAAC;AAAC,OAAO,GAAY,EAAE,CAAC;IACtB,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;IAClD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CACV,qCAAqC,OAAO,CAAC,GAAG,EAAE,KAChD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,KAAK,CAAC;KACX,WAAW,CAAC,aAAa,CAAC;KAC1B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;AAEhC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAE1C,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function readEnvFile(path: string): Record<string, string>;
2
+ export declare function upsertEnvFile(path: string, updates: Record<string, string>): void;
3
+ //# sourceMappingURL=env-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../../src/lib/env-file.ts"],"names":[],"mappings":"AAiBA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAahE;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,IAAI,CA2CN"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Minimal `.env` read / upsert preserving unrelated keys, comments, and blank
3
+ * lines. We don't need the full `dotenv` spec — just replace-or-append.
4
+ *
5
+ * `upsertEnvFile` writes atomically by writing a sibling `<path>.tmp` first
6
+ * and then renaming. Same-FS rename is atomic on POSIX and on Windows in
7
+ * Node 22, which is the only target.
8
+ */
9
+ import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync, } from "node:fs";
10
+ import { dirname } from "node:path";
11
+ export function readEnvFile(path) {
12
+ if (!existsSync(path))
13
+ return {};
14
+ const out = {};
15
+ for (const raw of readFileSync(path, "utf8").split("\n")) {
16
+ const line = raw.trim();
17
+ if (!line || line.startsWith("#"))
18
+ continue;
19
+ const eq = line.indexOf("=");
20
+ if (eq < 1)
21
+ continue;
22
+ const key = line.slice(0, eq).trim();
23
+ const value = line.slice(eq + 1).trim();
24
+ out[key] = value;
25
+ }
26
+ return out;
27
+ }
28
+ export function upsertEnvFile(path, updates) {
29
+ mkdirSync(dirname(path), { recursive: true });
30
+ const updateKeys = new Set(Object.keys(updates));
31
+ const seen = new Set();
32
+ let lines = [];
33
+ if (existsSync(path)) {
34
+ lines = readFileSync(path, "utf8").split("\n");
35
+ }
36
+ const out = [];
37
+ for (const raw of lines) {
38
+ const trimmed = raw.trim();
39
+ if (!trimmed || trimmed.startsWith("#")) {
40
+ out.push(raw);
41
+ continue;
42
+ }
43
+ const eq = trimmed.indexOf("=");
44
+ if (eq < 1) {
45
+ out.push(raw);
46
+ continue;
47
+ }
48
+ const key = trimmed.slice(0, eq).trim();
49
+ if (updateKeys.has(key)) {
50
+ out.push(`${key}=${updates[key]}`);
51
+ seen.add(key);
52
+ }
53
+ else {
54
+ out.push(raw);
55
+ }
56
+ }
57
+ while (out.length > 0 && out[out.length - 1] === "")
58
+ out.pop();
59
+ for (const key of updateKeys) {
60
+ if (!seen.has(key)) {
61
+ out.push(`${key}=${updates[key]}`);
62
+ }
63
+ }
64
+ out.push("");
65
+ const tmp = `${path}.tmp`;
66
+ writeFileSync(tmp, out.join("\n"));
67
+ renameSync(tmp, path);
68
+ }
69
+ //# sourceMappingURL=env-file.js.map