@kamilio/wire 0.1.0 → 0.1.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.
- package/README.md +50 -0
- package/dist/adapters/root.d.ts +1 -1
- package/dist/adapters/root.d.ts.map +1 -1
- package/dist/adapters/root.js +24 -30
- package/dist/adapters/root.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/executable.d.ts.map +1 -1
- package/dist/executable.js +2 -2
- package/dist/executable.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +5 -2
- package/dist/mcp.js.map +1 -1
- package/node_modules/provider-google-docs/dist/google-docs.d.ts.map +1 -1
- package/node_modules/provider-google-docs/dist/google-docs.js +3 -331
- package/node_modules/provider-google-docs/dist/google-docs.js.map +1 -1
- package/node_modules/provider-google-docs/src/google-docs.ts +3 -294
- package/node_modules/wire-core/dist/core/model.d.ts +1 -1
- package/node_modules/wire-core/dist/core/model.d.ts.map +1 -1
- package/node_modules/wire-core/dist/index.d.ts +1 -1
- package/node_modules/wire-core/dist/index.d.ts.map +1 -1
- package/node_modules/wire-core/dist/index.js +1 -1
- package/node_modules/wire-core/dist/index.js.map +1 -1
- package/node_modules/wire-core/dist/operations.d.ts.map +1 -1
- package/node_modules/wire-core/dist/operations.js +2 -3
- package/node_modules/wire-core/dist/operations.js.map +1 -1
- package/node_modules/wire-core/dist/storage/registry.d.ts.map +1 -1
- package/node_modules/wire-core/dist/storage/registry.js +208 -95
- package/node_modules/wire-core/dist/storage/registry.js.map +1 -1
- package/node_modules/wire-core/dist/storage/workspace.d.ts +0 -3
- package/node_modules/wire-core/dist/storage/workspace.d.ts.map +1 -1
- package/node_modules/wire-core/dist/storage/workspace.js +1 -10
- package/node_modules/wire-core/dist/storage/workspace.js.map +1 -1
- package/node_modules/wire-core/src/core/model.ts +1 -1
- package/node_modules/wire-core/src/index.ts +1 -1
- package/node_modules/wire-core/src/operations.ts +2 -3
- package/node_modules/wire-core/src/storage/registry.ts +15 -48
- package/node_modules/wire-core/src/storage/workspace.ts +1 -10
- package/package.json +16 -1
- package/src/adapters/root.ts +25 -29
- package/src/cli.ts +6 -3
- package/src/executable.ts +2 -2
- package/src/mcp.ts +6 -2
|
@@ -19,7 +19,7 @@ type ResourceIdRow = Readonly<{ resource_id: string }>;
|
|
|
19
19
|
type IdRow = Readonly<{ id: string }>;
|
|
20
20
|
type SqliteModule = Readonly<{ DatabaseSync: new (path: string) => DatabaseSync }>;
|
|
21
21
|
|
|
22
|
-
const require = createRequire(
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
23
|
|
|
24
24
|
function compareStrings(left: string, right: string): number {
|
|
25
25
|
const leftCodePoints = Array.from(left, (character) => character.codePointAt(0)!);
|
|
@@ -77,7 +77,7 @@ export class SqliteRegistry implements Registry {
|
|
|
77
77
|
|
|
78
78
|
constructor(path: string) {
|
|
79
79
|
this.path = realpathSyncParent(path);
|
|
80
|
-
|
|
80
|
+
using database = this.connect();
|
|
81
81
|
database.exec(`
|
|
82
82
|
PRAGMA journal_mode=DELETE;
|
|
83
83
|
PRAGMA foreign_keys=ON;
|
|
@@ -121,27 +121,11 @@ export class SqliteRegistry implements Registry {
|
|
|
121
121
|
);
|
|
122
122
|
CREATE INDEX IF NOT EXISTS resource_relationships_target ON resource_relationships(target_id);
|
|
123
123
|
`);
|
|
124
|
-
database.close();
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
async put(resource: Resource): Promise<Resource> {
|
|
128
127
|
const normalized = storageResource(resource);
|
|
129
|
-
|
|
130
|
-
const database = this.connect();
|
|
131
|
-
for (const identifier of normalized.identifiers) {
|
|
132
|
-
const row = database.prepare("SELECT resource_id FROM resource_identifiers WHERE service = ? AND identifier = ? AND resource_id != ?").get(identifier.service, identifier.identifier, normalized.id) as ResourceIdRow | undefined;
|
|
133
|
-
if (row !== undefined) {
|
|
134
|
-
database.close();
|
|
135
|
-
throw new Error(`${identifier.service}/${identifier.identifier}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
for (const url of normalized.urls) {
|
|
139
|
-
const row = database.prepare("SELECT resource_id FROM resource_urls WHERE url = ? AND resource_id != ?").get(url, normalized.id) as ResourceIdRow | undefined;
|
|
140
|
-
if (row !== undefined) {
|
|
141
|
-
database.close();
|
|
142
|
-
throw new Error(url);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
128
|
+
using database = this.connect();
|
|
145
129
|
database.exec("BEGIN");
|
|
146
130
|
database.prepare("INSERT INTO resources (id, type) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET type=excluded.type").run(normalized.id, normalized.type);
|
|
147
131
|
database.prepare("DELETE FROM resource_identifiers WHERE resource_id = ?").run(normalized.id);
|
|
@@ -160,76 +144,58 @@ export class SqliteRegistry implements Registry {
|
|
|
160
144
|
const insertRelationship = database.prepare("INSERT INTO resource_relationships (source_id, target_id, type, data_json) VALUES (?, ?, ?, ?)");
|
|
161
145
|
for (const relationship of normalized.relationships) insertRelationship.run(normalized.id, relationship.target_id, relationship.type, stableJsonCompact(relationship.data));
|
|
162
146
|
database.exec("COMMIT");
|
|
163
|
-
database.close();
|
|
164
147
|
return normalized;
|
|
165
148
|
}
|
|
166
149
|
|
|
167
150
|
async get(resourceId: string): Promise<Resource> {
|
|
168
|
-
|
|
169
|
-
const row = database.prepare("SELECT id FROM resources WHERE id = ?").get(resourceId) as IdRow | undefined;
|
|
170
|
-
if (row === undefined) {
|
|
171
|
-
database.close();
|
|
172
|
-
throw missingResource(resourceId);
|
|
173
|
-
}
|
|
151
|
+
using database = this.connect();
|
|
174
152
|
database.exec("BEGIN");
|
|
175
153
|
const resource = this.getResource(database, resourceId);
|
|
176
154
|
database.exec("COMMIT");
|
|
177
|
-
database.close();
|
|
178
155
|
return resource;
|
|
179
156
|
}
|
|
180
157
|
|
|
181
158
|
async findByIdentifier(service: string, identifier: string): Promise<Resource> {
|
|
182
|
-
|
|
159
|
+
using database = this.connect();
|
|
183
160
|
database.exec("BEGIN");
|
|
184
161
|
const row = database.prepare("SELECT resource_id FROM resource_identifiers WHERE service = ? AND identifier = ?").get(service, identifier) as ResourceIdRow | undefined;
|
|
185
|
-
if (row === undefined)
|
|
186
|
-
database.close();
|
|
187
|
-
throw missingIdentifier(service, identifier);
|
|
188
|
-
}
|
|
162
|
+
if (row === undefined) throw missingIdentifier(service, identifier);
|
|
189
163
|
const resource = this.getResource(database, row.resource_id);
|
|
190
164
|
database.exec("COMMIT");
|
|
191
|
-
database.close();
|
|
192
165
|
return resource;
|
|
193
166
|
}
|
|
194
167
|
|
|
195
168
|
async findByUrl(url: string): Promise<Resource> {
|
|
196
|
-
|
|
169
|
+
using database = this.connect();
|
|
197
170
|
database.exec("BEGIN");
|
|
198
171
|
const row = database.prepare("SELECT resource_id FROM resource_urls WHERE url = ?").get(url) as ResourceIdRow | undefined;
|
|
199
|
-
if (row === undefined)
|
|
200
|
-
database.close();
|
|
201
|
-
throw missingUrl(url);
|
|
202
|
-
}
|
|
172
|
+
if (row === undefined) throw missingUrl(url);
|
|
203
173
|
const resource = this.getResource(database, row.resource_id);
|
|
204
174
|
database.exec("COMMIT");
|
|
205
|
-
database.close();
|
|
206
175
|
return resource;
|
|
207
176
|
}
|
|
208
177
|
|
|
209
178
|
async findByPath(path: string): Promise<readonly Resource[]> {
|
|
210
|
-
|
|
179
|
+
using database = this.connect();
|
|
211
180
|
database.exec("BEGIN");
|
|
212
181
|
const rows = database.prepare("SELECT DISTINCT resource_id FROM filesystem_links WHERE path = ? ORDER BY resource_id").all(path.replaceAll(sep, "/")) as ResourceIdRow[];
|
|
213
182
|
const resources = rows.map((row) => this.getResource(database, row.resource_id));
|
|
214
183
|
database.exec("COMMIT");
|
|
215
|
-
database.close();
|
|
216
184
|
return resources;
|
|
217
185
|
}
|
|
218
186
|
|
|
219
187
|
async listResources(): Promise<readonly Resource[]> {
|
|
220
|
-
|
|
188
|
+
using database = this.connect();
|
|
221
189
|
database.exec("BEGIN");
|
|
222
190
|
const rows = database.prepare("SELECT id FROM resources ORDER BY id").all() as IdRow[];
|
|
223
191
|
const resources = rows.map((row) => this.getResource(database, row.id));
|
|
224
192
|
database.exec("COMMIT");
|
|
225
|
-
database.close();
|
|
226
193
|
return resources;
|
|
227
194
|
}
|
|
228
195
|
|
|
229
196
|
async delete(resourceId: string): Promise<void> {
|
|
230
|
-
|
|
197
|
+
using database = this.connect();
|
|
231
198
|
database.prepare("DELETE FROM resources WHERE id = ?").run(resourceId);
|
|
232
|
-
database.close();
|
|
233
199
|
}
|
|
234
200
|
|
|
235
201
|
private getResource(database: DatabaseSync, resourceId: string): Resource {
|
|
@@ -279,9 +245,10 @@ export class FileRegistry implements Registry {
|
|
|
279
245
|
if (existingUrls.has(url)) throw new Error(url);
|
|
280
246
|
}
|
|
281
247
|
const tempPath = join(this.path, randomUUID());
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
248
|
+
{
|
|
249
|
+
await using handle = await open(tempPath, "wx", 0o600);
|
|
250
|
+
await handle.writeFile(`${stableJsonPretty(normalized)}\n`, "utf8");
|
|
251
|
+
}
|
|
285
252
|
await rename(tempPath, this.resourcePath(normalized.id));
|
|
286
253
|
return normalized;
|
|
287
254
|
}
|
|
@@ -9,15 +9,6 @@ import { FileRegistry, SqliteRegistry } from "./registry.js";
|
|
|
9
9
|
|
|
10
10
|
export type { InitializedWire, WireBackend, WireConfig } from "../ports.js";
|
|
11
11
|
|
|
12
|
-
export const defaultWireBackend = "files" satisfies WireBackend;
|
|
13
|
-
export const defaultWireRegistryPath = "records";
|
|
14
|
-
|
|
15
|
-
export function registryPathForBackend(backend: WireBackend): string {
|
|
16
|
-
if (backend === "files") return "records";
|
|
17
|
-
if (backend === "sqlite") return "registry.sqlite3";
|
|
18
|
-
throw new Error(backend);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
function canonicalPath(path: string): string {
|
|
22
13
|
const resolved = resolve(path);
|
|
23
14
|
if (existsSync(resolved)) return realpathSync(resolved);
|
|
@@ -85,7 +76,7 @@ export async function switchWireBackend(path: string, home: string): Promise<Swi
|
|
|
85
76
|
const source = config.backend === "sqlite" ? new SqliteRegistry(fromPath) : new FileRegistry(fromPath);
|
|
86
77
|
const resources = await source.listResources();
|
|
87
78
|
const to = config.backend === "sqlite" ? "files" : "sqlite";
|
|
88
|
-
const targetRelativePath =
|
|
79
|
+
const targetRelativePath = to === "sqlite" ? "registry.sqlite3" : "records";
|
|
89
80
|
const toPath = join(wireRoot, targetRelativePath);
|
|
90
81
|
await rm(toPath, { recursive: true, force: true });
|
|
91
82
|
const target = to === "sqlite" ? new SqliteRegistry(toPath) : new FileRegistry(toPath);
|
package/package.json
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kamilio/wire",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Attach Notion, Google Docs, Slack, Gmail, Asana, ChatGPT, and Zoom resources as local Markdown you can sync.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown",
|
|
7
|
+
"sync",
|
|
8
|
+
"notion",
|
|
9
|
+
"google-docs",
|
|
10
|
+
"slack",
|
|
11
|
+
"gmail",
|
|
12
|
+
"asana",
|
|
13
|
+
"chatgpt",
|
|
14
|
+
"zoom",
|
|
15
|
+
"mcp",
|
|
16
|
+
"cli",
|
|
17
|
+
"agent-tools"
|
|
18
|
+
],
|
|
5
19
|
"type": "module",
|
|
6
20
|
"bin": {
|
|
7
21
|
"wire": "bin/wire.mjs",
|
|
@@ -34,6 +48,7 @@
|
|
|
34
48
|
}
|
|
35
49
|
},
|
|
36
50
|
"files": [
|
|
51
|
+
"README.md",
|
|
37
52
|
"bin",
|
|
38
53
|
"dist",
|
|
39
54
|
"src"
|
package/src/adapters/root.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UserError, defineCommand, defineGroup, S, type Group } from "toolcraft";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { FetchedDocument, InitializedWire, Resource, SwitchedWireBackend, Wire, WireBackend, WireResult, WireWatchSession } from "wire-core";
|
|
4
4
|
import type { Auth, AuthResult, AuthService } from "../auth.js";
|
|
5
5
|
|
|
6
6
|
export type WireRoot = Group;
|
|
@@ -116,22 +116,20 @@ function formatWireCountsRich(result: WireResult, primitives: any): string {
|
|
|
116
116
|
|
|
117
117
|
function formatWireResult(result: WireResult): string {
|
|
118
118
|
const local = lineText(primaryFilesystemLink(result.resource).path);
|
|
119
|
-
const remote = lineText(result.summary.remote);
|
|
120
119
|
const titleText = lineText(wireTitle(result.resource));
|
|
121
120
|
if (result.summary.action === "failed") return [`failed ${titleText}`, `local: ${local}`, formatMultilineField("error", result.summary.error!)].join("\n");
|
|
122
|
-
const changed = result.summary.
|
|
121
|
+
const changed = result.summary.added !== 0 || result.summary.modified !== 0 || result.summary.removed !== 0;
|
|
123
122
|
const title = changed ? `${result.summary.action} ${formatWireCounts(result)} ${titleText}` : `${result.summary.action} ${titleText}`;
|
|
124
|
-
return [title,
|
|
123
|
+
return [title, `local: ${local}`].join("\n");
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
function formatWireResultRich(result: WireResult, primitives: any): string {
|
|
128
127
|
const local = lineText(primaryFilesystemLink(result.resource).path);
|
|
129
|
-
const remote = lineText(result.summary.remote);
|
|
130
128
|
const titleText = lineText(wireTitle(result.resource));
|
|
131
129
|
if (result.summary.action === "failed") return [`failed ${titleText}`, `local: ${local}`, formatMultilineField("error", result.summary.error!)].join("\n");
|
|
132
|
-
const changed = result.summary.
|
|
130
|
+
const changed = result.summary.added !== 0 || result.summary.modified !== 0 || result.summary.removed !== 0;
|
|
133
131
|
const title = changed ? `${result.summary.action} ${formatWireCountsRich(result, primitives)} ${titleText}` : `${result.summary.action} ${titleText}`;
|
|
134
|
-
return [title,
|
|
132
|
+
return [title, `local: ${local}`].join("\n");
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
function formatMultilineField(label: string, value: string): string {
|
|
@@ -195,12 +193,12 @@ function watchSessionJson(value: WireWatchSession) {
|
|
|
195
193
|
}
|
|
196
194
|
|
|
197
195
|
function wireUserErrorMessage(message: string): boolean {
|
|
198
|
-
return /^Unsupported source URL: /.test(message) || /^Wire workspace not initialized\. Run `wire init` or `wire <url>` first\.$/.test(message) || /^Wire workspace already initialized with (sqlite|files) registry at .+\. Existing registries are not overwritten\.$/.test(message) || /^Resource path is not registered: /.test(message) || /^Resource (path |identifier |URL )?not found: /.test(message) || /^Ambiguous resource path /.test(message) || /^Login not saved$/.test(message) || /authentication (is missing|is missing or expired|failed|expired).*Run `wire (asana|chatgpt|gmail|google-docs|notion|slack|zoom) login`/i.test(message) || /^Asana API /.test(message) || /^(GET|POST|PUT|DELETE) \/.* failed: \d+ /.test(message) || /^(Invalid Asana project heading|Asana section appears|Asana milestone appears|Asana task appears|Asana subtask appears|Unsupported Asana Markdown|Duplicate Asana identity|Missing Asana project heading|Asana project identity changed|Unknown Asana identity|Conflicting Asana edits)/.test(message) || /^ChatGPT conversation download failed\. Run `wire chatgpt login`\./.test(message) || /^Gmail API .+ failed: HTTP \d+ /.test(message) || /^Slack API [\w.]+ failed: /.test(message) || /^Zoom Hub (.+ failed: HTTP \d+|file .+ was not returned by batch_get)/.test(message) || /^(Markdown document requires a first heading|Markdown and Notion changed since last sync|lossless Markdown headings deeper than level 3 are not supported)$/.test(message) || /^Indented Notion Markdown block has no parent at indent \d+$/.test(message) || /^POST [A-Za-z0-9]+ failed: \d+ /.test(message) || /^Google (Docs|Sheets
|
|
196
|
+
return /^Unsupported source URL: /.test(message) || /^Wire workspace not initialized\. Run `wire init` or `wire <url>` first\.$/.test(message) || /^Wire workspace already initialized with (sqlite|files) registry at .+\. Existing registries are not overwritten\.$/.test(message) || /^Resource path is not registered: /.test(message) || /^Resource (path |identifier |URL )?not found: /.test(message) || /^Ambiguous resource path /.test(message) || /^Login not saved$/.test(message) || /authentication (is missing|is missing or expired|failed|expired).*Run `wire (asana|chatgpt|gmail|google-docs|notion|slack|zoom) login`/i.test(message) || /^Asana API /.test(message) || /^(GET|POST|PUT|DELETE) \/.* failed: \d+ /.test(message) || /^(Invalid Asana project heading|Asana section appears|Asana milestone appears|Asana task appears|Asana subtask appears|Unsupported Asana Markdown|Duplicate Asana identity|Missing Asana project heading|Asana project identity changed|Unknown Asana identity|Conflicting Asana edits)/.test(message) || /^ChatGPT conversation download failed\. Run `wire chatgpt login`\./.test(message) || /^Gmail API .+ failed: HTTP \d+ /.test(message) || /^Slack API [\w.]+ failed: /.test(message) || /^Zoom Hub (.+ failed: HTTP \d+|file .+ was not returned by batch_get)/.test(message) || /^(Markdown document requires a first heading|Markdown and Notion changed since last sync|lossless Markdown headings deeper than level 3 are not supported)$/.test(message) || /^Indented Notion Markdown block has no parent at indent \d+$/.test(message) || /^POST [A-Za-z0-9]+ failed: \d+ /.test(message) || /^Google (Docs|Sheets) changed remotely and locally\./.test(message) || /^Google Docs local edit cannot be mapped to the live document text$/.test(message) || /^Google Docs sync cannot upload formatting-only Markdown edits$/.test(message) || /^Google (Docs|Sheets) editor did not include save metadata$/.test(message) || /^Google (Docs Markdown|Sheets CSV) export (did not include a filename|failed: HTTP \d+)$/.test(message) || /^Google (Docs|Sheets) save (failed: |verification failed$)/.test(message) || /^Google sync base must /.test(message) || /^Google Sheets sync base /.test(message) || /^Google Sheets sync cannot upload formula-like cell text at row \d+, column \d+\nPrefix it with an apostrophe or rewrite it as plain text before syncing\.$/.test(message) || /^Google Sheets sync requires a Markdown table/.test(message) || /^Google Sheets sync requires every Markdown table row to have the same number of cells$/.test(message) || /^Google Sheets sync requires every Markdown table row to have \d+ cells: line \d+ has \d+$/.test(message);
|
|
199
197
|
}
|
|
200
198
|
|
|
201
199
|
function wireUserErrorDisplayMessage(message: string): string {
|
|
202
200
|
const unsupportedSource = /^Unsupported source URL: ([\s\S]+)$/.exec(message);
|
|
203
|
-
if (unsupportedSource !== null) return `unsupported source\nurl: ${lineText(unsupportedSource[1]!)}\nsupported: Asana, ChatGPT, Gmail, Google Docs/Sheets
|
|
201
|
+
if (unsupportedSource !== null) return `unsupported source\nurl: ${lineText(unsupportedSource[1]!)}\nsupported: Asana, ChatGPT, Gmail, Google Docs/Sheets, Notion, Slack, Zoom`;
|
|
204
202
|
const missingWorkspace = /^Wire workspace not initialized\. Run `wire init` or `wire <url>` first\.$/.exec(message);
|
|
205
203
|
if (missingWorkspace !== null) return "workspace not initialized\nrun: wire init\nattach: wire <url>";
|
|
206
204
|
const existingWorkspace = /^Wire workspace already initialized with (sqlite|files) registry at ([\s\S]+)\. Existing registries are not overwritten\.$/.exec(message);
|
|
@@ -237,32 +235,30 @@ function wireUserErrorDisplayMessage(message: string): string {
|
|
|
237
235
|
if (notionIndentedBlock !== null) return `local markdown invalid\nservice: Notion\nindent: ${notionIndentedBlock[1]!}\ndetail: indented block has no parent`;
|
|
238
236
|
const chatgptDownload = /^ChatGPT conversation download failed\. Run `wire chatgpt login`\.([\s\S]*)$/.exec(message);
|
|
239
237
|
if (chatgptDownload !== null) return `download failed\nservice: ChatGPT\nlogin: wire chatgpt login\ndetail: ${lineText(chatgptDownload[1]!)}`;
|
|
240
|
-
const googleExportMissingFilename = /^Google (Docs Markdown|Sheets CSV
|
|
241
|
-
if (googleExportMissingFilename !== null) return `export failed\nservice: Google Docs/Sheets
|
|
242
|
-
const googleExportHttp = /^Google (Docs Markdown|Sheets CSV
|
|
243
|
-
if (googleExportHttp !== null) return `export failed\nservice: Google Docs/Sheets
|
|
238
|
+
const googleExportMissingFilename = /^Google (Docs Markdown|Sheets CSV) export did not include a filename$/.exec(message);
|
|
239
|
+
if (googleExportMissingFilename !== null) return `export failed\nservice: Google Docs/Sheets\nsource: Google ${googleExportMissingFilename[1]!}\ndetail: missing filename`;
|
|
240
|
+
const googleExportHttp = /^Google (Docs Markdown|Sheets CSV) export failed: HTTP (\d+)(?: ([\s\S]+))?$/.exec(message);
|
|
241
|
+
if (googleExportHttp !== null) return `export failed\nservice: Google Docs/Sheets\nsource: Google ${googleExportHttp[1]!}\nstatus: HTTP ${googleExportHttp[2]!}${googleExportHttp[3] === undefined ? "" : `\ndetail: ${lineText(googleExportHttp[3]!)}`}`;
|
|
244
242
|
const googleEditorMetadata = /^Google (Docs|Sheets) editor did not include save metadata$/.exec(message);
|
|
245
|
-
if (googleEditorMetadata !== null) return `save metadata missing\nservice: Google Docs/Sheets
|
|
243
|
+
if (googleEditorMetadata !== null) return `save metadata missing\nservice: Google Docs/Sheets\nsource: Google ${googleEditorMetadata[1]!} editor`;
|
|
246
244
|
const googleSaveFailed = /^Google (Docs|Sheets) save failed: ([\s\S]+)$/.exec(message);
|
|
247
|
-
if (googleSaveFailed !== null) return `save failed\nservice: Google Docs/Sheets
|
|
245
|
+
if (googleSaveFailed !== null) return `save failed\nservice: Google Docs/Sheets\nsource: Google ${googleSaveFailed[1]!}\ndetail: ${lineText(googleSaveFailed[2]!)}`;
|
|
248
246
|
const googleSaveVerification = /^Google (Docs|Sheets) save verification failed$/.exec(message);
|
|
249
|
-
if (googleSaveVerification !== null) return `save verification failed\nservice: Google Docs/Sheets
|
|
250
|
-
const googleConflict = /^Google (Docs|Sheets
|
|
251
|
-
if (googleConflict !== null) return `sync conflict\nservice: Google Docs/Sheets
|
|
247
|
+
if (googleSaveVerification !== null) return `save verification failed\nservice: Google Docs/Sheets\nsource: Google ${googleSaveVerification[1]!}`;
|
|
248
|
+
const googleConflict = /^Google (Docs|Sheets) changed remotely and locally\. Resolve the conflict in Google (Docs|Sheets) or the local Markdown file before syncing again\.$/.exec(message);
|
|
249
|
+
if (googleConflict !== null) return `sync conflict\nservice: Google Docs/Sheets\nsource: Google ${googleConflict[1]!}\nresolve: edit Google ${googleConflict[2]!} or local Markdown, then sync again`;
|
|
252
250
|
const googleDocsMapping = /^Google Docs local edit cannot be mapped to the live document text$/.exec(message);
|
|
253
|
-
if (googleDocsMapping !== null) return "local edit not mappable\nservice: Google Docs/Sheets
|
|
251
|
+
if (googleDocsMapping !== null) return "local edit not mappable\nservice: Google Docs/Sheets\nsource: Google Docs\nresolve: edit Google Docs or simplify local Markdown";
|
|
254
252
|
const googleFormattingOnly = /^Google Docs sync cannot upload formatting-only Markdown edits$/.exec(message);
|
|
255
|
-
if (googleFormattingOnly !== null) return "local edit not uploadable\nservice: Google Docs/Sheets
|
|
256
|
-
const googleSlidesDownloadOnly = /^Google Slides sync is download-only\. Revert local edits or use `wire download <url>` for a fresh copy\.$/.exec(message);
|
|
257
|
-
if (googleSlidesDownloadOnly !== null) return "local edit not uploadable\nservice: Google Docs/Sheets/Slides\nsource: Google Slides\ndetail: download-only";
|
|
253
|
+
if (googleFormattingOnly !== null) return "local edit not uploadable\nservice: Google Docs/Sheets\nsource: Google Docs\ndetail: formatting-only Markdown edit";
|
|
258
254
|
const googleSyncBase = /^Google sync base must ([\s\S]+)$/.exec(message);
|
|
259
|
-
if (googleSyncBase !== null) return `sync base invalid\nservice: Google Docs/Sheets
|
|
255
|
+
if (googleSyncBase !== null) return `sync base invalid\nservice: Google Docs/Sheets\ndetail: must ${lineText(googleSyncBase[1]!)}`;
|
|
260
256
|
const googleSheetsSyncBase = /^Google Sheets sync base ([\s\S]+)$/.exec(message);
|
|
261
|
-
if (googleSheetsSyncBase !== null) return `sync base invalid\nservice: Google Docs/Sheets
|
|
257
|
+
if (googleSheetsSyncBase !== null) return `sync base invalid\nservice: Google Docs/Sheets\nsource: Google Sheets\ndetail: ${lineText(googleSheetsSyncBase[1]!)}`;
|
|
262
258
|
const googleSheetsFormulaCell = /^Google Sheets sync cannot upload formula-like cell text at row (\d+), column (\d+)\nPrefix it with an apostrophe or rewrite it as plain text before syncing\.$/.exec(message);
|
|
263
|
-
if (googleSheetsFormulaCell !== null) return `formula-like cell blocked\nservice: Google Docs/Sheets
|
|
259
|
+
if (googleSheetsFormulaCell !== null) return `formula-like cell blocked\nservice: Google Docs/Sheets\nsource: Google Sheets\ncell: row ${googleSheetsFormulaCell[1]!}, column ${googleSheetsFormulaCell[2]!}\nresolve: prefix with an apostrophe or rewrite as plain text`;
|
|
264
260
|
const googleSheetsTable = /^Google Sheets sync requires (a Markdown table(?: separator row)?|every Markdown table row to have the same number of cells|every Markdown table row to have \d+ cells: line \d+ has \d+)$/.exec(message);
|
|
265
|
-
if (googleSheetsTable !== null) return `local table invalid\nservice: Google Docs/Sheets
|
|
261
|
+
if (googleSheetsTable !== null) return `local table invalid\nservice: Google Docs/Sheets\nsource: Google Sheets\ndetail: requires ${lineText(googleSheetsTable[1]!)}`;
|
|
266
262
|
const missingPath = /^Resource path (not found|is not registered): ([\s\S]+)$/.exec(message);
|
|
267
263
|
if (missingPath !== null) return `${missingPath[1] === "not found" ? "resource not found" : "resource not registered"}\npath: ${lineText(missingPath[2]!)}`;
|
|
268
264
|
const missingResource = /^Resource (identifier |URL )?not found: ([\s\S]+)$/.exec(message);
|
|
@@ -294,7 +290,7 @@ function serviceTitle(service: AuthService): string {
|
|
|
294
290
|
asana: "Asana",
|
|
295
291
|
chatgpt: "ChatGPT",
|
|
296
292
|
gmail: "Gmail",
|
|
297
|
-
"google-docs": "Google Docs/Sheets
|
|
293
|
+
"google-docs": "Google Docs/Sheets",
|
|
298
294
|
notion: "Notion",
|
|
299
295
|
slack: "Slack",
|
|
300
296
|
zoom: "Zoom",
|
|
@@ -392,9 +388,9 @@ export function createRoot(wire: Wire, currentDirectory: string, auth?: Auth, re
|
|
|
392
388
|
name: "init",
|
|
393
389
|
description: "Initialize a wire workspace in the current directory.",
|
|
394
390
|
params: S.Object({
|
|
395
|
-
backend: S.Optional(S.Enum(["
|
|
391
|
+
backend: S.Optional(S.Enum(["sqlite", "files"], { default: "sqlite", description: "Registry backend: sqlite or files." })),
|
|
396
392
|
}),
|
|
397
|
-
handler: ({ params }) => userFacing(() => wire.init(currentDirectory, params.backend as WireBackend,
|
|
393
|
+
handler: ({ params }) => userFacing(() => wire.init(currentDirectory, params.backend as WireBackend, params.backend === "files" ? "records" : "registry.sqlite3")),
|
|
398
394
|
render: wirePresentation.init,
|
|
399
395
|
}),
|
|
400
396
|
defineCommand({
|
package/src/cli.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
1
3
|
import { runCLI } from "toolcraft/cli";
|
|
2
4
|
|
|
3
5
|
import type { WireRoot } from "./adapters/root.js";
|
|
@@ -131,8 +133,8 @@ const removedCommandNames = Object.freeze({
|
|
|
131
133
|
const outputFormatNames = new Set(["rich", "md", "markdown", "json"]);
|
|
132
134
|
const outputFormatList = "md, markdown, json, rich";
|
|
133
135
|
const debugModeNames = new Set(["raw"]);
|
|
134
|
-
const backendNames = new Set(["
|
|
135
|
-
const backendList = "
|
|
136
|
+
const backendNames = new Set(["sqlite", "files"]);
|
|
137
|
+
const backendList = "sqlite, files";
|
|
136
138
|
|
|
137
139
|
function isSourceUrl(value: string): boolean {
|
|
138
140
|
return /^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(value);
|
|
@@ -501,5 +503,6 @@ export async function runWireCli(root: WireRoot | WireRootFactory, argv: readonl
|
|
|
501
503
|
if (!version && renderExcessPositionals(normalized)) {
|
|
502
504
|
return;
|
|
503
505
|
}
|
|
504
|
-
|
|
506
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { version: string };
|
|
507
|
+
await runCLI(typeof root === "function" ? root(currentDirectory) : root, { version: packageJson.version, rootUsageName: "wire", presets: false, approvals: false, controls: { output: true, debug: true }, argv: normalized });
|
|
505
508
|
}
|
package/src/executable.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { createRoot } from "./adapters/root.js";
|
|
|
6
6
|
import { composeAuth } from "./auth.js";
|
|
7
7
|
import { serviceCatalog } from "./adapters/services/catalog.js";
|
|
8
8
|
import { withWireHooks } from "./hooks.js";
|
|
9
|
-
import { composeWire, configuredWireRoot, createCookiesCapability, createGoogleTokensCapability, createNodeClock, createNodeConfiguration, createNodeFilesystem, createNodeHttp, createNodeOpenFiles, createNodeProcess, createNodeSecrets, createNodeWatch,
|
|
9
|
+
import { composeWire, configuredWireRoot, createCookiesCapability, createGoogleTokensCapability, createNodeClock, createNodeConfiguration, createNodeFilesystem, createNodeHttp, createNodeOpenFiles, createNodeProcess, createNodeSecrets, createNodeWatch, extractChromeCookies, initializeWire, loadWireConfig, openWireRegistry, switchWireBackend, wireRelativePath, type NodeEnvironment } from "wire-core";
|
|
10
10
|
import type { WireConfig } from "wire-core";
|
|
11
11
|
|
|
12
12
|
function readStandardInput(): Promise<string> {
|
|
@@ -93,7 +93,7 @@ export function createExecutableRoot(environment: NodeEnvironment, currentDirect
|
|
|
93
93
|
relativePath: wireRelativePath,
|
|
94
94
|
switchBackend: switchWireBackend,
|
|
95
95
|
},
|
|
96
|
-
initialization: { backend:
|
|
96
|
+
initialization: { backend: "sqlite", registryPath: "registry.sqlite3" },
|
|
97
97
|
watch: createNodeWatch(),
|
|
98
98
|
now: runtime.clock.now,
|
|
99
99
|
open: runtime.openFiles.open,
|
package/src/mcp.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
1
3
|
import { createMCPServer, runMCP } from "toolcraft/mcp";
|
|
2
4
|
|
|
3
5
|
import type { WireRoot } from "./adapters/root.js";
|
|
4
6
|
|
|
5
7
|
export function createWireMcpServer(root: WireRoot) {
|
|
6
|
-
|
|
8
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { version: string };
|
|
9
|
+
return createMCPServer(root, { name: "wire", version: packageJson.version, omitRootToolNamePrefix: true, approvals: false });
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function runWireMcp(root: WireRoot): Promise<void> {
|
|
10
|
-
|
|
13
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { version: string };
|
|
14
|
+
return runMCP(root, { name: "wire", version: packageJson.version, omitRootToolNamePrefix: true, approvals: false });
|
|
11
15
|
}
|