@thingd/cli 0.77.0 → 0.77.2

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 (68) hide show
  1. package/dist/commands/cloud.d.ts +3 -0
  2. package/dist/commands/cloud.d.ts.map +1 -0
  3. package/dist/commands/cloud.js +669 -0
  4. package/dist/commands/mcp-connect.d.ts +3 -0
  5. package/dist/commands/mcp-connect.d.ts.map +1 -0
  6. package/dist/commands/mcp-connect.js +154 -0
  7. package/dist/commands/sync.d.ts +3 -0
  8. package/dist/commands/sync.d.ts.map +1 -0
  9. package/dist/commands/sync.js +197 -0
  10. package/dist/dashboard/public/assets/favicon-CgGFvG_0.svg +4 -0
  11. package/dist/dashboard/public/assets/index-C6PkDB7y.css +1 -0
  12. package/dist/dashboard/public/assets/index-DrpfyClj.js +4 -0
  13. package/dist/dashboard/public/index.html +19 -0
  14. package/dist/dashboard/server.d.ts +6 -0
  15. package/dist/dashboard/server.d.ts.map +1 -0
  16. package/dist/dashboard/server.js +684 -0
  17. package/dist/data-movement.d.ts +6 -0
  18. package/dist/data-movement.d.ts.map +1 -0
  19. package/dist/data-movement.js +475 -0
  20. package/dist/doctor.d.ts +3 -0
  21. package/dist/doctor.d.ts.map +1 -0
  22. package/dist/doctor.js +108 -0
  23. package/dist/index.d.ts +48 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +1446 -0
  26. package/dist/install.d.ts +3 -0
  27. package/dist/install.d.ts.map +1 -0
  28. package/dist/install.js +229 -0
  29. package/dist/interactive.d.ts +2 -0
  30. package/dist/interactive.d.ts.map +1 -0
  31. package/dist/interactive.js +3039 -0
  32. package/dist/lib/cloud-api.d.ts +143 -0
  33. package/dist/lib/cloud-api.d.ts.map +1 -0
  34. package/dist/lib/cloud-api.js +207 -0
  35. package/dist/lib/cloud-config.d.ts +33 -0
  36. package/dist/lib/cloud-config.d.ts.map +1 -0
  37. package/dist/lib/cloud-config.js +42 -0
  38. package/dist/lib/mcp-config-writer.d.ts +26 -0
  39. package/dist/lib/mcp-config-writer.d.ts.map +1 -0
  40. package/dist/lib/mcp-config-writer.js +86 -0
  41. package/dist/lib/sync-config.d.ts +22 -0
  42. package/dist/lib/sync-config.d.ts.map +1 -0
  43. package/dist/lib/sync-config.js +25 -0
  44. package/dist/logo.d.ts +3 -0
  45. package/dist/logo.d.ts.map +1 -0
  46. package/dist/logo.js +8 -0
  47. package/dist/mcp/cluster.d.ts +69 -0
  48. package/dist/mcp/cluster.d.ts.map +1 -0
  49. package/dist/mcp/cluster.js +304 -0
  50. package/dist/mcp/config.d.ts +14 -0
  51. package/dist/mcp/config.d.ts.map +1 -0
  52. package/dist/mcp/config.js +67 -0
  53. package/dist/mcp/http.d.ts +35 -0
  54. package/dist/mcp/http.d.ts.map +1 -0
  55. package/dist/mcp/http.js +957 -0
  56. package/dist/mcp/index.d.ts +5 -0
  57. package/dist/mcp/index.d.ts.map +1 -0
  58. package/dist/mcp/index.js +3 -0
  59. package/dist/mcp-http.d.ts +3 -0
  60. package/dist/mcp-http.d.ts.map +1 -0
  61. package/dist/mcp-http.js +42 -0
  62. package/dist/mcp.d.ts +3 -0
  63. package/dist/mcp.d.ts.map +1 -0
  64. package/dist/mcp.js +28 -0
  65. package/dist/paths.d.ts +4 -0
  66. package/dist/paths.d.ts.map +1 -0
  67. package/dist/paths.js +14 -0
  68. package/package.json +2 -2
@@ -0,0 +1,143 @@
1
+ import type { CloudConfig } from "./cloud-config.js";
2
+ export type CloudProject = {
3
+ id: string;
4
+ name: string;
5
+ slug: string;
6
+ createdAt: string;
7
+ };
8
+ export type CloudInstance = {
9
+ id: string;
10
+ name: string;
11
+ slug: string;
12
+ mcpUrl: string;
13
+ createdAt: string;
14
+ };
15
+ export type CloudApiKey = {
16
+ id: string;
17
+ name: string;
18
+ prefix: string;
19
+ token?: string;
20
+ createdAt: string;
21
+ };
22
+ export type UserTokenDto = {
23
+ id: string;
24
+ name: string;
25
+ prefix: string;
26
+ projectAccess: string;
27
+ createdAt: string;
28
+ lastUsedAt: string | null;
29
+ revokedAt: string | null;
30
+ };
31
+ export type CloudOrganization = {
32
+ id: string;
33
+ name: string;
34
+ slug: string;
35
+ createdAt: string;
36
+ };
37
+ export type CloudOrganizationMember = {
38
+ id: string;
39
+ organizationId: string;
40
+ userId: string;
41
+ role: string;
42
+ invitedBy: string;
43
+ joinedAt: string;
44
+ };
45
+ export declare class CloudApiError extends Error {
46
+ status: number;
47
+ constructor(status: number, message: string);
48
+ }
49
+ export declare function getMe(config: CloudConfig): Promise<{
50
+ user: {
51
+ id: string;
52
+ email: string;
53
+ name: string;
54
+ role: string;
55
+ };
56
+ }>;
57
+ export declare function listProjects(config: CloudConfig): Promise<{
58
+ projects: CloudProject[];
59
+ }>;
60
+ export declare function createProject(config: CloudConfig, name: string, organizationId?: string): Promise<{
61
+ project: CloudProject;
62
+ }>;
63
+ export declare function listInstances(config: CloudConfig, projectId: string): Promise<{
64
+ instances: CloudInstance[];
65
+ }>;
66
+ export declare function createInstance(config: CloudConfig, projectId: string, name: string): Promise<{
67
+ instance: CloudInstance;
68
+ }>;
69
+ export declare function createApiKey(config: CloudConfig, projectId: string, name?: string): Promise<{
70
+ key: CloudApiKey;
71
+ token: string;
72
+ }>;
73
+ export declare function createOrganization(config: CloudConfig, name: string): Promise<{
74
+ organization: CloudOrganization;
75
+ }>;
76
+ export declare function listOrganizations(config: CloudConfig): Promise<{
77
+ organizations: CloudOrganization[];
78
+ }>;
79
+ export declare function getOrganization(config: CloudConfig, orgId: string): Promise<{
80
+ organization: CloudOrganization;
81
+ role: string;
82
+ }>;
83
+ export declare function listOrganizationMembers(config: CloudConfig, orgId: string): Promise<{
84
+ members: CloudOrganizationMember[];
85
+ }>;
86
+ export declare function addOrganizationMember(config: CloudConfig, orgId: string, userId: string, role?: string): Promise<{
87
+ member: CloudOrganizationMember;
88
+ }>;
89
+ export declare function removeOrganizationMember(config: CloudConfig, orgId: string, userId: string): Promise<{
90
+ ok: boolean;
91
+ }>;
92
+ export declare function startCliAuth(config: CloudConfig): Promise<{
93
+ code: string;
94
+ }>;
95
+ export declare function pollCliAuth(config: CloudConfig, code: string): Promise<{
96
+ token: string;
97
+ } | {
98
+ status: string;
99
+ }>;
100
+ export type ResolvedInstance = {
101
+ mcpUrl: string;
102
+ projectId: string;
103
+ projectSlug: string;
104
+ instanceSlug: string;
105
+ };
106
+ /**
107
+ * Derive the REST base URL from an MCP gateway URL.
108
+ * 'https://api.thingd.cloud/mcp/proj/inst' → 'https://api.thingd.cloud'
109
+ * 'https://api.thingd.cloud' → 'https://api.thingd.cloud' (idempotent)
110
+ */
111
+ export declare function deriveRestUrl(mcpUrl: string): string;
112
+ /**
113
+ * Fetch the first available cloud instance for the logged-in user.
114
+ * Returns null if no projects or instances exist.
115
+ */
116
+ export declare function resolveFirstInstance(config: CloudConfig): Promise<ResolvedInstance | null>;
117
+ export declare function createUserToken(config: CloudConfig, name: string, projectAccess?: string): Promise<{
118
+ token: string;
119
+ userToken: UserTokenDto;
120
+ }>;
121
+ export declare function listUserTokens(config: CloudConfig): Promise<{
122
+ userTokens: UserTokenDto[];
123
+ }>;
124
+ export declare function revokeUserToken(config: CloudConfig, tokenId: string): Promise<void>;
125
+ export declare function updateUserToken(config: CloudConfig, tokenId: string, updates: {
126
+ name?: string;
127
+ projectAccess?: string;
128
+ }): Promise<{
129
+ userToken: UserTokenDto;
130
+ }>;
131
+ /**
132
+ * Extract the token ID from a full user token string (md_user_<hexId>_<secret> → utk_<hexId>).
133
+ */
134
+ export declare function cleanupSessions(config: CloudConfig): Promise<{
135
+ removed: number;
136
+ }>;
137
+ export declare function parseUserTokenId(userToken: string): string | null;
138
+ /**
139
+ * Fetch all cloud instances across all projects for the logged-in user.
140
+ * Returns an empty array if no projects or instances exist.
141
+ */
142
+ export declare function resolveAllInstances(config: CloudConfig): Promise<ResolvedInstance[]>;
143
+ //# sourceMappingURL=cloud-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-api.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,207 @@
1
+ const DEFAULT_API_URL = "https://api.thingd.cloud";
2
+ export class CloudApiError extends Error {
3
+ status;
4
+ constructor(status, message) {
5
+ super(message);
6
+ this.status = status;
7
+ this.name = "CloudApiError";
8
+ }
9
+ }
10
+ function resolveAuthToken(config) {
11
+ return config.userToken ?? config.token ?? config.apiKey ?? "";
12
+ }
13
+ async function request(config, path, opts = {}) {
14
+ const url = `${config.url ?? DEFAULT_API_URL}${path}`;
15
+ const authToken = resolveAuthToken(config);
16
+ const headers = {
17
+ authorization: `Bearer ${authToken}`,
18
+ "content-type": "application/json",
19
+ };
20
+ const res = await fetch(url, {
21
+ method: opts.method ?? "GET",
22
+ headers,
23
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
24
+ });
25
+ if (!res.ok) {
26
+ if (res.status === 401) {
27
+ throw new CloudApiError(401, "Token expired or invalid. Run `thingd cloud login` again.");
28
+ }
29
+ const body = await res.json().catch(() => ({ message: res.statusText }));
30
+ throw new CloudApiError(res.status, body.message ?? res.statusText);
31
+ }
32
+ return res.json();
33
+ }
34
+ export async function getMe(config) {
35
+ return request(config, "/users/me");
36
+ }
37
+ export async function listProjects(config) {
38
+ return request(config, "/projects");
39
+ }
40
+ export async function createProject(config, name, organizationId) {
41
+ const body = { name };
42
+ if (organizationId) {
43
+ body.organizationId = organizationId;
44
+ }
45
+ return request(config, "/projects", { method: "POST", body });
46
+ }
47
+ export async function listInstances(config, projectId) {
48
+ return request(config, `/projects/${projectId}/instances`);
49
+ }
50
+ export async function createInstance(config, projectId, name) {
51
+ return request(config, `/projects/${projectId}/instances`, {
52
+ method: "POST",
53
+ body: { name },
54
+ });
55
+ }
56
+ export async function createApiKey(config, projectId, name) {
57
+ return request(config, `/projects/${projectId}/api-keys`, {
58
+ method: "POST",
59
+ body: { name: name ?? "thingd CLI" },
60
+ });
61
+ }
62
+ // ── Organization API ─────────────────────────────────────────────────
63
+ export async function createOrganization(config, name) {
64
+ return request(config, "/organizations", { method: "POST", body: { name } });
65
+ }
66
+ export async function listOrganizations(config) {
67
+ return request(config, "/organizations");
68
+ }
69
+ export async function getOrganization(config, orgId) {
70
+ return request(config, `/organizations/${orgId}`);
71
+ }
72
+ export async function listOrganizationMembers(config, orgId) {
73
+ return request(config, `/organizations/${orgId}/members`);
74
+ }
75
+ export async function addOrganizationMember(config, orgId, userId, role = "member") {
76
+ return request(config, `/organizations/${orgId}/members`, {
77
+ method: "POST",
78
+ body: { userId, role },
79
+ });
80
+ }
81
+ export async function removeOrganizationMember(config, orgId, userId) {
82
+ return request(config, `/organizations/${orgId}/members/${userId}`, {
83
+ method: "DELETE",
84
+ });
85
+ }
86
+ // ── CLI device code auth (unauthenticated) ──────────────────────────
87
+ async function requestUnauthenticated(apiUrl, path, body) {
88
+ const url = `${apiUrl}${path}`;
89
+ const res = await fetch(url, {
90
+ method: "POST",
91
+ headers: { "content-type": "application/json" },
92
+ body: JSON.stringify(body),
93
+ });
94
+ if (!res.ok) {
95
+ const errBody = await res.json().catch(() => ({ message: res.statusText }));
96
+ throw new CloudApiError(res.status, errBody.message ?? res.statusText);
97
+ }
98
+ return res.json();
99
+ }
100
+ export async function startCliAuth(config) {
101
+ return requestUnauthenticated(config.url ?? DEFAULT_API_URL, "/auth/cli/start", {});
102
+ }
103
+ export async function pollCliAuth(config, code) {
104
+ return requestUnauthenticated(config.url ?? DEFAULT_API_URL, "/auth/cli/poll", { code });
105
+ }
106
+ /**
107
+ * Derive the REST base URL from an MCP gateway URL.
108
+ * 'https://api.thingd.cloud/mcp/proj/inst' → 'https://api.thingd.cloud'
109
+ * 'https://api.thingd.cloud' → 'https://api.thingd.cloud' (idempotent)
110
+ */
111
+ export function deriveRestUrl(mcpUrl) {
112
+ return new URL(mcpUrl).origin;
113
+ }
114
+ /**
115
+ * Fetch the first available cloud instance for the logged-in user.
116
+ * Returns null if no projects or instances exist.
117
+ */
118
+ export async function resolveFirstInstance(config) {
119
+ try {
120
+ const { projects } = await listProjects(config);
121
+ for (const project of projects) {
122
+ try {
123
+ const { instances } = await listInstances(config, project.id);
124
+ const instance = instances[0];
125
+ if (instance?.mcpUrl) {
126
+ return {
127
+ mcpUrl: instance.mcpUrl,
128
+ projectId: project.id,
129
+ projectSlug: project.slug,
130
+ instanceSlug: instance.slug,
131
+ };
132
+ }
133
+ }
134
+ catch {
135
+ // Skip projects that fail to list instances
136
+ }
137
+ }
138
+ }
139
+ catch {
140
+ // API unreachable or token invalid
141
+ }
142
+ return null;
143
+ }
144
+ // ── User Token API ──────────────────────────────────────────────────
145
+ export async function createUserToken(config, name, projectAccess) {
146
+ return request(config, "/auth/user-tokens", {
147
+ method: "POST",
148
+ body: { name, projectAccess },
149
+ });
150
+ }
151
+ export async function listUserTokens(config) {
152
+ return request(config, "/auth/user-tokens");
153
+ }
154
+ export async function revokeUserToken(config, tokenId) {
155
+ await request(config, `/auth/user-tokens/${tokenId}`, { method: "DELETE" });
156
+ }
157
+ export async function updateUserToken(config, tokenId, updates) {
158
+ return request(config, `/auth/user-tokens/${tokenId}`, {
159
+ method: "PATCH",
160
+ body: updates,
161
+ });
162
+ }
163
+ /**
164
+ * Extract the token ID from a full user token string (md_user_<hexId>_<secret> → utk_<hexId>).
165
+ */
166
+ export async function cleanupSessions(config) {
167
+ return request(config, "/auth/sessions/cleanup", { method: "DELETE" });
168
+ }
169
+ export function parseUserTokenId(userToken) {
170
+ const match = /^md_user_([a-f0-9]{20})_/.exec(userToken);
171
+ if (!match?.[1]) {
172
+ return null;
173
+ }
174
+ return `utk_${match[1]}`;
175
+ }
176
+ /**
177
+ * Fetch all cloud instances across all projects for the logged-in user.
178
+ * Returns an empty array if no projects or instances exist.
179
+ */
180
+ export async function resolveAllInstances(config) {
181
+ const all = [];
182
+ try {
183
+ const { projects } = await listProjects(config);
184
+ for (const project of projects) {
185
+ try {
186
+ const { instances } = await listInstances(config, project.id);
187
+ for (const instance of instances) {
188
+ if (instance?.mcpUrl) {
189
+ all.push({
190
+ mcpUrl: instance.mcpUrl,
191
+ projectId: project.id,
192
+ projectSlug: project.slug,
193
+ instanceSlug: instance.slug,
194
+ });
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // Skip projects that fail to list instances
200
+ }
201
+ }
202
+ }
203
+ catch {
204
+ // API unreachable or token invalid
205
+ }
206
+ return all;
207
+ }
@@ -0,0 +1,33 @@
1
+ export type CloudConfig = {
2
+ /** Primary credential — user token (md_user_*) created on login. */
3
+ userToken?: string;
4
+ /** Email of the logged-in user. */
5
+ email?: string;
6
+ /** API base URL (defaults to https://api.thingd.cloud). */
7
+ url?: string;
8
+ /** Currently active organization context (set by `thingd cloud org use`). */
9
+ organizationId?: string;
10
+ /** Resolved MCP URL for the active cloud instance (auto-discovered or set by `thingd cloud instance use`). */
11
+ instanceUrl?: string;
12
+ /** Active project ID (set when instanceUrl is resolved). */
13
+ projectId?: string;
14
+ /** Active project slug (set when instanceUrl is resolved). */
15
+ projectSlug?: string;
16
+ /** Active instance slug (set when instanceUrl is resolved). */
17
+ instanceSlug?: string;
18
+ /** @deprecated Old JWT — still read as fallback if no userToken. */
19
+ token?: string;
20
+ /** @deprecated Old project API key — still read as fallback if no userToken or token. */
21
+ apiKey?: string;
22
+ };
23
+ export declare function cloudConfigPath(): string;
24
+ export declare function readCloudConfig(): CloudConfig | null;
25
+ export declare function writeCloudConfig(config: CloudConfig): void;
26
+ /**
27
+ * Returns the best available cloud MCP URL from saved config.
28
+ * Only returns URL when a concrete instance endpoint is available
29
+ * (instanceUrl) — the bare API url is not enough for MCP connections.
30
+ */
31
+ export declare function resolveCloudUrl(config: CloudConfig): string | undefined;
32
+ export declare function removeCloudConfig(): void;
33
+ //# sourceMappingURL=cloud-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8GAA8G;IAC9G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAUpD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAKvE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { defaultThingdDir, ensureThingdDir } from "../paths.js";
4
+ const CONFIG_FILE = "cloud-config.json";
5
+ export function cloudConfigPath() {
6
+ return join(defaultThingdDir(), CONFIG_FILE);
7
+ }
8
+ export function readCloudConfig() {
9
+ const path = cloudConfigPath();
10
+ if (!existsSync(path)) {
11
+ return null;
12
+ }
13
+ try {
14
+ return JSON.parse(readFileSync(path, "utf-8"));
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export function writeCloudConfig(config) {
21
+ ensureThingdDir();
22
+ writeFileSync(cloudConfigPath(), JSON.stringify(config, null, 2), "utf-8");
23
+ }
24
+ /**
25
+ * Returns the best available cloud MCP URL from saved config.
26
+ * Only returns URL when a concrete instance endpoint is available
27
+ * (instanceUrl) — the bare API url is not enough for MCP connections.
28
+ */
29
+ export function resolveCloudUrl(config) {
30
+ if (config.instanceUrl) {
31
+ return config.instanceUrl;
32
+ }
33
+ return undefined;
34
+ }
35
+ export function removeCloudConfig() {
36
+ try {
37
+ unlinkSync(cloudConfigPath());
38
+ }
39
+ catch {
40
+ // File may not exist
41
+ }
42
+ }
@@ -0,0 +1,26 @@
1
+ export type McpServerConfig = {
2
+ command: string;
3
+ args: string[];
4
+ } | {
5
+ url: string;
6
+ headers?: Record<string, string>;
7
+ };
8
+ export type McpServersBlock = {
9
+ mcpServers: Record<string, McpServerConfig>;
10
+ };
11
+ export declare function printMcpConfigJson(config: McpServerConfig): string;
12
+ export type ClaudeUpdateResult = {
13
+ updated: true;
14
+ path: string;
15
+ skipped?: undefined;
16
+ reason?: undefined;
17
+ } | {
18
+ updated?: undefined;
19
+ skipped: true;
20
+ reason: string;
21
+ path?: undefined;
22
+ };
23
+ export declare function getClaudeConfigPath(): string;
24
+ export declare function updateClaudeDesktopConfig(config: McpServerConfig): ClaudeUpdateResult;
25
+ export declare function updateAntigravityConfig(config: McpServerConfig): ClaudeUpdateResult;
26
+ //# sourceMappingURL=mcp-config-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config-writer.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-config-writer.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GACnC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC7C,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAOlE;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GACxE;IAAE,OAAO,CAAC,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AAE7E,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GAAG,kBAAkB,CA+BrF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,kBAAkB,CAkDnF"}
@@ -0,0 +1,86 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir, platform } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ export function printMcpConfigJson(config) {
5
+ const full = {
6
+ mcpServers: {
7
+ thingd: config,
8
+ },
9
+ };
10
+ return JSON.stringify(full, null, 2);
11
+ }
12
+ export function getClaudeConfigPath() {
13
+ return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
14
+ }
15
+ export function updateClaudeDesktopConfig(config) {
16
+ if (platform() !== "darwin") {
17
+ return { skipped: true, reason: "Claude Desktop auto-config is only supported on macOS." };
18
+ }
19
+ const configPath = getClaudeConfigPath();
20
+ if (!existsSync(configPath)) {
21
+ return {
22
+ skipped: true,
23
+ reason: `Config file not found at ${configPath}. Is Claude Desktop installed?`,
24
+ };
25
+ }
26
+ try {
27
+ const raw = readFileSync(configPath, "utf-8");
28
+ const existing = JSON.parse(raw);
29
+ const mcpServers = (existing.mcpServers ?? {});
30
+ mcpServers.thingd = config;
31
+ existing.mcpServers = mcpServers;
32
+ writeFileSync(configPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
33
+ return { updated: true, path: configPath };
34
+ }
35
+ catch (error) {
36
+ return {
37
+ skipped: true,
38
+ reason: `Failed to update config: ${error instanceof Error ? error.message : String(error)}`,
39
+ };
40
+ }
41
+ }
42
+ export function updateAntigravityConfig(config) {
43
+ const candidates = [
44
+ join(homedir(), ".gemini", "config", "mcp_config.json"),
45
+ join(homedir(), ".gemini", "antigravity-ide", "mcp_config.json"),
46
+ ];
47
+ let updatedAny = false;
48
+ const pathsUpdated = [];
49
+ let lastError = null;
50
+ for (const configPath of candidates) {
51
+ const dir = dirname(configPath);
52
+ if (existsSync(dir)) {
53
+ try {
54
+ let existing = {};
55
+ if (existsSync(configPath)) {
56
+ const raw = readFileSync(configPath, "utf-8").trim();
57
+ if (raw) {
58
+ existing = JSON.parse(raw);
59
+ }
60
+ }
61
+ const mcpServers = (existing.mcpServers ?? {});
62
+ mcpServers.thingd = config;
63
+ existing.mcpServers = mcpServers;
64
+ writeFileSync(configPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
65
+ updatedAny = true;
66
+ pathsUpdated.push(configPath);
67
+ }
68
+ catch (error) {
69
+ lastError = error instanceof Error ? error : new Error(String(error));
70
+ }
71
+ }
72
+ }
73
+ if (updatedAny) {
74
+ return { updated: true, path: pathsUpdated.join(" & ") };
75
+ }
76
+ if (lastError) {
77
+ return {
78
+ skipped: true,
79
+ reason: `Failed to update config: ${lastError.message}`,
80
+ };
81
+ }
82
+ return {
83
+ skipped: true,
84
+ reason: `Antigravity directory not found in ${candidates.map((c) => dirname(c)).join(" or ")}.`,
85
+ };
86
+ }
@@ -0,0 +1,22 @@
1
+ export type SyncRole = "source" | "replica";
2
+ export type SyncConfig = {
3
+ localUrl: string;
4
+ remoteUrl: string;
5
+ localToken?: string;
6
+ remoteToken?: string;
7
+ role?: SyncRole;
8
+ cursor: number;
9
+ paused?: boolean;
10
+ provider?: "self-hosted" | "thingd.cloud" | string;
11
+ projectId?: string;
12
+ instanceSlug?: string;
13
+ sourceId?: string;
14
+ allowCloudTarget?: boolean;
15
+ targetConfirmed?: boolean;
16
+ configHash?: string;
17
+ };
18
+ export declare function syncConfigPath(): string;
19
+ export declare function readSyncConfig(): SyncConfig | null;
20
+ export declare function writeSyncConfig(config: SyncConfig): void;
21
+ export declare function removeSyncConfig(): void;
22
+ //# sourceMappingURL=sync-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-config.d.ts","sourceRoot":"","sources":["../../src/lib/sync-config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,GAAG,cAAc,GAAG,MAAM,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,IAAI,UAAU,GAAG,IAAI,CAMlD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
@@ -0,0 +1,25 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ export function syncConfigPath() {
5
+ return join(homedir(), ".thingd", "sync.json");
6
+ }
7
+ export function readSyncConfig() {
8
+ try {
9
+ return JSON.parse(readFileSync(syncConfigPath(), "utf8"));
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function writeSyncConfig(config) {
16
+ const path = syncConfigPath();
17
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
18
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
19
+ }
20
+ export function removeSyncConfig() {
21
+ const config = readSyncConfig();
22
+ if (config) {
23
+ writeSyncConfig({ ...config, cursor: 0, paused: false });
24
+ }
25
+ }
package/dist/logo.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function logoText(): string;
2
+ export declare function logoLine(): string;
3
+ //# sourceMappingURL=logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.d.ts","sourceRoot":"","sources":["../src/logo.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAEjC"}
package/dist/logo.js ADDED
@@ -0,0 +1,8 @@
1
+ const orange = (s) => `\x1b[38;2;224;83;22m${s}\x1b[0m`;
2
+ const cyan = (s) => `\x1b[38;2;0;196;212m${s}\x1b[0m`;
3
+ export function logoText() {
4
+ return `${orange("{")}${cyan("t")}${cyan("h")}${cyan("i")}${cyan("n")}${cyan("g")}${orange(":")}${orange("d")}${orange("}")}`;
5
+ }
6
+ export function logoLine() {
7
+ return `${logoText()}\n`;
8
+ }