@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 +12 -0
- package/dist/api/index.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/daemon/index.js +1 -1
- package/dist/index.js +40 -5
- package/dist/lib/mode.js +1 -1
- package/dist/lib/storage/index.js +39 -4
- package/dist/lib/storage/postgres-loop-storage.d.ts +2 -2
- package/dist/mcp/index.js +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/serve/index.js +40 -5
- package/package.json +1 -1
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.
|
|
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.
|
|
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",
|
package/dist/daemon/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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():
|
|
103
|
-
archiveWorkflow():
|
|
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.
|
|
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",
|
package/dist/runner/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.
|
|
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.
|
|
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",
|
package/dist/serve/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.
|
|
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
|
-
|
|
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
|
-
|
|
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