@hasna/loops 0.4.25 → 0.4.27

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ documented in this file. Version entries are generated from the
5
5
  conventional-commit git history; one commit maps to one released patch version
6
6
  unless noted.
7
7
 
8
+ ## 0.4.26
9
+
10
+ ### Fixed
11
+
12
+ - `PostgresLoopStorage.createWorkflow` is now implemented (ported from the sqlite
13
+ `Store`), so `POST /v1/workflows` on the self-hosted server persists workflow
14
+ specs instead of returning `500 not_implemented`. This unblocks the cloud-mode
15
+ CLI `workflows create` / `templates create-workflow` and the MCP workflow
16
+ tools. `archiveWorkflow` is likewise ported so `POST /v1/workflows/:id/archive`
17
+ works on the Postgres backend. Requires an ECS redeploy of the loops server
18
+ image for the live self-hosted API to pick up the new endpoints.
19
+
8
20
  ## Unreleased
9
21
 
10
22
  ### Added
package/dist/api/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hasna/loops",
6
- version: "0.4.25",
6
+ version: "0.4.27",
7
7
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8
8
  type: "module",
9
9
  main: "dist/index.js",
package/dist/cli/index.js CHANGED
@@ -4972,7 +4972,7 @@ class Store {
4972
4972
  // package.json
4973
4973
  var package_default = {
4974
4974
  name: "@hasna/loops",
4975
- version: "0.4.25",
4975
+ version: "0.4.27",
4976
4976
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4977
4977
  type: "module",
4978
4978
  main: "dist/index.js",
@@ -8934,7 +8934,7 @@ function enableStartup(result) {
8934
8934
  // package.json
8935
8935
  var package_default = {
8936
8936
  name: "@hasna/loops",
8937
- version: "0.4.25",
8937
+ version: "0.4.27",
8938
8938
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8939
8939
  type: "module",
8940
8940
  main: "dist/index.js",
package/dist/index.js CHANGED
@@ -4970,7 +4970,7 @@ class Store {
4970
4970
  // package.json
4971
4971
  var package_default = {
4972
4972
  name: "@hasna/loops",
4973
- version: "0.4.25",
4973
+ version: "0.4.27",
4974
4974
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4975
4975
  type: "module",
4976
4976
  main: "dist/index.js",
@@ -13144,11 +13144,46 @@ class PostgresLoopStorage {
13144
13144
  }
13145
13145
  return rows.map(rowToGoalRun);
13146
13146
  }
13147
- createWorkflow() {
13148
- throw new NotImplementedError("createWorkflow");
13147
+ async createWorkflow(...args) {
13148
+ const [input] = args;
13149
+ const normalized = normalizeCreateWorkflowInput(input);
13150
+ const now = nowIso();
13151
+ const workflow = {
13152
+ id: genId(),
13153
+ name: normalized.name,
13154
+ description: normalized.description,
13155
+ version: normalized.version ?? 1,
13156
+ status: "active",
13157
+ goal: normalized.goal,
13158
+ steps: normalized.steps,
13159
+ createdAt: now,
13160
+ updatedAt: now
13161
+ };
13162
+ await this.client.execute(`INSERT INTO workflow_specs (id, name, description, version, status, goal_json, steps_json, created_at, updated_at)
13163
+ VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7::jsonb,$8,$9)`, [
13164
+ workflow.id,
13165
+ workflow.name,
13166
+ workflow.description ?? null,
13167
+ workflow.version,
13168
+ workflow.status,
13169
+ workflow.goal ? JSON.stringify(workflow.goal) : null,
13170
+ JSON.stringify(workflow.steps),
13171
+ workflow.createdAt,
13172
+ workflow.updatedAt
13173
+ ]);
13174
+ return workflow;
13149
13175
  }
13150
- archiveWorkflow() {
13151
- throw new NotImplementedError("archiveWorkflow");
13176
+ async archiveWorkflow(...args) {
13177
+ const [idOrName] = args;
13178
+ const existing = await this.loadWorkflow(this.client, idOrName) ?? await this.client.get("SELECT * FROM workflow_specs WHERE name = $1 AND status = 'active' ORDER BY updated_at DESC LIMIT 1", [idOrName]).then((row) => row ? rowToWorkflow(row) : undefined);
13179
+ if (!existing)
13180
+ throw new Error(`workflow not found: ${idOrName}`);
13181
+ const updated = nowIso();
13182
+ await this.client.execute("UPDATE workflow_specs SET status='archived', updated_at=$1 WHERE id=$2", [updated, existing.id]);
13183
+ const archived = await this.getWorkflow(existing.id);
13184
+ if (!archived)
13185
+ throw new Error(`workflow not found after archive: ${existing.id}`);
13186
+ return archived;
13152
13187
  }
13153
13188
  createWorkflowInvocation() {
13154
13189
  throw new NotImplementedError("createWorkflowInvocation");
package/dist/lib/mode.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@hasna/loops",
5
- version: "0.4.25",
5
+ version: "0.4.27",
6
6
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
7
7
  type: "module",
8
8
  main: "dist/index.js",
@@ -6630,11 +6630,46 @@ class PostgresLoopStorage {
6630
6630
  }
6631
6631
  return rows.map(rowToGoalRun);
6632
6632
  }
6633
- createWorkflow() {
6634
- throw new NotImplementedError("createWorkflow");
6633
+ async createWorkflow(...args) {
6634
+ const [input] = args;
6635
+ const normalized = normalizeCreateWorkflowInput(input);
6636
+ const now = nowIso();
6637
+ const workflow = {
6638
+ id: genId(),
6639
+ name: normalized.name,
6640
+ description: normalized.description,
6641
+ version: normalized.version ?? 1,
6642
+ status: "active",
6643
+ goal: normalized.goal,
6644
+ steps: normalized.steps,
6645
+ createdAt: now,
6646
+ updatedAt: now
6647
+ };
6648
+ await this.client.execute(`INSERT INTO workflow_specs (id, name, description, version, status, goal_json, steps_json, created_at, updated_at)
6649
+ VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7::jsonb,$8,$9)`, [
6650
+ workflow.id,
6651
+ workflow.name,
6652
+ workflow.description ?? null,
6653
+ workflow.version,
6654
+ workflow.status,
6655
+ workflow.goal ? JSON.stringify(workflow.goal) : null,
6656
+ JSON.stringify(workflow.steps),
6657
+ workflow.createdAt,
6658
+ workflow.updatedAt
6659
+ ]);
6660
+ return workflow;
6635
6661
  }
6636
- archiveWorkflow() {
6637
- throw new NotImplementedError("archiveWorkflow");
6662
+ async archiveWorkflow(...args) {
6663
+ const [idOrName] = args;
6664
+ const existing = await this.loadWorkflow(this.client, idOrName) ?? await this.client.get("SELECT * FROM workflow_specs WHERE name = $1 AND status = 'active' ORDER BY updated_at DESC LIMIT 1", [idOrName]).then((row) => row ? rowToWorkflow(row) : undefined);
6665
+ if (!existing)
6666
+ throw new Error(`workflow not found: ${idOrName}`);
6667
+ const updated = nowIso();
6668
+ await this.client.execute("UPDATE workflow_specs SET status='archived', updated_at=$1 WHERE id=$2", [updated, existing.id]);
6669
+ const archived = await this.getWorkflow(existing.id);
6670
+ if (!archived)
6671
+ throw new Error(`workflow not found after archive: ${existing.id}`);
6672
+ return archived;
6638
6673
  }
6639
6674
  createWorkflowInvocation() {
6640
6675
  throw new NotImplementedError("createWorkflowInvocation");
@@ -99,8 +99,8 @@ export declare class PostgresLoopStorage implements LoopStorageContract {
99
99
  listGoals(...args: M<"listGoals">["args"]): Promise<M<"listGoals">["result"]>;
100
100
  listGoalPlanNodes(...args: M<"listGoalPlanNodes">["args"]): Promise<M<"listGoalPlanNodes">["result"]>;
101
101
  listGoalRuns(...args: M<"listGoalRuns">["args"]): Promise<M<"listGoalRuns">["result"]>;
102
- createWorkflow(): never;
103
- archiveWorkflow(): never;
102
+ createWorkflow(...args: M<"createWorkflow">["args"]): Promise<M<"createWorkflow">["result"]>;
103
+ archiveWorkflow(...args: M<"archiveWorkflow">["args"]): Promise<M<"archiveWorkflow">["result"]>;
104
104
  createWorkflowInvocation(): never;
105
105
  upsertWorkflowWorkItem(): never;
106
106
  admitWorkflowWorkItem(): never;
package/dist/mcp/index.js CHANGED
@@ -4972,7 +4972,7 @@ class Store {
4972
4972
  // package.json
4973
4973
  var package_default = {
4974
4974
  name: "@hasna/loops",
4975
- version: "0.4.25",
4975
+ version: "0.4.27",
4976
4976
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4977
4977
  type: "module",
4978
4978
  main: "dist/index.js",
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hasna/loops",
6
- version: "0.4.25",
6
+ version: "0.4.27",
7
7
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8
8
  type: "module",
9
9
  main: "dist/index.js",
package/dist/sdk/index.js CHANGED
@@ -4970,7 +4970,7 @@ class Store {
4970
4970
  // package.json
4971
4971
  var package_default = {
4972
4972
  name: "@hasna/loops",
4973
- version: "0.4.25",
4973
+ version: "0.4.27",
4974
4974
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4975
4975
  type: "module",
4976
4976
  main: "dist/index.js",
@@ -4972,7 +4972,7 @@ class Store {
4972
4972
  // package.json
4973
4973
  var package_default = {
4974
4974
  name: "@hasna/loops",
4975
- version: "0.4.25",
4975
+ version: "0.4.27",
4976
4976
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4977
4977
  type: "module",
4978
4978
  main: "dist/index.js",
@@ -8931,11 +8931,46 @@ class PostgresLoopStorage {
8931
8931
  }
8932
8932
  return rows.map(rowToGoalRun);
8933
8933
  }
8934
- createWorkflow() {
8935
- throw new NotImplementedError("createWorkflow");
8934
+ async createWorkflow(...args) {
8935
+ const [input] = args;
8936
+ const normalized = normalizeCreateWorkflowInput(input);
8937
+ const now = nowIso();
8938
+ const workflow = {
8939
+ id: genId(),
8940
+ name: normalized.name,
8941
+ description: normalized.description,
8942
+ version: normalized.version ?? 1,
8943
+ status: "active",
8944
+ goal: normalized.goal,
8945
+ steps: normalized.steps,
8946
+ createdAt: now,
8947
+ updatedAt: now
8948
+ };
8949
+ await this.client.execute(`INSERT INTO workflow_specs (id, name, description, version, status, goal_json, steps_json, created_at, updated_at)
8950
+ VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7::jsonb,$8,$9)`, [
8951
+ workflow.id,
8952
+ workflow.name,
8953
+ workflow.description ?? null,
8954
+ workflow.version,
8955
+ workflow.status,
8956
+ workflow.goal ? JSON.stringify(workflow.goal) : null,
8957
+ JSON.stringify(workflow.steps),
8958
+ workflow.createdAt,
8959
+ workflow.updatedAt
8960
+ ]);
8961
+ return workflow;
8936
8962
  }
8937
- archiveWorkflow() {
8938
- throw new NotImplementedError("archiveWorkflow");
8963
+ async archiveWorkflow(...args) {
8964
+ const [idOrName] = args;
8965
+ const existing = await this.loadWorkflow(this.client, idOrName) ?? await this.client.get("SELECT * FROM workflow_specs WHERE name = $1 AND status = 'active' ORDER BY updated_at DESC LIMIT 1", [idOrName]).then((row) => row ? rowToWorkflow(row) : undefined);
8966
+ if (!existing)
8967
+ throw new Error(`workflow not found: ${idOrName}`);
8968
+ const updated = nowIso();
8969
+ await this.client.execute("UPDATE workflow_specs SET status='archived', updated_at=$1 WHERE id=$2", [updated, existing.id]);
8970
+ const archived = await this.getWorkflow(existing.id);
8971
+ if (!archived)
8972
+ throw new Error(`workflow not found after archive: ${existing.id}`);
8973
+ return archived;
8939
8974
  }
8940
8975
  createWorkflowInvocation() {
8941
8976
  throw new NotImplementedError("createWorkflowInvocation");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",