@loopstack/git-examples 0.1.1 → 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 +36 -0
- package/dist/workflows/github-repo-sync/github-repo-sync-example.workflow.d.ts +1 -1
- package/dist/workflows/github-repo-sync/github-repo-sync-example.workflow.d.ts.map +1 -1
- package/dist/workflows/github-repo-sync/github-repo-sync-example.workflow.js +2 -2
- package/dist/workflows/github-repo-sync/github-repo-sync-example.workflow.js.map +1 -1
- package/package.json +2 -2
- package/src/workflows/git-commit-flow/__tests__/git-commit-flow-example.spec.ts +66 -0
- package/src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,42 @@ npm install @loopstack/git-examples
|
|
|
38
38
|
import { GitExamplesModule } from '@loopstack/git-examples';
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## Required app-module configuration
|
|
42
|
+
|
|
43
|
+
Both examples invoke git tools that execute on a **remote agent**, so the workspace needs a connected `sandbox` environment. `GitExamplesModule` declares the slot via `RemoteClientModule.forFeature(...)`, but the consumer must register at least one matching environment in their root module via `RemoteClientModule.forRoot(...)`:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Module } from '@nestjs/common';
|
|
47
|
+
import { GitExamplesModule } from '@loopstack/git-examples';
|
|
48
|
+
import { LoopstackModule } from '@loopstack/loopstack-module';
|
|
49
|
+
import { RemoteClientModule } from '@loopstack/remote-client';
|
|
50
|
+
|
|
51
|
+
@Module({
|
|
52
|
+
imports: [
|
|
53
|
+
LoopstackModule.forRoot(),
|
|
54
|
+
RemoteClientModule.forRoot({
|
|
55
|
+
environments: {
|
|
56
|
+
available: [
|
|
57
|
+
{
|
|
58
|
+
type: 'sandbox',
|
|
59
|
+
name: 'Local Remote Server',
|
|
60
|
+
connectionUrl: process.env.SANDBOX_URL ?? 'http://localhost:3080',
|
|
61
|
+
agentUrl: process.env.SANDBOX_AGENT_URL ?? 'http://localhost:3001',
|
|
62
|
+
local: true,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
GitExamplesModule,
|
|
68
|
+
],
|
|
69
|
+
})
|
|
70
|
+
export class AppModule {}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `type: 'sandbox'` must match the slot type declared by `GitExamplesModule`. The simplest remote agent option is `@loopstack/remote-server` on `localhost:3001`.
|
|
74
|
+
|
|
75
|
+
`GitExamplesModule` re-imports `GitModule.forFeature()` and `GitHubIntegrationModule`, which transitively brings in the global `OAuthModule` and the GitHub OAuth provider — no additional OAuth wiring required in your AppModule. You just need to set the GitHub OAuth client env vars (see [Environment](#environment) below).
|
|
76
|
+
|
|
41
77
|
## Environment
|
|
42
78
|
|
|
43
79
|
The repo-sync example requires GitHub OAuth credentials:
|
|
@@ -12,7 +12,7 @@ type ConnectResult = z.infer<typeof ConnectResultSchema>;
|
|
|
12
12
|
export declare class GithubRepoSyncExampleWorkflow extends BaseWorkflow {
|
|
13
13
|
private readonly connectGitHubWorkflow;
|
|
14
14
|
constructor(connectGitHubWorkflow: ConnectGitHubWorkflow);
|
|
15
|
-
connect(
|
|
15
|
+
connect(): Promise<void>;
|
|
16
16
|
connectComplete(_state: Record<string, unknown>, input: TransitionInput<ConnectResult>): Promise<void>;
|
|
17
17
|
}
|
|
18
18
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-repo-sync-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAA0C,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,QAAA,MAAM,mBAAmB;;;;;mBAGvB,CAAC;AACH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEzD,qBAKa,6BAA8B,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,qBAAqB;IAKnE,OAAO
|
|
1
|
+
{"version":3,"file":"github-repo-sync-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAA0C,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,QAAA,MAAM,mBAAmB;;;;;mBAGvB,CAAC;AACH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEzD,qBAKa,6BAA8B,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,qBAAqB;IAKnE,OAAO;IAaP,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC;CAa7F"}
|
|
@@ -23,7 +23,7 @@ let GithubRepoSyncExampleWorkflow = class GithubRepoSyncExampleWorkflow extends
|
|
|
23
23
|
super();
|
|
24
24
|
this.connectGitHubWorkflow = connectGitHubWorkflow;
|
|
25
25
|
}
|
|
26
|
-
async connect(
|
|
26
|
+
async connect() {
|
|
27
27
|
await this.connectGitHubWorkflow.run({}, { callback: { transition: 'connectComplete' }, show: 'inline', label: 'Connecting to GitHub...' });
|
|
28
28
|
}
|
|
29
29
|
async connectComplete(_state, input) {
|
|
@@ -43,7 +43,7 @@ exports.GithubRepoSyncExampleWorkflow = GithubRepoSyncExampleWorkflow;
|
|
|
43
43
|
__decorate([
|
|
44
44
|
(0, common_1.Transition)({ to: 'connecting' }),
|
|
45
45
|
__metadata("design:type", Function),
|
|
46
|
-
__metadata("design:paramtypes", [
|
|
46
|
+
__metadata("design:paramtypes", []),
|
|
47
47
|
__metadata("design:returntype", Promise)
|
|
48
48
|
], GithubRepoSyncExampleWorkflow.prototype, "connect", null);
|
|
49
49
|
__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-repo-sync-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAyF;AAEzF,sEAAsE;AAEtE,MAAM,mBAAmB,GAAG,OAAC,CAAC,KAAK,CAAC;IAClC,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;CACzC,CAAC,CAAC;AAQI,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,qBAAY;IAChC;IAA7B,YAA6B,qBAA4C;QACvE,KAAK,EAAE,CAAC;QADmB,0BAAqB,GAArB,qBAAqB,CAAuB;IAEzE,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO
|
|
1
|
+
{"version":3,"file":"github-repo-sync-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAyF;AAEzF,sEAAsE;AAEtE,MAAM,mBAAmB,GAAG,OAAC,CAAC,KAAK,CAAC;IAClC,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;CACzC,CAAC,CAAC;AAQI,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,qBAAY;IAChC;IAA7B,YAA6B,qBAA4C;QACvE,KAAK,EAAE,CAAC;QADmB,0BAAqB,GAArB,qBAAqB,CAAuB;IAEzE,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAClC,EAAE,EACF,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAClG,CAAC;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,eAAe,CAAC,MAA+B,EAAE,KAAqC;QAC1F,IAAI,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;gBAC9C,QAAQ,EAAE,0CAA0C;aACrD,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;YAC9C,QAAQ,EAAE,2CAA2C,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI;SAC5F,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAwC,CAAC,CAAC;IACvG,CAAC;CACF,CAAA;AAhCY,sEAA6B;AAMlC;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;;;;4DAMhC;AAQK;IANL,IAAA,mBAAU,EAAC;QACV,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,mBAAmB;KAC5B,CAAC;;;;oEAaD;wCA/BU,6BAA6B;IALzC,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,gCAAgC;QACvC,WAAW,EACT,+LAA+L;KAClM,CAAC;qCAEoD,0CAAqB;GAD9D,6BAA6B,CAgCzC"}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"github",
|
|
10
10
|
"workflow"
|
|
11
11
|
],
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.2",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Jakob Klippel",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@loopstack/common": "*",
|
|
34
34
|
"@loopstack/git-module": "*",
|
|
35
35
|
"@loopstack/github-integration": "*",
|
|
36
|
-
"@nestjs/common": "^
|
|
36
|
+
"@nestjs/common": "^12.0.1",
|
|
37
37
|
"zod": "^4.3.6"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { GitModule } from '@loopstack/git-module';
|
|
3
|
+
import { RemoteClientModule } from '@loopstack/remote-client';
|
|
4
|
+
import { replay, runWorkflow } from '@loopstack/testing';
|
|
5
|
+
import { GitExamplesModule } from '../../../git-examples.module';
|
|
6
|
+
import { GitCommitFlowExampleWorkflow } from '../git-commit-flow-example.workflow';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A scripted multi-tool pipeline with no LLM. The git and write tools are HTTP-backed (they
|
|
10
|
+
* talk to a remote workspace), so the whole flow runs against the tool boundary: each tool's
|
|
11
|
+
* response is replayed in call order, and the workflow's own state threading and document
|
|
12
|
+
* rendering run for real. Envelopes match each tool's `resultSchema`.
|
|
13
|
+
*
|
|
14
|
+
* `RemoteClientModule.forRoot()` establishes the global remote-client services the git tools
|
|
15
|
+
* depend on; the tools' actual network calls are replaced by the replay script.
|
|
16
|
+
*/
|
|
17
|
+
describe('GitCommitFlowExampleWorkflow', () => {
|
|
18
|
+
it('writes a file, then stages, commits, and reads back the log', async () => {
|
|
19
|
+
const run = await runWorkflow(GitCommitFlowExampleWorkflow, undefined, {
|
|
20
|
+
imports: [
|
|
21
|
+
RemoteClientModule.forRoot(),
|
|
22
|
+
GitModule.forFeature(),
|
|
23
|
+
RemoteClientModule.forFeature({ slots: [{ id: 'sandbox', type: 'sandbox', title: 'Sandbox' }] }),
|
|
24
|
+
GitExamplesModule,
|
|
25
|
+
],
|
|
26
|
+
replay: replay({
|
|
27
|
+
version: 3,
|
|
28
|
+
recordings: [
|
|
29
|
+
{ tool: 'write', envelope: { data: { success: true, path: 'tmp/example.txt' } } },
|
|
30
|
+
{
|
|
31
|
+
tool: 'git_status',
|
|
32
|
+
envelope: {
|
|
33
|
+
data: { branch: 'main', staged: [], modified: [], untracked: ['tmp/example.txt'], deleted: [] },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{ tool: 'git_add', envelope: { data: { success: true } } },
|
|
37
|
+
{ tool: 'git_commit', envelope: { data: { hash: 'abc123', message: 'chore: example commit' } } },
|
|
38
|
+
{
|
|
39
|
+
tool: 'git_log',
|
|
40
|
+
envelope: {
|
|
41
|
+
data: {
|
|
42
|
+
commits: [
|
|
43
|
+
{
|
|
44
|
+
hash: 'abc123',
|
|
45
|
+
shortHash: 'abc123',
|
|
46
|
+
message: 'chore: example commit',
|
|
47
|
+
author: 'Test',
|
|
48
|
+
date: '2026-01-01',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(run.error).toBeUndefined();
|
|
59
|
+
expect(run.status).toBe('completed');
|
|
60
|
+
expect(run.path).toEqual(['writeTestFile', 'checkStatus', 'stageAll', 'commit', 'readBack']);
|
|
61
|
+
|
|
62
|
+
const texts = run.documents.map((d) => (d.content as { text?: string }).text ?? '');
|
|
63
|
+
expect(texts.some((t) => t.includes('"branch": "main"'))).toBe(true);
|
|
64
|
+
expect(texts.some((t) => t.includes('chore: example commit'))).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -20,7 +20,7 @@ export class GithubRepoSyncExampleWorkflow extends BaseWorkflow {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
@Transition({ to: 'connecting' })
|
|
23
|
-
async connect(
|
|
23
|
+
async connect() {
|
|
24
24
|
await this.connectGitHubWorkflow.run(
|
|
25
25
|
{},
|
|
26
26
|
{ callback: { transition: 'connectComplete' }, show: 'inline', label: 'Connecting to GitHub...' },
|