mossbear 0.1.0-alpha.32 → 0.1.0-alpha.33
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/dist/commands/login.d.ts +18 -2
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +19 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/login/open-browser.d.ts +63 -0
- package/dist/login/open-browser.d.ts.map +1 -0
- package/dist/login/open-browser.js +107 -0
- package/dist/login/open-browser.js.map +1 -0
- package/package.json +4 -4
package/dist/commands/login.d.ts
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
* without ever passing through a clipboard.
|
|
7
7
|
*
|
|
8
8
|
* **Two modes, and the second is not a fallback.** Interactive is the developer
|
|
9
|
-
* at a terminal
|
|
10
|
-
*
|
|
9
|
+
* at a terminal, and there the approval page is *opened for them* — the URL is
|
|
10
|
+
* still printed, because a launcher that fails silently would otherwise leave
|
|
11
|
+
* nothing to click (`login/open-browser.ts` says which failures it cannot see).
|
|
12
|
+
* *Delegating* (`--print-url`, and automatic when stdout is not a TTY) prints
|
|
13
|
+
* the URL and code on their own lines, opens nothing, and keeps polling, so an
|
|
11
14
|
* agent running this can courier the URL to its human and still be holding the
|
|
12
15
|
* connection when they approve. That mode is what `agent-driven-setup` needs
|
|
13
16
|
* and what a cloud container with no browser has instead of one; it is named in
|
|
@@ -27,6 +30,13 @@ export type LoginEnvironment = {
|
|
|
27
30
|
/** Whether stdout is a terminal. Decides the mode when no flag is given. */
|
|
28
31
|
isTty: boolean;
|
|
29
32
|
log: (line: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Launch the approval page. Returns whether a launcher was started — false
|
|
35
|
+
* covers "nothing to launch into" (no display, `BROWSER=none`) as well as a
|
|
36
|
+
* launcher that could not be spawned, and the caller only uses it to choose
|
|
37
|
+
* a sentence.
|
|
38
|
+
*/
|
|
39
|
+
openBrowser: (url: string) => boolean;
|
|
30
40
|
transport: PairingTransport;
|
|
31
41
|
writeToken: (token: string) => Promise<void>;
|
|
32
42
|
hostname: () => string;
|
|
@@ -53,6 +63,7 @@ export type LoginResult = {
|
|
|
53
63
|
*/
|
|
54
64
|
export declare function performLogin(options: {
|
|
55
65
|
printUrl: boolean;
|
|
66
|
+
open?: boolean;
|
|
56
67
|
dashboardUrlFlag?: string;
|
|
57
68
|
}, environment: LoginEnvironment): Promise<LoginResult>;
|
|
58
69
|
export declare const loginCommand: import("citty").CommandDef<{
|
|
@@ -61,6 +72,11 @@ export declare const loginCommand: import("citty").CommandDef<{
|
|
|
61
72
|
description: string;
|
|
62
73
|
default: false;
|
|
63
74
|
};
|
|
75
|
+
open: {
|
|
76
|
+
type: "boolean";
|
|
77
|
+
description: string;
|
|
78
|
+
default: true;
|
|
79
|
+
};
|
|
64
80
|
'dashboard-url': {
|
|
65
81
|
type: "string";
|
|
66
82
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAeH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B;;;;;OAKG;IACH,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;IACrC,SAAS,EAAE,gBAAgB,CAAA;IAC3B,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,QAAQ,EAAE,MAAM,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzC;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,EACzE,WAAW,EAAE,gBAAgB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA0FtB;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;EAsDvB,CAAA"}
|
package/dist/commands/login.js
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
* without ever passing through a clipboard.
|
|
7
7
|
*
|
|
8
8
|
* **Two modes, and the second is not a fallback.** Interactive is the developer
|
|
9
|
-
* at a terminal
|
|
10
|
-
*
|
|
9
|
+
* at a terminal, and there the approval page is *opened for them* — the URL is
|
|
10
|
+
* still printed, because a launcher that fails silently would otherwise leave
|
|
11
|
+
* nothing to click (`login/open-browser.ts` says which failures it cannot see).
|
|
12
|
+
* *Delegating* (`--print-url`, and automatic when stdout is not a TTY) prints
|
|
13
|
+
* the URL and code on their own lines, opens nothing, and keeps polling, so an
|
|
11
14
|
* agent running this can courier the URL to its human and still be holding the
|
|
12
15
|
* connection when they approve. That mode is what `agent-driven-setup` needs
|
|
13
16
|
* and what a cloud container with no browser has instead of one; it is named in
|
|
@@ -23,6 +26,7 @@
|
|
|
23
26
|
import { defineCommand } from 'citty';
|
|
24
27
|
import { MOSSBEAR_TOKEN_ENV, readEnvToken, resolveDashboardUrl, writeConfigToken, } from '../config.js';
|
|
25
28
|
import { errText } from '../err-text.js';
|
|
29
|
+
import { openInBrowser } from '../login/open-browser.js';
|
|
26
30
|
import { defaultTransport, pollForToken, startPairing } from '../login/pairing.js';
|
|
27
31
|
import { CLI_VERSION } from '../version.js';
|
|
28
32
|
/**
|
|
@@ -72,7 +76,12 @@ export async function performLogin(options, environment) {
|
|
|
72
76
|
environment.log(` Open ${session.verificationUriComplete}`);
|
|
73
77
|
environment.log(` Code ${session.userCode}`);
|
|
74
78
|
environment.log('');
|
|
75
|
-
|
|
79
|
+
// Printed first, launched second: whatever the browser does with focus,
|
|
80
|
+
// the URL and the code are already on screen to fall back to.
|
|
81
|
+
const opening = options.open !== false && environment.openBrowser(session.verificationUriComplete);
|
|
82
|
+
environment.log(opening
|
|
83
|
+
? ' Opening that in your browser. Waiting for you to approve it…'
|
|
84
|
+
: ' Waiting for you to approve it…');
|
|
76
85
|
}
|
|
77
86
|
let outcome;
|
|
78
87
|
try {
|
|
@@ -111,6 +120,11 @@ export const loginCommand = defineCommand({
|
|
|
111
120
|
description: 'Print the approval URL and code on their own lines and keep waiting, instead of assuming someone is at this terminal. Automatic when stdout is not a TTY',
|
|
112
121
|
default: false,
|
|
113
122
|
},
|
|
123
|
+
open: {
|
|
124
|
+
type: 'boolean',
|
|
125
|
+
description: 'Open the approval page in your browser. On by default at a terminal; use --no-open to just print it. Never happens with --print-url, or when stdout is not a TTY',
|
|
126
|
+
default: true,
|
|
127
|
+
},
|
|
114
128
|
'dashboard-url': {
|
|
115
129
|
type: 'string',
|
|
116
130
|
description: 'Dashboard to pair with. Defaults to the configured one',
|
|
@@ -120,11 +134,13 @@ export const loginCommand = defineCommand({
|
|
|
120
134
|
const { hostname } = await import('node:os');
|
|
121
135
|
const result = await performLogin({
|
|
122
136
|
printUrl: Boolean(args['print-url']),
|
|
137
|
+
open: args.open !== false,
|
|
123
138
|
dashboardUrlFlag: typeof args['dashboard-url'] === 'string' ? args['dashboard-url'] : '',
|
|
124
139
|
}, {
|
|
125
140
|
env: process.env,
|
|
126
141
|
isTty: Boolean(process.stdout.isTTY),
|
|
127
142
|
log: (line) => console.log(line),
|
|
143
|
+
openBrowser: (url) => openInBrowser(url),
|
|
128
144
|
transport: defaultTransport,
|
|
129
145
|
writeToken: (token) => writeConfigToken(token),
|
|
130
146
|
hostname,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand a URL to the machine's own browser.
|
|
3
|
+
*
|
|
4
|
+
* `mossbear login` prints an approval URL that is useless until a human loads
|
|
5
|
+
* it, and the shortest path from "printed" to "loaded" is not the user
|
|
6
|
+
* selecting a wrapped URL out of a terminal. So interactive mode launches it
|
|
7
|
+
* for them — while still printing it, because a launcher that silently fails is
|
|
8
|
+
* worse than no launcher at all and every branch here can fail for reasons this
|
|
9
|
+
* process cannot see.
|
|
10
|
+
*
|
|
11
|
+
* Three rules the shape here exists to keep:
|
|
12
|
+
*
|
|
13
|
+
* - **Only ever a plain `http(s)` URL, passed as an argument.** The value comes
|
|
14
|
+
* off the wire from whatever dashboard is configured, and handing an
|
|
15
|
+
* arbitrary string to a shell — or to a `file:`/`javascript:` handler — turns
|
|
16
|
+
* a misconfigured `dashboardUrl` into local code execution. `spawn` with an
|
|
17
|
+
* argument array never involves a shell, and {@link isLaunchableUrl} refuses
|
|
18
|
+
* anything that is not http(s).
|
|
19
|
+
* - **Never block the login.** The child is detached and unref'd, so a
|
|
20
|
+
* launcher that waits for the browser to exit (several do) cannot hold the
|
|
21
|
+
* poll loop, and an `ENOENT` for a launcher that is not installed is an
|
|
22
|
+
* ignored event rather than a crash.
|
|
23
|
+
* - **Don't launch into a machine with no display.** On a headless Linux box
|
|
24
|
+
* `xdg-open` may fall through to a *terminal* browser, which would take over
|
|
25
|
+
* the very terminal the user is reading the code from.
|
|
26
|
+
*/
|
|
27
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
28
|
+
/** Injected in tests; the real caller uses the process it runs in. */
|
|
29
|
+
export type BrowserLauncher = {
|
|
30
|
+
platform: NodeJS.Platform;
|
|
31
|
+
env: NodeJS.ProcessEnv;
|
|
32
|
+
spawn: typeof nodeSpawn;
|
|
33
|
+
};
|
|
34
|
+
export declare const defaultLauncher: BrowserLauncher;
|
|
35
|
+
/**
|
|
36
|
+
* Whether this URL is safe to pass to a system handler.
|
|
37
|
+
*
|
|
38
|
+
* http(s) only, and deliberately not "whatever `new URL` accepts": `file:`,
|
|
39
|
+
* `javascript:` and custom app schemes are all valid URLs and none of them
|
|
40
|
+
* should be openable by a value the CLI received over a network.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isLaunchableUrl(url: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The command that opens a URL here, or null when nothing should be launched.
|
|
45
|
+
*
|
|
46
|
+
* `BROWSER=none` is the convention already understood by the tools developers
|
|
47
|
+
* run beside this one, so it is honoured rather than reinvented as a
|
|
48
|
+
* Mossbear-specific variable; any other `BROWSER` value is left to the
|
|
49
|
+
* platform handler, which on Linux reads it itself.
|
|
50
|
+
*/
|
|
51
|
+
export declare function launchCommand(url: string, launcher: BrowserLauncher): {
|
|
52
|
+
command: string;
|
|
53
|
+
args: string[];
|
|
54
|
+
} | null;
|
|
55
|
+
/**
|
|
56
|
+
* Try to open `url`, and say whether a launcher was actually started.
|
|
57
|
+
*
|
|
58
|
+
* "Started" is the strongest claim available without waiting for a browser
|
|
59
|
+
* that may take seconds to paint, so the caller's copy must stay at
|
|
60
|
+
* "opened in your browser" rather than promising the page is up.
|
|
61
|
+
*/
|
|
62
|
+
export declare function openInBrowser(url: string, launcher?: BrowserLauncher): boolean;
|
|
63
|
+
//# sourceMappingURL=open-browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/login/open-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEvD,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,KAAK,EAAE,OAAO,SAAS,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAI7B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,eAAe,GACxB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAqB5C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,eAAiC,GAC1C,OAAO,CAmBT"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand a URL to the machine's own browser.
|
|
3
|
+
*
|
|
4
|
+
* `mossbear login` prints an approval URL that is useless until a human loads
|
|
5
|
+
* it, and the shortest path from "printed" to "loaded" is not the user
|
|
6
|
+
* selecting a wrapped URL out of a terminal. So interactive mode launches it
|
|
7
|
+
* for them — while still printing it, because a launcher that silently fails is
|
|
8
|
+
* worse than no launcher at all and every branch here can fail for reasons this
|
|
9
|
+
* process cannot see.
|
|
10
|
+
*
|
|
11
|
+
* Three rules the shape here exists to keep:
|
|
12
|
+
*
|
|
13
|
+
* - **Only ever a plain `http(s)` URL, passed as an argument.** The value comes
|
|
14
|
+
* off the wire from whatever dashboard is configured, and handing an
|
|
15
|
+
* arbitrary string to a shell — or to a `file:`/`javascript:` handler — turns
|
|
16
|
+
* a misconfigured `dashboardUrl` into local code execution. `spawn` with an
|
|
17
|
+
* argument array never involves a shell, and {@link isLaunchableUrl} refuses
|
|
18
|
+
* anything that is not http(s).
|
|
19
|
+
* - **Never block the login.** The child is detached and unref'd, so a
|
|
20
|
+
* launcher that waits for the browser to exit (several do) cannot hold the
|
|
21
|
+
* poll loop, and an `ENOENT` for a launcher that is not installed is an
|
|
22
|
+
* ignored event rather than a crash.
|
|
23
|
+
* - **Don't launch into a machine with no display.** On a headless Linux box
|
|
24
|
+
* `xdg-open` may fall through to a *terminal* browser, which would take over
|
|
25
|
+
* the very terminal the user is reading the code from.
|
|
26
|
+
*/
|
|
27
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
28
|
+
export const defaultLauncher = {
|
|
29
|
+
platform: process.platform,
|
|
30
|
+
env: process.env,
|
|
31
|
+
spawn: nodeSpawn,
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Whether this URL is safe to pass to a system handler.
|
|
35
|
+
*
|
|
36
|
+
* http(s) only, and deliberately not "whatever `new URL` accepts": `file:`,
|
|
37
|
+
* `javascript:` and custom app schemes are all valid URLs and none of them
|
|
38
|
+
* should be openable by a value the CLI received over a network.
|
|
39
|
+
*/
|
|
40
|
+
export function isLaunchableUrl(url) {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = new URL(url);
|
|
43
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The command that opens a URL here, or null when nothing should be launched.
|
|
51
|
+
*
|
|
52
|
+
* `BROWSER=none` is the convention already understood by the tools developers
|
|
53
|
+
* run beside this one, so it is honoured rather than reinvented as a
|
|
54
|
+
* Mossbear-specific variable; any other `BROWSER` value is left to the
|
|
55
|
+
* platform handler, which on Linux reads it itself.
|
|
56
|
+
*/
|
|
57
|
+
export function launchCommand(url, launcher) {
|
|
58
|
+
const browser = launcher.env.BROWSER?.trim().toLowerCase();
|
|
59
|
+
if (browser === 'none')
|
|
60
|
+
return null;
|
|
61
|
+
if (launcher.platform === 'darwin')
|
|
62
|
+
return { command: 'open', args: [url] };
|
|
63
|
+
if (launcher.platform === 'win32') {
|
|
64
|
+
// Not `cmd /c start`: `cmd` re-parses its arguments, and this URL carries a
|
|
65
|
+
// query string, so an `&` in it would be read as a command separator.
|
|
66
|
+
return { command: 'rundll32', args: ['url.dll,FileProtocolHandler', url] };
|
|
67
|
+
}
|
|
68
|
+
// WSL has a Linux platform and no Linux browser — `wslview` (wslu) hands the
|
|
69
|
+
// URL to Windows. Absent that package the spawn fails and the printed URL is
|
|
70
|
+
// still there, which is the same outcome as any other missing launcher.
|
|
71
|
+
if (launcher.env.WSL_DISTRO_NAME)
|
|
72
|
+
return { command: 'wslview', args: [url] };
|
|
73
|
+
// A Linux session with no display server. See the module header: launching
|
|
74
|
+
// here can capture the terminal the user is reading.
|
|
75
|
+
if (!launcher.env.DISPLAY && !launcher.env.WAYLAND_DISPLAY)
|
|
76
|
+
return null;
|
|
77
|
+
return { command: 'xdg-open', args: [url] };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Try to open `url`, and say whether a launcher was actually started.
|
|
81
|
+
*
|
|
82
|
+
* "Started" is the strongest claim available without waiting for a browser
|
|
83
|
+
* that may take seconds to paint, so the caller's copy must stay at
|
|
84
|
+
* "opened in your browser" rather than promising the page is up.
|
|
85
|
+
*/
|
|
86
|
+
export function openInBrowser(url, launcher = defaultLauncher) {
|
|
87
|
+
if (!isLaunchableUrl(url))
|
|
88
|
+
return false;
|
|
89
|
+
const launch = launchCommand(url, launcher);
|
|
90
|
+
if (!launch)
|
|
91
|
+
return false;
|
|
92
|
+
try {
|
|
93
|
+
const child = launcher.spawn(launch.command, launch.args, {
|
|
94
|
+
detached: true,
|
|
95
|
+
stdio: 'ignore',
|
|
96
|
+
});
|
|
97
|
+
// Without this an ENOENT for a missing launcher is an unhandled 'error'
|
|
98
|
+
// event, which ends the process — during a login the user is mid-approval on.
|
|
99
|
+
child.on('error', () => { });
|
|
100
|
+
child.unref();
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=open-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../../src/login/open-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AASvD,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,KAAK,EAAE,SAAS;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAA;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,QAAyB;IAEzB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC1D,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,IAAI,CAAA;IAEnC,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAA;IAC3E,IAAI,QAAQ,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,4EAA4E;QAC5E,sEAAsE;QACtE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAA;IAC5E,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,wEAAwE;IACxE,IAAI,QAAQ,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAA;IAE5E,2EAA2E;IAC3E,qDAAqD;IACrD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,IAAI,CAAA;IAEvE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAA;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,WAA4B,eAAe;IAE3C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IAEvC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEzB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE;YACxD,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAA;QACF,wEAAwE;QACxE,8EAA8E;QAC9E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAC3B,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mossbear",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.33",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local-first CLI for Mossbear — records what your AI coding agents did and syncs it to your dashboard, which grades it against the rules and skills you wrote.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
57
|
-
"@mossbear/eval-core": "0.1.0-alpha.
|
|
58
|
-
"@mossbear/projections": "0.1.0-alpha.
|
|
59
|
-
"@mossbear/protocol": "0.1.0-alpha.
|
|
57
|
+
"@mossbear/eval-core": "0.1.0-alpha.33",
|
|
58
|
+
"@mossbear/projections": "0.1.0-alpha.33",
|
|
59
|
+
"@mossbear/protocol": "0.1.0-alpha.33",
|
|
60
60
|
"citty": "^0.1.6",
|
|
61
61
|
"valibot": "^1.4.2"
|
|
62
62
|
},
|