@hasna/cloud 0.1.26 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -11838,9 +11838,9 @@ async function syncTransfer(source, target, options, _direction) {
11838
11838
  const batch = rows.slice(offset, offset + batchSize);
11839
11839
  try {
11840
11840
  if (isAsyncAdapter(target)) {
11841
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
11841
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
11842
11842
  } else {
11843
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
11843
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
11844
11844
  }
11845
11845
  result.rowsWritten += batch.length;
11846
11846
  } catch (err) {
@@ -11887,7 +11887,7 @@ async function syncTransfer(source, target, options, _direction) {
11887
11887
  }
11888
11888
  return results;
11889
11889
  }
11890
- async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch) {
11890
+ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
11891
11891
  if (batch.length === 0)
11892
11892
  return;
11893
11893
  const colList = columns.map((c) => `"${c}"`).join(", ");
@@ -11897,20 +11897,22 @@ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, ba
11897
11897
  }).join(", ");
11898
11898
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
11899
11899
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
11900
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
11900
11901
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
11901
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
11902
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
11902
11903
  const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
11903
11904
  await target.run(sql, ...params);
11904
11905
  }
11905
- function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch) {
11906
+ function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
11906
11907
  if (batch.length === 0)
11907
11908
  return;
11908
11909
  const colList = columns.map((c) => `"${c}"`).join(", ");
11909
11910
  const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
11910
11911
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
11911
11912
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
11913
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
11912
11914
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
11913
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
11915
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
11914
11916
  const params = batch.flatMap((row) => columns.map((c) => coerceForSqlite(row[c])));
11915
11917
  target.run(sql, ...params);
11916
11918
  }
package/dist/index.js CHANGED
@@ -9766,9 +9766,9 @@ async function syncTransfer(source, target, options, _direction) {
9766
9766
  const batch = rows.slice(offset, offset + batchSize);
9767
9767
  try {
9768
9768
  if (isAsyncAdapter(target)) {
9769
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
9769
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
9770
9770
  } else {
9771
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
9771
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
9772
9772
  }
9773
9773
  result.rowsWritten += batch.length;
9774
9774
  } catch (err) {
@@ -9815,7 +9815,7 @@ async function syncTransfer(source, target, options, _direction) {
9815
9815
  }
9816
9816
  return results;
9817
9817
  }
9818
- async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch) {
9818
+ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
9819
9819
  if (batch.length === 0)
9820
9820
  return;
9821
9821
  const colList = columns.map((c) => `"${c}"`).join(", ");
@@ -9825,20 +9825,22 @@ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, ba
9825
9825
  }).join(", ");
9826
9826
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
9827
9827
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
9828
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
9828
9829
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
9829
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
9830
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
9830
9831
  const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
9831
9832
  await target.run(sql, ...params);
9832
9833
  }
9833
- function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch) {
9834
+ function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
9834
9835
  if (batch.length === 0)
9835
9836
  return;
9836
9837
  const colList = columns.map((c) => `"${c}"`).join(", ");
9837
9838
  const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
9838
9839
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
9839
9840
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
9841
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
9840
9842
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
9841
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
9843
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
9842
9844
  const params = batch.flatMap((row) => columns.map((c) => coerceForSqlite(row[c])));
9843
9845
  target.run(sql, ...params);
9844
9846
  }
package/dist/mcp/index.js CHANGED
@@ -25491,9 +25491,9 @@ async function syncTransfer(source, target, options, _direction) {
25491
25491
  const batch = rows.slice(offset, offset + batchSize);
25492
25492
  try {
25493
25493
  if (isAsyncAdapter(target)) {
25494
- await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
25494
+ await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
25495
25495
  } else {
25496
- batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
25496
+ batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch, columns.includes(conflictColumn) ? conflictColumn : undefined);
25497
25497
  }
25498
25498
  result.rowsWritten += batch.length;
25499
25499
  } catch (err) {
@@ -25540,7 +25540,7 @@ async function syncTransfer(source, target, options, _direction) {
25540
25540
  }
25541
25541
  return results;
25542
25542
  }
25543
- async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch) {
25543
+ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
25544
25544
  if (batch.length === 0)
25545
25545
  return;
25546
25546
  const colList = columns.map((c) => `"${c}"`).join(", ");
@@ -25550,20 +25550,22 @@ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, ba
25550
25550
  }).join(", ");
25551
25551
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
25552
25552
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
25553
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
25553
25554
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
25554
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
25555
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
25555
25556
  const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
25556
25557
  await target.run(sql, ...params);
25557
25558
  }
25558
- function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch) {
25559
+ function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch, conflictColumn) {
25559
25560
  if (batch.length === 0)
25560
25561
  return;
25561
25562
  const colList = columns.map((c) => `"${c}"`).join(", ");
25562
25563
  const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
25563
25564
  const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
25564
25565
  const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
25566
+ const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
25565
25567
  const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
25566
- ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
25568
+ ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}${whereClause}`;
25567
25569
  const params = batch.flatMap((row) => columns.map((c) => coerceForSqlite(row[c])));
25568
25570
  target.run(sql, ...params);
25569
25571
  }
@@ -26059,6 +26061,40 @@ Done. ${results.length} services, ${totalApplied} migrations applied.`);
26059
26061
  return { content: [{ type: "text", text: lines.join(`
26060
26062
  `) }] };
26061
26063
  });
26064
+ var mcpAgentRegistry = new Map;
26065
+ server.tool("register_agent", "Register an agent session for attribution", {
26066
+ name: exports_external.string().describe("Agent name"),
26067
+ session_id: exports_external.string().optional().describe("Session identifier")
26068
+ }, async ({ name, session_id }) => {
26069
+ const existing = [...mcpAgentRegistry.values()].find((a) => a.name === name);
26070
+ if (existing) {
26071
+ existing.last_seen_at = new Date().toISOString();
26072
+ return { content: [{ type: "text", text: JSON.stringify({ agent_id: existing.id, name: existing.name, last_seen_at: existing.last_seen_at }) }] };
26073
+ }
26074
+ const id = Math.random().toString(36).slice(2, 10);
26075
+ const agent = { id, name, last_seen_at: new Date().toISOString() };
26076
+ mcpAgentRegistry.set(id, agent);
26077
+ return { content: [{ type: "text", text: JSON.stringify(agent) }] };
26078
+ });
26079
+ server.tool("heartbeat", "Update agent last_seen_at", {
26080
+ agent_id: exports_external.string().optional().describe("Agent ID (optional \u2014 updates by name if registered)")
26081
+ }, async () => {
26082
+ return { content: [{ type: "text", text: `heartbeat at ${new Date().toISOString()}` }] };
26083
+ });
26084
+ server.tool("list_agents", "List registered agents", {}, async () => {
26085
+ const agents = [...mcpAgentRegistry.values()];
26086
+ return { content: [{ type: "text", text: agents.length > 0 ? JSON.stringify(agents) : "No agents registered" }] };
26087
+ });
26088
+ server.tool("set_focus", "Set active project context", {
26089
+ agent_id: exports_external.string().describe("Agent ID"),
26090
+ project_id: exports_external.string().optional().describe("Project to focus on")
26091
+ }, async ({ agent_id, project_id }) => {
26092
+ const agent = mcpAgentRegistry.get(agent_id);
26093
+ if (!agent)
26094
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
26095
+ agent.project_id = project_id;
26096
+ return { content: [{ type: "text", text: project_id ? `Focus set: ${project_id}` : "Focus cleared" }] };
26097
+ });
26062
26098
  async function main() {
26063
26099
  const transport = new StdioServerTransport;
26064
26100
  await server.connect(transport);
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAwuBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAKxD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAKxE"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAMD;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAGvB;AAsvBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAKxD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAKxE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/cloud",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Shared cloud infrastructure — database adapter (SQLite + PostgreSQL), sync engine, feedback system, unified dotfile config",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",