agyx 0.1.2 → 0.1.3

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 (41) hide show
  1. package/README.md +39 -14
  2. package/dist/src/cli.js +150 -99
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/color.d.ts +9 -0
  5. package/dist/src/color.js +28 -0
  6. package/dist/src/color.js.map +1 -0
  7. package/dist/src/config.d.ts +28 -4
  8. package/dist/src/config.js +89 -21
  9. package/dist/src/config.js.map +1 -1
  10. package/dist/src/coordinator.d.ts +12 -2
  11. package/dist/src/coordinator.js +265 -11
  12. package/dist/src/coordinator.js.map +1 -1
  13. package/dist/src/eligibility.d.ts +4 -0
  14. package/dist/src/eligibility.js +10 -0
  15. package/dist/src/eligibility.js.map +1 -0
  16. package/dist/src/google_auth.d.ts +1 -0
  17. package/dist/src/google_auth.js +42 -0
  18. package/dist/src/google_auth.js.map +1 -0
  19. package/dist/src/install.d.ts +2 -0
  20. package/dist/src/install.js +24 -7
  21. package/dist/src/install.js.map +1 -1
  22. package/dist/src/keychain.js +2 -2
  23. package/dist/src/keychain.js.map +1 -1
  24. package/dist/src/onboarding.d.ts +1 -0
  25. package/dist/src/onboarding.js +87 -0
  26. package/dist/src/onboarding.js.map +1 -0
  27. package/dist/src/profile_view.d.ts +22 -0
  28. package/dist/src/profile_view.js +77 -0
  29. package/dist/src/profile_view.js.map +1 -0
  30. package/dist/src/quota.d.ts +1 -0
  31. package/dist/src/quota.js +3 -0
  32. package/dist/src/quota.js.map +1 -1
  33. package/dist/src/selection.d.ts +1 -1
  34. package/dist/src/selection.js +18 -1
  35. package/dist/src/selection.js.map +1 -1
  36. package/dist/src/session.js +54 -31
  37. package/dist/src/session.js.map +1 -1
  38. package/dist/src/ui.d.ts +19 -0
  39. package/dist/src/ui.js +306 -0
  40. package/dist/src/ui.js.map +1 -0
  41. package/package.json +8 -1
@@ -0,0 +1 @@
1
+ export declare function detectCredentialEmail(credential: Buffer): Promise<string | undefined>;
@@ -0,0 +1,42 @@
1
+ function parseCredential(credential) {
2
+ const value = credential.toString("utf8").trim();
3
+ const prefix = "go-keyring-base64:";
4
+ try {
5
+ const json = value.startsWith(prefix)
6
+ ? Buffer.from(value.slice(prefix.length), "base64").toString("utf8")
7
+ : value;
8
+ return JSON.parse(json);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ async function fetchEmailFromUserInfo(accessToken) {
15
+ const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
16
+ headers: { Authorization: `Bearer ${accessToken}` },
17
+ });
18
+ if (!response.ok)
19
+ return undefined;
20
+ const body = await response.json();
21
+ return typeof body.email === "string" ? body.email : undefined;
22
+ }
23
+ async function fetchEmailFromTokenInfo(accessToken) {
24
+ const response = await fetch(`https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${encodeURIComponent(accessToken)}`);
25
+ if (!response.ok)
26
+ return undefined;
27
+ const body = await response.json();
28
+ return typeof body.email === "string" ? body.email : undefined;
29
+ }
30
+ export async function detectCredentialEmail(credential) {
31
+ const accessToken = parseCredential(credential)?.token?.access_token;
32
+ if (!accessToken)
33
+ return undefined;
34
+ try {
35
+ return await fetchEmailFromUserInfo(accessToken)
36
+ ?? await fetchEmailFromTokenInfo(accessToken);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ //# sourceMappingURL=google_auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google_auth.js","sourceRoot":"","sources":["../../src/google_auth.ts"],"names":[],"mappings":"AAMA,SAAS,eAAe,CAAC,UAAkB;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,oBAAoB,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;YACnC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpE,CAAC,CAAC,KAAK,CAAC;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,WAAmB;IACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,+CAA+C,EAAE;QAC5E,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE;KACpD,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAyB,CAAC;IAC1D,OAAO,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,WAAmB;IACxD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,+DAA+D,kBAAkB,CAAC,WAAW,CAAC,EAAE,CACjG,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAyB,CAAC;IAC1D,OAAO,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB;IAElB,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC;IACrE,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,MAAM,sBAAsB,CAAC,WAAW,CAAC;eAC3C,MAAM,uBAAuB,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -1,2 +1,4 @@
1
+ export declare function shellIntegrationPath(): string;
1
2
  export declare function shellInit(): string;
3
+ export declare function shellIntegrationInstalled(): Promise<boolean>;
2
4
  export declare function installShellIntegration(): Promise<string>;
@@ -5,28 +5,45 @@ import { ensureParent } from "./config.js";
5
5
  import { findRealAgy } from "./processes.js";
6
6
  const startMarker = "# >>> agyx >>>";
7
7
  const endMarker = "# <<< agyx <<<";
8
+ export function shellIntegrationPath() {
9
+ const shell = process.env.SHELL?.split("/").at(-1) ?? "zsh";
10
+ return shell === "bash"
11
+ ? join(homedir(), ".bashrc")
12
+ : join(homedir(), ".zshrc");
13
+ }
8
14
  export function shellInit() {
9
15
  return `agy() { command agyx session -- "$@"; }`;
10
16
  }
17
+ export async function shellIntegrationInstalled() {
18
+ const rcPath = shellIntegrationPath();
19
+ try {
20
+ return (await readFile(rcPath, "utf8")).includes(startMarker);
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
11
26
  export async function installShellIntegration() {
12
27
  await findRealAgy();
13
- const shell = process.env.SHELL?.split("/").at(-1) ?? "zsh";
14
- const rcPath = shell === "bash"
15
- ? join(homedir(), ".bashrc")
16
- : join(homedir(), ".zshrc");
28
+ const rcPath = shellIntegrationPath();
17
29
  await ensureParent(rcPath);
18
30
  let content = "";
31
+ let existing = true;
19
32
  try {
20
33
  content = await readFile(rcPath, "utf8");
21
34
  }
22
- catch { /* New shell rc file. */ }
35
+ catch {
36
+ existing = false;
37
+ }
23
38
  const block = `${startMarker}\n${shellInit()}\n${endMarker}`;
24
39
  const pattern = new RegExp(`${startMarker}[\\s\\S]*?${endMarker}`, "m");
40
+ const trimmed = content.trimEnd();
25
41
  content = pattern.test(content)
26
42
  ? content.replace(pattern, block)
27
- : `${content.trimEnd()}\n\n${block}\n`;
43
+ : `${trimmed ? `${trimmed}\n\n` : ""}${block}\n`;
28
44
  await writeFile(rcPath, content, { mode: 0o600 });
29
- await chmod(rcPath, 0o600);
45
+ if (!existing)
46
+ await chmod(rcPath, 0o600);
30
47
  return rcPath;
31
48
  }
32
49
  //# sourceMappingURL=install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,WAAW,GAAG,gBAAgB,CAAC;AACrC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAEnC,MAAM,UAAU,SAAS;IACvB,OAAO,yCAAyC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAC5D,MAAM,MAAM,GAAG,KAAK,KAAK,MAAM;QAC7B,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC;QAC5B,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9B,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,CAAC;QAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IACjD,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,WAAW,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,aAAa,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;IACxE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;QACjC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,WAAW,GAAG,gBAAgB,CAAC;AACrC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAEnC,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAC5D,OAAO,KAAK,KAAK,MAAM;QACrB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC;QAC5B,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,yCAAyC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAC7C,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC;QAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IACjD,MAAM,CAAC;QAAC,QAAQ,GAAG,KAAK,CAAC;IAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,GAAG,WAAW,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,aAAa,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAClC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;QACjC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACnD,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ;QAAE,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -27,8 +27,8 @@ async function write(service, account, credential, trustedApplications) {
27
27
  for (const application of trustedApplications) {
28
28
  args.push("-T", application);
29
29
  }
30
- args.push("-w");
31
- await run(security, args, { input: Buffer.concat([credential, Buffer.from("\n")]) });
30
+ args.push("-X", credential.toString("hex"));
31
+ await run(security, args);
32
32
  }
33
33
  export const keychain = {
34
34
  readActive: () => read(activeService, activeAccount),
@@ -1 +1 @@
1
- {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AACrC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAC/B,MAAM,aAAa,GAAG,aAAa,CAAC;AACpC,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,KAAK,UAAU,IAAI,CAAC,OAAe,EAAE,OAAe;IAClD,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,QAAQ,EACR,CAAC,uBAAuB,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAC7D,EAAE,YAAY,EAAE,IAAI,EAAE,CACvB,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,IAAI,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IAC/B,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAE,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IAC3E,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,OAAe,EAAE,OAAe;IACpD,MAAM,GAAG,CACP,QAAQ,EACR,CAAC,yBAAyB,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,EACzD,EAAE,YAAY,EAAE,IAAI,EAAE,CACvB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,KAAK,CAClB,OAAe,EACf,OAAe,EACf,UAAkB,EAClB,mBAA6B;IAE7B,MAAM,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG;QACX,sBAAsB;QACtB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,GAAG,OAAO,IAAI,OAAO,EAAE;KAC9B,CAAC;IACF,KAAK,MAAM,WAAW,IAAI,mBAAmB,EAAE,CAAC;QAC9C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,MAAM,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC;IACpD,KAAK,CAAC,WAAW,CAAC,UAAkB;QAClC,MAAM,KAAK,CACT,aAAa,EACb,aAAa,EACb,UAAU,EACV,CAAC,MAAM,WAAW,EAAE,EAAE,QAAQ,CAAC,CAChC,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC;IACxD,WAAW,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC;IACvD,YAAY,EAAE,CAAC,IAAY,EAAE,UAAkB,EAAE,EAAE,CACjD,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC;IACnD,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC;CAC5D,CAAC"}
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/keychain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function maybeRunOnboarding(command: string): Promise<void>;
@@ -0,0 +1,87 @@
1
+ import { loadState, saveState } from "./config.js";
2
+ import { installShellIntegration, shellIntegrationInstalled } from "./install.js";
3
+ import { run } from "./processes.js";
4
+ import { confirmAction } from "./ui.js";
5
+ const repository = "dongwook-chan/agyx";
6
+ async function ghInstalled() {
7
+ const result = await run("/bin/sh", ["-lc", "command -v gh >/dev/null 2>&1"], { allowFailure: true });
8
+ return result.code === 0;
9
+ }
10
+ async function promptShellIntegration() {
11
+ const state = await loadState();
12
+ if (state.onboarding?.shellIntegrationPromptedAt)
13
+ return;
14
+ if (await shellIntegrationInstalled())
15
+ return;
16
+ const now = new Date().toISOString();
17
+ state.onboarding = {
18
+ ...state.onboarding,
19
+ shellIntegrationPromptedAt: now,
20
+ };
21
+ await saveState(state);
22
+ if (!await confirmAction("Install agy shell integration so `agy` runs through agyx?", true)) {
23
+ console.log("Skipped shell integration. You can run it later with: agyx install");
24
+ return;
25
+ }
26
+ const path = await installShellIntegration();
27
+ const updated = await loadState();
28
+ updated.onboarding = {
29
+ ...updated.onboarding,
30
+ shellIntegrationInstalledAt: new Date().toISOString(),
31
+ };
32
+ await saveState(updated);
33
+ console.log(`Installed agy shell function in ${path}`);
34
+ console.log("This does not change the current terminal automatically.");
35
+ console.log("Open a new terminal, or run:");
36
+ console.log(` source ${path}`);
37
+ console.log("For this terminal only, you can also run:");
38
+ console.log(' eval "$(agyx shell-init)"');
39
+ console.log("Verify with:");
40
+ console.log(" type agy");
41
+ }
42
+ async function promptGithubStar() {
43
+ const state = await loadState();
44
+ if (state.onboarding?.githubStarPromptedAt || state.onboarding?.githubStarredAt)
45
+ return;
46
+ if (!await ghInstalled())
47
+ return;
48
+ const now = new Date().toISOString();
49
+ state.onboarding = {
50
+ ...state.onboarding,
51
+ githubStarPromptedAt: now,
52
+ };
53
+ await saveState(state);
54
+ if (!await confirmAction(`Star ${repository} on GitHub with gh?`, false))
55
+ return;
56
+ try {
57
+ await run("/usr/bin/env", [
58
+ "gh",
59
+ "api",
60
+ "--method", "PUT",
61
+ "/user/starred/dongwook-chan/agyx",
62
+ "--silent",
63
+ ]);
64
+ const updated = await loadState();
65
+ updated.onboarding = {
66
+ ...updated.onboarding,
67
+ githubStarredAt: new Date().toISOString(),
68
+ };
69
+ await saveState(updated);
70
+ console.log(`Starred ${repository}.`);
71
+ }
72
+ catch (error) {
73
+ console.error(`agyx: failed to star ${repository}: ${error.message}`);
74
+ }
75
+ }
76
+ export async function maybeRunOnboarding(command) {
77
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
78
+ return;
79
+ if (process.env.AGYX_NO_ONBOARDING === "1")
80
+ return;
81
+ if (["session", "shell-init", "_activate"].includes(command))
82
+ return;
83
+ if (command !== "install")
84
+ await promptShellIntegration();
85
+ await promptGithubStar();
86
+ }
87
+ //# sourceMappingURL=onboarding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/onboarding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { ProfileRecord, State } from "./config.js";
2
+ import { ProfileRuntimeStatus } from "./selection.js";
3
+ export interface ProfileView {
4
+ marker: string;
5
+ number: string;
6
+ name: string;
7
+ expectedEmail: string;
8
+ actualEmail: string;
9
+ status: string;
10
+ quotaReset: string;
11
+ lastRequest: string;
12
+ activated: string;
13
+ verified: string;
14
+ switches: string;
15
+ selectable: boolean;
16
+ runtimeStatus: ProfileRuntimeStatus;
17
+ disabledReason?: string;
18
+ profile: ProfileRecord;
19
+ }
20
+ export declare function relativeTime(value: string | undefined, now?: Date): string;
21
+ export declare function profileStatusText(profile: ProfileRecord, now?: Date): string;
22
+ export declare function buildProfileViews(state: Pick<State, "activeProfile" | "profiles">, now?: Date): ProfileView[];
@@ -0,0 +1,77 @@
1
+ import { effectiveProfileStatus } from "./selection.js";
2
+ export function relativeTime(value, now = new Date()) {
3
+ if (!value)
4
+ return "-";
5
+ const timestamp = Date.parse(value);
6
+ if (Number.isNaN(timestamp))
7
+ return "-";
8
+ const delta = timestamp - now.getTime();
9
+ const absolute = Math.abs(delta);
10
+ const units = [
11
+ [24 * 60 * 60 * 1000, "d"],
12
+ [60 * 60 * 1000, "h"],
13
+ [60 * 1000, "m"],
14
+ [1000, "s"],
15
+ ];
16
+ const [unitMs, suffix] = units.find(([ms]) => absolute >= ms) ?? units.at(-1);
17
+ const amount = Math.max(1, Math.round(absolute / unitMs));
18
+ return delta >= 0 ? `in ${amount}${suffix}` : `${amount}${suffix} ago`;
19
+ }
20
+ export function profileStatusText(profile, now = new Date()) {
21
+ const status = effectiveProfileStatus(profile, now);
22
+ if (status === "disabled")
23
+ return "disabled";
24
+ if (status === "mismatch")
25
+ return "mismatch";
26
+ if (status === "error")
27
+ return "auth-error";
28
+ if (status === "ineligible")
29
+ return "ineligible";
30
+ if (status === "exhausted")
31
+ return "quota";
32
+ return profile.quotaStatus === "available" ? "ready" : "unknown";
33
+ }
34
+ export function buildProfileViews(state, now = new Date()) {
35
+ return state.profiles.map((profile, index) => {
36
+ const runtimeStatus = effectiveProfileStatus(profile, now);
37
+ const disabledReason = (() => {
38
+ if (runtimeStatus === "ready")
39
+ return undefined;
40
+ if (runtimeStatus === "mismatch") {
41
+ return profile.credentialError
42
+ ?? `expected ${profile.email ?? "-"}, got ${profile.verifiedEmail ?? "-"}`;
43
+ }
44
+ if (runtimeStatus === "error") {
45
+ return profile.credentialError ?? "credential could not be verified";
46
+ }
47
+ if (runtimeStatus === "ineligible") {
48
+ return profile.eligibilityReason
49
+ ?? "account is not eligible for Antigravity; verify it in the browser or login another account";
50
+ }
51
+ if (runtimeStatus === "exhausted") {
52
+ return profile.quotaResetAt
53
+ ? `quota resets ${relativeTime(profile.quotaResetAt, now)}`
54
+ : "quota exhausted";
55
+ }
56
+ return "disabled";
57
+ })();
58
+ return {
59
+ marker: profile.name === state.activeProfile ? "*" : "",
60
+ number: String(index + 1),
61
+ name: profile.name,
62
+ expectedEmail: profile.email ?? "-",
63
+ actualEmail: profile.verifiedEmail ?? "-",
64
+ status: profileStatusText(profile, now),
65
+ quotaReset: relativeTime(profile.quotaResetAt, now),
66
+ lastRequest: relativeTime(profile.lastRequestAt, now),
67
+ activated: relativeTime(profile.lastActivatedAt, now),
68
+ verified: relativeTime(profile.credentialVerifiedAt ?? profile.credentialMismatchAt, now),
69
+ switches: String(profile.selectionCount ?? 0),
70
+ selectable: runtimeStatus === "ready",
71
+ runtimeStatus,
72
+ disabledReason,
73
+ profile,
74
+ };
75
+ });
76
+ }
77
+ //# sourceMappingURL=profile_view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_view.js","sourceRoot":"","sources":["../../src/profile_view.ts"],"names":[],"mappings":"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"}
@@ -2,4 +2,5 @@ export interface QuotaEvent {
2
2
  reason: string;
3
3
  resetAt?: string;
4
4
  }
5
+ export declare function isRequestEventLine(line: string): boolean;
5
6
  export declare function parseQuotaEventLine(line: string, now?: Date): QuotaEvent | undefined;
package/dist/src/quota.js CHANGED
@@ -1,3 +1,6 @@
1
+ export function isRequestEventLine(line) {
2
+ return /Sending user message to conversation [0-9a-f-]{36}/i.test(line);
3
+ }
1
4
  function parseDurationMs(value) {
2
5
  const pattern = /(\d+(?:\.\d+)?)\s*(d|day|days|h|hr|hrs|hour|hours|m|min|mins|minute|minutes|s|sec|secs|second|seconds)/gi;
3
6
  let total = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/quota.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/quota.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { ProfileRecord, State } from "./config.js";
2
- export type ProfileRuntimeStatus = "ready" | "exhausted" | "disabled";
2
+ export type ProfileRuntimeStatus = "ready" | "exhausted" | "disabled" | "mismatch" | "error" | "ineligible";
3
3
  export declare function effectiveProfileStatus(profile: ProfileRecord, now?: Date): ProfileRuntimeStatus;
4
4
  export declare function isProfileSelectable(profile: ProfileRecord, now?: Date): boolean;
5
5
  export declare function selectNextProfile(state: State, now?: Date): ProfileRecord;
@@ -1,6 +1,12 @@
1
1
  export function effectiveProfileStatus(profile, now = new Date()) {
2
2
  if (profile.disabled)
3
3
  return "disabled";
4
+ if (profile.credentialStatus === "mismatch")
5
+ return "mismatch";
6
+ if (profile.credentialStatus === "error")
7
+ return "error";
8
+ if (profile.eligibilityStatus === "ineligible")
9
+ return "ineligible";
4
10
  if (profile.quotaStatus !== "exhausted")
5
11
  return "ready";
6
12
  if (!profile.quotaResetAt)
@@ -27,9 +33,20 @@ export function selectNextProfile(state, now = new Date()) {
27
33
  const earliestReset = state.profiles
28
34
  .filter((profile) => profile.quotaResetAt)
29
35
  .sort((left, right) => Date.parse(left.quotaResetAt) - Date.parse(right.quotaResetAt))[0];
36
+ const credentialIssues = state.profiles
37
+ .filter((profile) => profile.credentialStatus === "mismatch"
38
+ || profile.credentialStatus === "error"
39
+ || profile.eligibilityStatus === "ineligible")
40
+ .map((profile) => profile.eligibilityStatus === "ineligible"
41
+ ? `${profile.name}:ineligible`
42
+ : `${profile.name}:${profile.credentialStatus}`)
43
+ .join(", ");
30
44
  const resetText = earliestReset
31
45
  ? ` Earliest reset: ${earliestReset.name} at ${earliestReset.quotaResetAt}.`
32
46
  : "";
33
- throw new Error(`No selectable profiles.${resetText}`);
47
+ const credentialText = credentialIssues
48
+ ? ` Credential issues: ${credentialIssues}.`
49
+ : "";
50
+ throw new Error(`No selectable profiles.${credentialText}${resetText}`);
34
51
  }
35
52
  //# sourceMappingURL=selection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/selection.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,sBAAsB,CACpC,OAAsB,EACtB,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAC;IACxC,IAAI,OAAO,CAAC,WAAW,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IACxD,IAAI,CAAC,OAAO,CAAC,YAAY;QAAE,OAAO,WAAW,CAAC;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE;QACrD,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,OAAO,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,OAAsB,EACtB,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,OAAO,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAY,EACZ,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa;QACrC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,aAAa,CAAC;QACtE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEP,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;cACzE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAE,CAAC;QAC5B,IAAI,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC;IACxD,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ;SACjC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;SACzC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAa,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAa,CAAC,CACjE,CAAC,CAAC,CAAC,CAAC;IACP,MAAM,SAAS,GAAG,aAAa;QAC7B,CAAC,CAAC,oBAAoB,aAAa,CAAC,IAAI,OAAO,aAAa,CAAC,YAAY,GAAG;QAC5E,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,SAAS,EAAE,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/selection.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { createServer } from "node:net";
3
- import { readFile, writeFile } from "node:fs/promises";
3
+ import { chmod, readFile, rename, rm, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { cleanupRuntimeFile, ensureDirectories, loadState, logDir, recordProfileQuotaExhausted, runtimeDir, } from "./config.js";
5
+ import { cleanupRuntimeFile, ensureDirectories, loadState, logDir, recordProfileIneligible, recordProfileQuotaExhausted, recordProfileRequest, runtimeDir, } from "./config.js";
6
6
  import { findRealAgy, isRestartable, withConversation, } from "./processes.js";
7
- import { parseQuotaEventLine } from "./quota.js";
7
+ import { parseEligibilityEventLine } from "./eligibility.js";
8
+ import { isRequestEventLine, parseQuotaEventLine } from "./quota.js";
8
9
  export function detectConversation(content) {
9
10
  const patterns = [
10
11
  /Created conversation ([0-9a-f-]{36})/gi,
@@ -38,30 +39,44 @@ export async function supervise(args) {
38
39
  const recordPath = join(runtimeDir, `${id}.json`);
39
40
  const logPath = join(logDir, `session-${id}.log`);
40
41
  const realAgy = await findRealAgy();
42
+ const startedAt = new Date().toISOString();
41
43
  let child;
42
44
  let paused = false;
43
45
  let intentionalStop = false;
44
46
  let conversationId;
45
47
  let finalCode = 0;
46
48
  let logOffset = 0;
47
- let scanningQuota = false;
49
+ let scanningLogEvents = false;
48
50
  let profileAtStart;
51
+ let quotaMarked = false;
49
52
  let quotaInterval;
53
+ let persistCount = 0;
54
+ const currentRecord = () => ({
55
+ id,
56
+ pid: process.pid,
57
+ childPid: child?.pid,
58
+ cwd: process.cwd(),
59
+ args,
60
+ conversationId,
61
+ socketPath,
62
+ logPath,
63
+ paused,
64
+ restartable: true,
65
+ startedAt,
66
+ });
50
67
  const persist = async () => {
51
- const record = {
52
- id,
53
- pid: process.pid,
54
- childPid: child?.pid,
55
- cwd: process.cwd(),
56
- args,
57
- conversationId,
58
- socketPath,
59
- logPath,
60
- paused,
61
- restartable: true,
62
- startedAt: new Date().toISOString(),
63
- };
64
- await writeFile(recordPath, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
68
+ const record = currentRecord();
69
+ const temporary = `${recordPath}.${process.pid}.${persistCount++}.tmp`;
70
+ try {
71
+ await writeFile(temporary, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
72
+ await chmod(temporary, 0o600);
73
+ await rename(temporary, recordPath);
74
+ }
75
+ catch (error) {
76
+ await rm(temporary, { force: true }).catch(() => undefined);
77
+ throw error;
78
+ }
79
+ return record;
65
80
  };
66
81
  const refreshConversation = async () => {
67
82
  try {
@@ -72,10 +87,10 @@ export async function supervise(args) {
72
87
  // The log may not exist until agy has initialized.
73
88
  }
74
89
  };
75
- const scanQuotaEvents = async () => {
76
- if (scanningQuota)
90
+ const scanLogEvents = async () => {
91
+ if (scanningLogEvents)
77
92
  return;
78
- scanningQuota = true;
93
+ scanningLogEvents = true;
79
94
  try {
80
95
  const profileName = profileAtStart;
81
96
  if (!profileName)
@@ -86,26 +101,34 @@ export async function supervise(args) {
86
101
  const appended = content.slice(logOffset);
87
102
  logOffset = content.length;
88
103
  for (const line of appended.split(/\r?\n/)) {
104
+ if (isRequestEventLine(line)) {
105
+ await recordProfileRequest(profileName);
106
+ }
107
+ const eligibilityEvent = parseEligibilityEventLine(line);
108
+ if (eligibilityEvent) {
109
+ await recordProfileIneligible(profileName, eligibilityEvent);
110
+ }
89
111
  const event = parseQuotaEventLine(line);
90
112
  if (!event)
91
113
  continue;
114
+ if (quotaMarked)
115
+ continue;
116
+ quotaMarked = true;
92
117
  await recordProfileQuotaExhausted(profileName, event);
93
- console.error(`agyx: marked profile '${profileName}' as quota exhausted`
94
- + (event.resetAt ? ` until ${event.resetAt}` : "")
95
- + ". Run 'agyx next' to switch.");
96
118
  }
97
119
  }
98
120
  catch {
99
121
  // The log or state file may not exist yet.
100
122
  }
101
123
  finally {
102
- scanningQuota = false;
124
+ scanningLogEvents = false;
103
125
  }
104
126
  };
105
127
  const startChild = async () => {
106
128
  intentionalStop = false;
107
129
  paused = false;
108
130
  profileAtStart = (await loadState()).activeProfile;
131
+ quotaMarked = false;
109
132
  const launchArgs = withConversation(args, conversationId);
110
133
  if (!launchArgs.some((argument) => argument === "--log-file" || argument.startsWith("--log-file="))) {
111
134
  launchArgs.push("--log-file", logPath);
@@ -123,7 +146,7 @@ export async function supervise(args) {
123
146
  child.on("exit", async (code, signal) => {
124
147
  finalCode = code ?? (signal ? 128 : 1);
125
148
  await refreshConversation();
126
- await scanQuotaEvents();
149
+ await scanLogEvents();
127
150
  child = undefined;
128
151
  await persist();
129
152
  if (!intentionalStop && !paused) {
@@ -163,8 +186,8 @@ export async function supervise(args) {
163
186
  if (request.command === "pause") {
164
187
  paused = true;
165
188
  await stopChild();
166
- await persist();
167
- writeJSON(socket, { ok: true, record: JSON.parse(await readFile(recordPath, "utf8")) });
189
+ const record = await persist();
190
+ writeJSON(socket, { ok: true, record });
168
191
  }
169
192
  else if (request.command === "resume") {
170
193
  if (!child)
@@ -179,8 +202,8 @@ export async function supervise(args) {
179
202
  }
180
203
  else {
181
204
  await refreshConversation();
182
- await persist();
183
- writeJSON(socket, { ok: true, record: JSON.parse(await readFile(recordPath, "utf8")) });
205
+ const record = await persist();
206
+ writeJSON(socket, { ok: true, record });
184
207
  }
185
208
  }
186
209
  catch (error) {
@@ -212,7 +235,7 @@ export async function supervise(args) {
212
235
  });
213
236
  await startChild();
214
237
  quotaInterval = setInterval(() => {
215
- void scanQuotaEvents();
238
+ void scanLogEvents();
216
239
  }, 750);
217
240
  quotaInterval.unref();
218
241
  return await new Promise(() => undefined);