@indigoai-us/hq-cli 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/bin/hq-auth-refresh.d.ts +13 -0
  2. package/dist/bin/hq-auth-refresh.d.ts.map +1 -0
  3. package/dist/bin/hq-auth-refresh.js +27 -0
  4. package/dist/bin/hq-auth-refresh.js.map +1 -0
  5. package/dist/commands/auth.d.ts +17 -13
  6. package/dist/commands/auth.d.ts.map +1 -1
  7. package/dist/commands/auth.js +83 -238
  8. package/dist/commands/auth.js.map +1 -1
  9. package/dist/commands/cloud.d.ts +3 -12
  10. package/dist/commands/cloud.d.ts.map +1 -1
  11. package/dist/commands/cloud.js +55 -212
  12. package/dist/commands/cloud.js.map +1 -1
  13. package/dist/commands/login.d.ts +6 -0
  14. package/dist/commands/login.d.ts.map +1 -0
  15. package/dist/commands/login.js +25 -0
  16. package/dist/commands/login.js.map +1 -0
  17. package/dist/commands/logout.d.ts +6 -0
  18. package/dist/commands/logout.d.ts.map +1 -0
  19. package/dist/commands/logout.js +21 -0
  20. package/dist/commands/logout.js.map +1 -0
  21. package/dist/commands/onboard.d.ts +23 -0
  22. package/dist/commands/onboard.d.ts.map +1 -0
  23. package/dist/commands/onboard.js +142 -0
  24. package/dist/commands/onboard.js.map +1 -0
  25. package/dist/commands/pkg-install.d.ts +17 -0
  26. package/dist/commands/pkg-install.d.ts.map +1 -0
  27. package/dist/commands/pkg-install.js +144 -0
  28. package/dist/commands/pkg-install.js.map +1 -0
  29. package/dist/commands/pkg-list.d.ts +8 -0
  30. package/dist/commands/pkg-list.d.ts.map +1 -0
  31. package/dist/commands/pkg-list.js +70 -0
  32. package/dist/commands/pkg-list.js.map +1 -0
  33. package/dist/commands/pkg-remove.d.ts +10 -0
  34. package/dist/commands/pkg-remove.d.ts.map +1 -0
  35. package/dist/commands/pkg-remove.js +53 -0
  36. package/dist/commands/pkg-remove.js.map +1 -0
  37. package/dist/commands/pkg-update.d.ts +9 -0
  38. package/dist/commands/pkg-update.d.ts.map +1 -0
  39. package/dist/commands/pkg-update.js +124 -0
  40. package/dist/commands/pkg-update.js.map +1 -0
  41. package/dist/commands/team-sync.d.ts +14 -0
  42. package/dist/commands/team-sync.d.ts.map +1 -0
  43. package/dist/commands/team-sync.js +422 -0
  44. package/dist/commands/team-sync.js.map +1 -0
  45. package/dist/commands/whoami.d.ts +6 -0
  46. package/dist/commands/whoami.d.ts.map +1 -0
  47. package/dist/commands/whoami.js +29 -0
  48. package/dist/commands/whoami.js.map +1 -0
  49. package/dist/index.d.ts +1 -1
  50. package/dist/index.js +33 -10
  51. package/dist/index.js.map +1 -1
  52. package/dist/strategies/merge.js +2 -2
  53. package/dist/strategies/merge.js.map +1 -1
  54. package/dist/utils/auth.d.ts +27 -0
  55. package/dist/utils/auth.d.ts.map +1 -0
  56. package/dist/utils/auth.js +155 -0
  57. package/dist/utils/auth.js.map +1 -0
  58. package/dist/utils/cognito-session.d.ts +35 -0
  59. package/dist/utils/cognito-session.d.ts.map +1 -0
  60. package/dist/utils/cognito-session.js +72 -0
  61. package/dist/utils/cognito-session.js.map +1 -0
  62. package/dist/utils/git.js +1 -1
  63. package/dist/utils/git.js.map +1 -1
  64. package/dist/utils/hq-root.d.ts +10 -0
  65. package/dist/utils/hq-root.d.ts.map +1 -0
  66. package/dist/utils/hq-root.js +22 -0
  67. package/dist/utils/hq-root.js.map +1 -0
  68. package/dist/utils/integrity.d.ts +14 -0
  69. package/dist/utils/integrity.d.ts.map +1 -0
  70. package/dist/utils/integrity.js +40 -0
  71. package/dist/utils/integrity.js.map +1 -0
  72. package/dist/utils/manifest.d.ts.map +1 -1
  73. package/dist/utils/manifest.js +2 -5
  74. package/dist/utils/manifest.js.map +1 -1
  75. package/dist/utils/registry-client.d.ts +65 -0
  76. package/dist/utils/registry-client.d.ts.map +1 -0
  77. package/dist/utils/registry-client.js +102 -0
  78. package/dist/utils/registry-client.js.map +1 -0
  79. package/dist/utils/registry.d.ts +33 -0
  80. package/dist/utils/registry.d.ts.map +1 -0
  81. package/dist/utils/registry.js +58 -0
  82. package/dist/utils/registry.js.map +1 -0
  83. package/dist/utils/token-store.d.ts +28 -0
  84. package/dist/utils/token-store.d.ts.map +1 -0
  85. package/dist/utils/token-store.js +68 -0
  86. package/dist/utils/token-store.js.map +1 -0
  87. package/package.json +35 -45
  88. package/src/bin/hq-auth-refresh.ts +32 -0
  89. package/src/commands/add.ts +74 -0
  90. package/src/commands/auth.ts +153 -0
  91. package/src/commands/cloud.ts +125 -0
  92. package/src/commands/list.ts +66 -0
  93. package/src/commands/login.ts +29 -0
  94. package/src/commands/logout.ts +25 -0
  95. package/src/commands/pkg-install.ts +181 -0
  96. package/src/commands/pkg-list.ts +98 -0
  97. package/src/commands/pkg-remove.ts +71 -0
  98. package/src/commands/pkg-update.ts +189 -0
  99. package/src/commands/sync.ts +149 -0
  100. package/src/commands/team-sync.ts +629 -0
  101. package/src/commands/update.ts +71 -0
  102. package/src/commands/whoami.ts +38 -0
  103. package/src/index.ts +72 -0
  104. package/src/schemas/hq-package.schema.json +137 -0
  105. package/src/strategies/link.ts +62 -0
  106. package/src/strategies/merge.ts +142 -0
  107. package/src/types.ts +47 -0
  108. package/src/utils/auth.ts +193 -0
  109. package/src/utils/cognito-session.ts +94 -0
  110. package/src/utils/git.ts +74 -0
  111. package/src/utils/hq-root.ts +27 -0
  112. package/src/utils/integrity.ts +54 -0
  113. package/src/utils/manifest.ts +109 -0
  114. package/src/utils/registry-client.ts +204 -0
  115. package/src/utils/registry.ts +89 -0
  116. package/src/utils/token-store.ts +83 -0
  117. package/tsconfig.json +8 -0
  118. package/dist/commands/cloud-setup.d.ts +0 -19
  119. package/dist/commands/cloud-setup.d.ts.map +0 -1
  120. package/dist/commands/cloud-setup.js +0 -206
  121. package/dist/commands/cloud-setup.js.map +0 -1
  122. package/dist/commands/initial-upload.d.ts +0 -67
  123. package/dist/commands/initial-upload.d.ts.map +0 -1
  124. package/dist/commands/initial-upload.js +0 -205
  125. package/dist/commands/initial-upload.js.map +0 -1
  126. package/dist/sync-worker.d.ts +0 -11
  127. package/dist/sync-worker.d.ts.map +0 -1
  128. package/dist/sync-worker.js +0 -77
  129. package/dist/sync-worker.js.map +0 -1
  130. package/dist/utils/api-client.d.ts +0 -26
  131. package/dist/utils/api-client.d.ts.map +0 -1
  132. package/dist/utils/api-client.js +0 -87
  133. package/dist/utils/api-client.js.map +0 -1
  134. package/dist/utils/credentials.d.ts +0 -44
  135. package/dist/utils/credentials.d.ts.map +0 -1
  136. package/dist/utils/credentials.js +0 -101
  137. package/dist/utils/credentials.js.map +0 -1
  138. package/dist/utils/sync.d.ts +0 -125
  139. package/dist/utils/sync.d.ts.map +0 -1
  140. package/dist/utils/sync.js +0 -291
  141. package/dist/utils/sync.js.map +0 -1
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Registry YAML helpers — read/write packages/registry.yaml (US-005)
3
+ */
4
+ export interface RegistryEntry {
5
+ name: string;
6
+ slug: string;
7
+ version: string;
8
+ source: string;
9
+ license_key?: string;
10
+ scope?: string;
11
+ installed_at: string;
12
+ updated_at: string;
13
+ }
14
+ /**
15
+ * Read all entries from packages/registry.yaml.
16
+ * Returns an empty array if the file does not exist.
17
+ */
18
+ export declare function readRegistry(hqRoot: string): RegistryEntry[];
19
+ /**
20
+ * Write the full registry back to packages/registry.yaml.
21
+ */
22
+ export declare function writeRegistry(hqRoot: string, entries: RegistryEntry[]): void;
23
+ /**
24
+ * Add or update an entry in packages/registry.yaml.
25
+ * If an entry with the same slug exists, it is replaced.
26
+ */
27
+ export declare function addToRegistry(hqRoot: string, entry: RegistryEntry): void;
28
+ /**
29
+ * Remove an entry by slug from packages/registry.yaml.
30
+ * No-op if the slug is not found.
31
+ */
32
+ export declare function removeFromRegistry(hqRoot: string, slug: string): void;
33
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAUD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAQ5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,EAAE,GACvB,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,GACnB,IAAI,CASN;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,IAAI,CAGN"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Registry YAML helpers — read/write packages/registry.yaml (US-005)
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as yaml from 'js-yaml';
7
+ function registryPath(hqRoot) {
8
+ return path.resolve(hqRoot, 'packages', 'registry.yaml');
9
+ }
10
+ /**
11
+ * Read all entries from packages/registry.yaml.
12
+ * Returns an empty array if the file does not exist.
13
+ */
14
+ export function readRegistry(hqRoot) {
15
+ const filePath = registryPath(hqRoot);
16
+ if (!fs.existsSync(filePath)) {
17
+ return [];
18
+ }
19
+ const content = fs.readFileSync(filePath, 'utf-8');
20
+ const parsed = yaml.load(content);
21
+ return parsed?.packages ?? [];
22
+ }
23
+ /**
24
+ * Write the full registry back to packages/registry.yaml.
25
+ */
26
+ export function writeRegistry(hqRoot, entries) {
27
+ const filePath = registryPath(hqRoot);
28
+ const dir = path.dirname(filePath);
29
+ if (!fs.existsSync(dir)) {
30
+ fs.mkdirSync(dir, { recursive: true });
31
+ }
32
+ const content = yaml.dump({ packages: entries }, { lineWidth: 120, noRefs: true });
33
+ fs.writeFileSync(filePath, content, 'utf-8');
34
+ }
35
+ /**
36
+ * Add or update an entry in packages/registry.yaml.
37
+ * If an entry with the same slug exists, it is replaced.
38
+ */
39
+ export function addToRegistry(hqRoot, entry) {
40
+ const entries = readRegistry(hqRoot);
41
+ const idx = entries.findIndex((e) => e.slug === entry.slug);
42
+ if (idx >= 0) {
43
+ entries[idx] = entry;
44
+ }
45
+ else {
46
+ entries.push(entry);
47
+ }
48
+ writeRegistry(hqRoot, entries);
49
+ }
50
+ /**
51
+ * Remove an entry by slug from packages/registry.yaml.
52
+ * No-op if the slug is not found.
53
+ */
54
+ export function removeFromRegistry(hqRoot, slug) {
55
+ const entries = readRegistry(hqRoot).filter((e) => e.slug !== slug);
56
+ writeRegistry(hqRoot, entries);
57
+ }
58
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAiBhC,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAwB,CAAC;IACzD,OAAO,MAAM,EAAE,QAAQ,IAAI,EAAE,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,OAAwB;IAExB,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,EAAE,QAAQ,EAAE,OAAO,EAAE,EACrB,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CACjC,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,KAAoB;IAEpB,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,IAAY;IAEZ,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACpE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Auth token store — manages ~/.hq/auth.json with secure file permissions (US-004)
3
+ */
4
+ export interface AuthToken {
5
+ clerk_session_token: string;
6
+ user_id: string;
7
+ email: string;
8
+ expires_at: string;
9
+ }
10
+ /**
11
+ * Save auth token to ~/.hq/auth.json with 0600 permissions.
12
+ * Token content is never logged or written to stdout.
13
+ */
14
+ export declare function saveToken(token: AuthToken): Promise<void>;
15
+ /**
16
+ * Load auth token from ~/.hq/auth.json.
17
+ * Returns null if file is missing, unreadable, or contains invalid JSON.
18
+ */
19
+ export declare function loadToken(): Promise<AuthToken | null>;
20
+ /**
21
+ * Delete the auth token file.
22
+ */
23
+ export declare function clearToken(): Promise<void>;
24
+ /**
25
+ * Check if a token is expired or within 5 minutes of expiry.
26
+ */
27
+ export declare function isTokenExpired(token: AuthToken): boolean;
28
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/utils/token-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,SAAS;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAWD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/D;AAED;;;GAGG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAsB3D;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAQhD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAIxD"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Auth token store — manages ~/.hq/auth.json with secure file permissions (US-004)
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as os from 'os';
7
+ const HQ_DIR = path.join(os.homedir(), '.hq');
8
+ const AUTH_FILE = path.join(HQ_DIR, 'auth.json');
9
+ function ensureHqDir() {
10
+ if (!fs.existsSync(HQ_DIR)) {
11
+ fs.mkdirSync(HQ_DIR, { recursive: true, mode: 0o700 });
12
+ }
13
+ }
14
+ /**
15
+ * Save auth token to ~/.hq/auth.json with 0600 permissions.
16
+ * Token content is never logged or written to stdout.
17
+ */
18
+ export async function saveToken(token) {
19
+ ensureHqDir();
20
+ const content = JSON.stringify(token, null, 2);
21
+ fs.writeFileSync(AUTH_FILE, content, { mode: 0o600 });
22
+ }
23
+ /**
24
+ * Load auth token from ~/.hq/auth.json.
25
+ * Returns null if file is missing, unreadable, or contains invalid JSON.
26
+ */
27
+ export async function loadToken() {
28
+ try {
29
+ if (!fs.existsSync(AUTH_FILE)) {
30
+ return null;
31
+ }
32
+ const content = fs.readFileSync(AUTH_FILE, 'utf-8');
33
+ const parsed = JSON.parse(content);
34
+ // Validate required fields
35
+ if (typeof parsed.clerk_session_token !== 'string' ||
36
+ typeof parsed.user_id !== 'string' ||
37
+ typeof parsed.email !== 'string' ||
38
+ typeof parsed.expires_at !== 'string') {
39
+ return null;
40
+ }
41
+ return parsed;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Delete the auth token file.
49
+ */
50
+ export async function clearToken() {
51
+ try {
52
+ if (fs.existsSync(AUTH_FILE)) {
53
+ fs.unlinkSync(AUTH_FILE);
54
+ }
55
+ }
56
+ catch {
57
+ // Ignore errors — file may already be gone
58
+ }
59
+ }
60
+ /**
61
+ * Check if a token is expired or within 5 minutes of expiry.
62
+ */
63
+ export function isTokenExpired(token) {
64
+ const expiresAt = new Date(token.expires_at).getTime();
65
+ const bufferMs = 5 * 60 * 1000; // 5 minutes
66
+ return Date.now() >= expiresAt - bufferMs;
67
+ }
68
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/utils/token-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AASzB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEjD,SAAS,WAAW;IAClB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAgB;IAC9C,WAAW,EAAE,CAAC;IACd,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEnC,2BAA2B;QAC3B,IACE,OAAO,MAAM,CAAC,mBAAmB,KAAK,QAAQ;YAC9C,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;YAClC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;YAChC,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EACrC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,MAAmB,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACvD,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;IAC5C,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,SAAS,GAAG,QAAQ,CAAC;AAC5C,CAAC"}
package/package.json CHANGED
@@ -1,45 +1,35 @@
1
- {
2
- "name": "@indigoai-us/hq-cli",
3
- "version": "5.3.0",
4
- "description": "HQ management CLI — modules and cloud sync",
5
- "main": "dist/index.js",
6
- "bin": {
7
- "hq": "dist/index.js"
8
- },
9
- "scripts": {
10
- "build": "tsc",
11
- "typecheck": "tsc --noEmit",
12
- "clean": "rm -rf dist",
13
- "test": "vitest run",
14
- "test:watch": "vitest"
15
- },
16
- "dependencies": {
17
- "chalk": "^5.3.0",
18
- "commander": "^12.1.0",
19
- "js-yaml": "^4.1.0",
20
- "simple-git": "^3.27.0"
21
- },
22
- "devDependencies": {
23
- "@types/js-yaml": "^4.0.9",
24
- "@types/node": "^22.0.0",
25
- "typescript": "^5.7.0",
26
- "vitest": "^1.2.2"
27
- },
28
- "repository": {
29
- "type": "git",
30
- "url": "https://github.com/indigoai-us/hq.git",
31
- "directory": "packages/hq-cli"
32
- },
33
- "files": [
34
- "dist"
35
- ],
36
- "keywords": [
37
- "hq",
38
- "ai",
39
- "modules",
40
- "sync",
41
- "cloud"
42
- ],
43
- "license": "MIT",
44
- "type": "module"
45
- }
1
+ {
2
+ "name": "@indigoai-us/hq-cli",
3
+ "version": "5.4.0",
4
+ "description": "HQ by Indigo management CLI — modules and cloud sync",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "hq": "dist/index.js",
8
+ "hq-auth-refresh": "dist/bin/hq-auth-refresh.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "typecheck": "tsc --noEmit",
13
+ "clean": "rm -rf dist"
14
+ },
15
+ "dependencies": {
16
+ "@indigoai-us/hq-cloud": "5.1.0",
17
+ "chalk": "^5.3.0",
18
+ "commander": "^12.1.0",
19
+ "js-yaml": "^4.1.0",
20
+ "simple-git": "^3.27.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/js-yaml": "^4.0.9",
24
+ "@types/node": "^22.0.0",
25
+ "typescript": "^5.7.0"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/indigoai-us/hq.git",
30
+ "directory": "packages/hq-cli"
31
+ },
32
+ "keywords": ["hq", "ai", "modules", "sync", "cloud"],
33
+ "license": "MIT",
34
+ "type": "module"
35
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standalone `hq-auth-refresh` entry point.
5
+ *
6
+ * Exists so the deploy skill (.claude/skills/deploy/SKILL.md step 4) can
7
+ * shell out to a plain binary instead of a subcommand. Equivalent to
8
+ * `hq auth refresh`.
9
+ *
10
+ * Exit 0 on refresh, exit 1 if no cached session or refresh failed.
11
+ * Writes the refreshed tokens to ~/.hq/cognito-tokens.json.
12
+ */
13
+
14
+ import { refreshCachedSession } from "../utils/cognito-session.js";
15
+
16
+ async function main(): Promise<void> {
17
+ const result = await refreshCachedSession();
18
+ if (result.refreshed) {
19
+ process.exit(0);
20
+ }
21
+ if (result.reason) {
22
+ process.stderr.write(`hq-auth-refresh: ${result.reason}\n`);
23
+ }
24
+ process.exit(1);
25
+ }
26
+
27
+ main().catch((err) => {
28
+ process.stderr.write(
29
+ `hq-auth-refresh: ${err instanceof Error ? err.message : String(err)}\n`,
30
+ );
31
+ process.exit(1);
32
+ });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * hq modules add command (US-003)
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { findHqRoot, addModule, parseRepoName, isValidRepoUrl } from '../utils/manifest.js';
7
+ import type { ModuleDefinition, SyncStrategy } from '../types.js';
8
+
9
+ export function registerAddCommand(program: Command): void {
10
+ program
11
+ .command('add <repo-url>')
12
+ .description('Add a module to the manifest')
13
+ .option('--as <name>', 'Module name (defaults to repo name)')
14
+ .option('--branch <branch>', 'Git branch to track', 'main')
15
+ .option('--strategy <strategy>', 'Sync strategy: link | merge | copy', 'link')
16
+ .option('--path <mapping>', 'Path mapping src:dest (can repeat)', (val, prev: string[]) => [...prev, val], [])
17
+ .action(async (repoUrl: string, options: {
18
+ as?: string;
19
+ branch: string;
20
+ strategy: string;
21
+ path: string[];
22
+ }) => {
23
+ try {
24
+ // Validate repo URL
25
+ if (!isValidRepoUrl(repoUrl)) {
26
+ console.error('Error: Invalid repo URL. Must start with https:// or git@');
27
+ process.exit(1);
28
+ }
29
+
30
+ // Parse name
31
+ const name = options.as || parseRepoName(repoUrl);
32
+
33
+ // Validate strategy
34
+ const validStrategies: SyncStrategy[] = ['link', 'merge', 'copy'];
35
+ if (!validStrategies.includes(options.strategy as SyncStrategy)) {
36
+ console.error(`Error: Invalid strategy "${options.strategy}". Use: ${validStrategies.join(', ')}`);
37
+ process.exit(1);
38
+ }
39
+
40
+ // Parse path mappings
41
+ const paths = options.path.length > 0
42
+ ? options.path.map(p => {
43
+ const [src, dest] = p.split(':');
44
+ if (!src || !dest) {
45
+ throw new Error(`Invalid path mapping: ${p}. Format: src:dest`);
46
+ }
47
+ return { src, dest };
48
+ })
49
+ : [{ src: '.', dest: `workers/${name}` }]; // Default: entire repo to workers/
50
+
51
+ const module: ModuleDefinition = {
52
+ name,
53
+ repo: repoUrl,
54
+ branch: options.branch,
55
+ strategy: options.strategy as SyncStrategy,
56
+ paths,
57
+ };
58
+
59
+ const hqRoot = findHqRoot();
60
+ addModule(hqRoot, module);
61
+
62
+ console.log(`Added module "${name}"`);
63
+ console.log(` Repo: ${repoUrl}`);
64
+ console.log(` Branch: ${options.branch}`);
65
+ console.log(` Strategy: ${options.strategy}`);
66
+ console.log(` Paths: ${paths.map(p => `${p.src} -> ${p.dest}`).join(', ')}`);
67
+ console.log('\nRun "hq modules sync" to fetch and sync the module.');
68
+
69
+ } catch (error) {
70
+ console.error('Error:', error instanceof Error ? error.message : error);
71
+ process.exit(1);
72
+ }
73
+ });
74
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * `hq auth` — Cognito identity management for HQ.
3
+ *
4
+ * Subcommands:
5
+ * hq auth login — open the Cognito Hosted UI in the browser and cache tokens
6
+ * hq auth logout — clear the cached HQ session
7
+ * hq auth refresh — refresh the cached Cognito session (non-interactive)
8
+ * hq auth status — show whether a valid session is cached + expiry
9
+ *
10
+ * Sign-up is owned by the onboarding web app at
11
+ * https://onboarding.indigo-hq.com. `hq auth login` signs an existing account
12
+ * into this machine by writing ~/.hq/cognito-tokens.json; once cached, the
13
+ * session is kept fresh by `hq auth refresh` / `hq-auth-refresh` and consumed
14
+ * by the deploy + sync skills.
15
+ *
16
+ * Note: the top-level `hq login` command authenticates against the HQ package
17
+ * registry (Clerk), not Cognito — that is a different auth stack.
18
+ */
19
+
20
+ import { Command } from "commander";
21
+ import chalk from "chalk";
22
+ import {
23
+ browserLogin,
24
+ clearCachedTokens,
25
+ loadCachedTokens,
26
+ isExpiring,
27
+ CognitoAuthError,
28
+ } from "@indigoai-us/hq-cloud";
29
+ import {
30
+ DEFAULT_COGNITO,
31
+ refreshCachedSession,
32
+ } from "../utils/cognito-session.js";
33
+
34
+ /**
35
+ * Decode the (unverified) ID token payload for display purposes only.
36
+ * The token was just returned by Cognito's token endpoint, so its contents
37
+ * are trusted enough to print — we never use these claims for authorization.
38
+ */
39
+ function peekIdToken(
40
+ idToken: string,
41
+ ): { email?: string; sub?: string } {
42
+ try {
43
+ const payload = idToken.split(".")[1];
44
+ if (!payload) return {};
45
+ const pad =
46
+ payload.length % 4 === 0 ? "" : "=".repeat(4 - (payload.length % 4));
47
+ const normalized =
48
+ payload.replace(/-/g, "+").replace(/_/g, "/") + pad;
49
+ const decoded = JSON.parse(
50
+ Buffer.from(normalized, "base64").toString("utf-8"),
51
+ );
52
+ return { email: decoded.email, sub: decoded.sub };
53
+ } catch {
54
+ return {};
55
+ }
56
+ }
57
+
58
+ export function registerAuthCommands(program: Command): void {
59
+ const authCmd = program
60
+ .command("auth")
61
+ .description("Manage the local HQ Cognito session");
62
+
63
+ authCmd
64
+ .command("login")
65
+ .description(
66
+ "Sign in to HQ — opens the Cognito Hosted UI and caches tokens locally",
67
+ )
68
+ .action(async () => {
69
+ const existing = loadCachedTokens();
70
+ if (existing && !isExpiring(existing, 120)) {
71
+ const who = peekIdToken(existing.idToken).email ?? "cached session";
72
+ console.log(
73
+ chalk.green(`Already signed in (${who}). Run \`hq auth logout\` to switch accounts.`),
74
+ );
75
+ return;
76
+ }
77
+ try {
78
+ const tokens = await browserLogin(DEFAULT_COGNITO);
79
+ const who = peekIdToken(tokens.idToken).email ?? "HQ";
80
+ console.log(chalk.green(`Signed in as ${who}`));
81
+ console.log(
82
+ chalk.dim(` Token cached at ~/.hq/cognito-tokens.json (expires ${tokens.expiresAt})`),
83
+ );
84
+ } catch (err) {
85
+ const msg =
86
+ err instanceof CognitoAuthError
87
+ ? err.message
88
+ : err instanceof Error
89
+ ? err.message
90
+ : String(err);
91
+ console.error(chalk.red(`Login failed: ${msg}`));
92
+ console.error(
93
+ chalk.dim(
94
+ " If you do not have an account, sign up at https://onboarding.indigo-hq.com",
95
+ ),
96
+ );
97
+ process.exit(1);
98
+ }
99
+ });
100
+
101
+ authCmd
102
+ .command("logout")
103
+ .description("Clear the cached HQ Cognito session")
104
+ .action(() => {
105
+ const existing = loadCachedTokens();
106
+ if (!existing) {
107
+ console.log(chalk.yellow("No cached HQ session"));
108
+ return;
109
+ }
110
+ clearCachedTokens();
111
+ console.log(chalk.green("Signed out — removed ~/.hq/cognito-tokens.json"));
112
+ });
113
+
114
+ authCmd
115
+ .command("refresh")
116
+ .description(
117
+ "Refresh the cached Cognito session using the stored refresh token",
118
+ )
119
+ .action(async () => {
120
+ const result = await refreshCachedSession();
121
+ if (result.refreshed) {
122
+ console.log(chalk.green("HQ session refreshed"));
123
+ return;
124
+ }
125
+ console.error(
126
+ chalk.yellow(`No refresh: ${result.reason ?? "unknown"}`),
127
+ );
128
+ process.exit(1);
129
+ });
130
+
131
+ authCmd
132
+ .command("status")
133
+ .description("Show whether a valid HQ session is cached")
134
+ .action(() => {
135
+ const cached = loadCachedTokens();
136
+ if (!cached) {
137
+ console.log(chalk.yellow("No cached HQ session — run `hq auth login`"));
138
+ process.exit(1);
139
+ }
140
+ const who = peekIdToken(cached.idToken).email;
141
+ const expiring = isExpiring(cached);
142
+ const label = who ? `${who} — ` : "";
143
+ console.log(
144
+ expiring
145
+ ? chalk.yellow(
146
+ `${label}HQ session cached but expiring (expiresAt=${cached.expiresAt})`,
147
+ )
148
+ : chalk.green(
149
+ `${label}HQ session valid (expiresAt=${cached.expiresAt})`,
150
+ ),
151
+ );
152
+ });
153
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * hq sync commands — cloud sync management
3
+ * Bridges hq-cli to @indigoai-us/hq-cloud
4
+ */
5
+
6
+ import { Command } from "commander";
7
+ import { findHqRoot } from "../utils/manifest.js";
8
+
9
+ export function registerCloudCommands(program: Command): void {
10
+ program
11
+ .command("init")
12
+ .description("Authenticate with IndigoAI and set up cloud sync")
13
+ .action(async () => {
14
+ try {
15
+ const hqRoot = findHqRoot();
16
+ const { initSync } = await import("@indigoai-us/hq-cloud");
17
+ await initSync(hqRoot);
18
+ } catch (error) {
19
+ console.error(
20
+ "Error:",
21
+ error instanceof Error ? error.message : error
22
+ );
23
+ process.exit(1);
24
+ }
25
+ });
26
+
27
+ program
28
+ .command("start")
29
+ .description("Start the background sync daemon")
30
+ .action(async () => {
31
+ try {
32
+ const hqRoot = findHqRoot();
33
+ const { startDaemon } = await import("@indigoai-us/hq-cloud");
34
+ await startDaemon(hqRoot);
35
+ console.log("Sync daemon started. Use 'hq sync status' to check.");
36
+ } catch (error) {
37
+ console.error(
38
+ "Error:",
39
+ error instanceof Error ? error.message : error
40
+ );
41
+ process.exit(1);
42
+ }
43
+ });
44
+
45
+ program
46
+ .command("stop")
47
+ .description("Stop the sync daemon")
48
+ .action(async () => {
49
+ try {
50
+ const hqRoot = findHqRoot();
51
+ const { stopDaemon } = await import("@indigoai-us/hq-cloud");
52
+ await stopDaemon(hqRoot);
53
+ console.log("Sync daemon stopped.");
54
+ } catch (error) {
55
+ console.error(
56
+ "Error:",
57
+ error instanceof Error ? error.message : error
58
+ );
59
+ process.exit(1);
60
+ }
61
+ });
62
+
63
+ program
64
+ .command("status")
65
+ .description("Show sync status")
66
+ .action(async () => {
67
+ try {
68
+ const hqRoot = findHqRoot();
69
+ const { getStatus } = await import("@indigoai-us/hq-cloud");
70
+ const status = await getStatus(hqRoot);
71
+ console.log(` State: ${status.running ? "running" : "stopped"}`);
72
+ console.log(` Last sync: ${status.lastSync || "never"}`);
73
+ console.log(` Files: ${status.fileCount} tracked`);
74
+ console.log(` Bucket: ${status.bucket || "not configured"}`);
75
+ if (status.errors.length > 0) {
76
+ console.log(` Errors: ${status.errors.length}`);
77
+ for (const err of status.errors.slice(0, 5)) {
78
+ console.log(` - ${err}`);
79
+ }
80
+ }
81
+ } catch (error) {
82
+ console.error(
83
+ "Error:",
84
+ error instanceof Error ? error.message : error
85
+ );
86
+ process.exit(1);
87
+ }
88
+ });
89
+
90
+ program
91
+ .command("push")
92
+ .description("Force push all local changes to cloud")
93
+ .action(async () => {
94
+ try {
95
+ const hqRoot = findHqRoot();
96
+ const { pushAll } = await import("@indigoai-us/hq-cloud");
97
+ const result = await pushAll(hqRoot);
98
+ console.log(`Pushed ${result.filesUploaded} files to cloud.`);
99
+ } catch (error) {
100
+ console.error(
101
+ "Error:",
102
+ error instanceof Error ? error.message : error
103
+ );
104
+ process.exit(1);
105
+ }
106
+ });
107
+
108
+ program
109
+ .command("pull")
110
+ .description("Force pull all cloud changes to local")
111
+ .action(async () => {
112
+ try {
113
+ const hqRoot = findHqRoot();
114
+ const { pullAll } = await import("@indigoai-us/hq-cloud");
115
+ const result = await pullAll(hqRoot);
116
+ console.log(`Pulled ${result.filesDownloaded} files from cloud.`);
117
+ } catch (error) {
118
+ console.error(
119
+ "Error:",
120
+ error instanceof Error ? error.message : error
121
+ );
122
+ process.exit(1);
123
+ }
124
+ });
125
+ }